[ES6] Add support for Symbol.toPrimitive
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.h
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003-2009, 2012-2016 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifndef JSObject_h
24 #define JSObject_h
25
26 #include "ArgList.h"
27 #include "ArrayConventions.h"
28 #include "ArrayStorage.h"
29 #include "Butterfly.h"
30 #include "CallFrame.h"
31 #include "ClassInfo.h"
32 #include "CommonIdentifiers.h"
33 #include "CopyBarrier.h"
34 #include "CustomGetterSetter.h"
35 #include "DeferGC.h"
36 #include "Heap.h"
37 #include "HeapInlines.h"
38 #include "IndexingHeaderInlines.h"
39 #include "JSCell.h"
40 #include "PropertySlot.h"
41 #include "PropertyStorage.h"
42 #include "PutDirectIndexMode.h"
43 #include "PutPropertySlot.h"
44
45 #include "Structure.h"
46 #include "VM.h"
47 #include "JSString.h"
48 #include "SparseArrayValueMap.h"
49 #include <wtf/StdLibExtras.h>
50
51 namespace JSC {
52
53 inline JSCell* getJSFunction(JSValue value)
54 {
55     if (value.isCell() && (value.asCell()->type() == JSFunctionType))
56         return value.asCell();
57     return 0;
58 }
59
60 class GetterSetter;
61 class InternalFunction;
62 class JSFunction;
63 class LLIntOffsetsExtractor;
64 class MarkedBlock;
65 class PropertyDescriptor;
66 class PropertyNameArray;
67 class Structure;
68 struct HashTable;
69 struct HashTableValue;
70
71 JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&);
72 extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError;
73
74 COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
75 COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
76 COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
77 COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
78 COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);
79
80 class JSFinalObject;
81
82 class JSObject : public JSCell {
83     friend class BatchedTransitionOptimizer;
84     friend class JIT;
85     friend class JSCell;
86     friend class JSFinalObject;
87     friend class MarkedBlock;
88     JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject*, PropertyName, PropertySlot&);
89
90     enum PutMode {
91         PutModePut,
92         PutModeDefineOwnProperty,
93     };
94
95 public:
96     typedef JSCell Base;
97
98     JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*);
99     JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
100     JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken);
101
102     JS_EXPORT_PRIVATE static String className(const JSObject*);
103     JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*);
104
105     JSValue prototype() const;
106     // This sets the prototype without checking for cycles and without
107     // doing dynamic dispatch on [[SetPrototypeOf]] operation in the specification.
108     // It is not valid to use this when performing a [[SetPrototypeOf]] operation in
109     // the specification. It is valid to use though when you know that you want to directly
110     // set it without consulting the method table and when you definitely won't
111     // introduce a cycle in the prototype chain. This is akin to setting the
112     // [[Prototype]] internal field directly as described in the specification.
113     JS_EXPORT_PRIVATE void setPrototypeDirect(VM&, JSValue prototype);
114 private:
115     // This is OrdinarySetPrototypeOf in the specification. Section 9.1.2.1
116     // https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof
117     JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet);
118 public:
119     // This is the fully virtual [[SetPrototypeOf]] internal function defined
120     // in the ECMAScript 6 specification. Use this when doing a [[SetPrototypeOf]] 
121     // operation as dictated in the specification.
122     bool setPrototype(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet = false);
123     JS_EXPORT_PRIVATE static bool setPrototype(JSObject*, ExecState*, JSValue prototype, bool shouldThrowIfCantSet);
124         
125     bool mayInterceptIndexedAccesses()
126     {
127         return structure()->mayInterceptIndexedAccesses();
128     }
129         
130     JSValue get(ExecState*, PropertyName) const;
131     JSValue get(ExecState*, unsigned propertyName) const;
132
133     bool getPropertySlot(ExecState*, PropertyName, PropertySlot&);
134     bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
135
136     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
137     JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
138
139     // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot
140     // currently returns incorrect results for the DOM window (with non-own properties)
141     // being returned. Once this is fixed we should migrate code & remove this method.
142     JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
143
144     JS_EXPORT_PRIVATE bool allowsAccessFrom(ExecState*);
145
146     unsigned getArrayLength() const
147     {
148         if (!hasIndexedProperties(indexingType()))
149             return 0;
150         return m_butterfly.get(this)->publicLength();
151     }
152         
153     unsigned getVectorLength()
154     {
155         if (!hasIndexedProperties(indexingType()))
156             return 0;
157         return m_butterfly.get(this)->vectorLength();
158     }
159     
160     static void putInline(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
161     
162     JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
163     JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
164         
165     ALWAYS_INLINE void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
166     {
167         if (canSetIndexQuickly(propertyName)) {
168             setIndexQuickly(exec->vm(), propertyName, value);
169             return;
170         }
171         methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow);
172     }
173         
174     // This is similar to the putDirect* methods:
175     //  - the prototype chain is not consulted
176     //  - accessors are not called.
177     //  - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
178     // This method creates a property with attributes writable, enumerable and configurable all set to true.
179     bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
180     {
181         if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) {
182             setIndexQuickly(exec->vm(), propertyName, value);
183             return true;
184         }
185         return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode);
186     }
187     bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value)
188     {
189         return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect);
190     }
191
192     // A non-throwing version of putDirect and putDirectIndex.
193     JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
194         
195     bool hasIndexingHeader() const
196     {
197         return structure()->hasIndexingHeader(this);
198     }
199     
200     bool canGetIndexQuickly(unsigned i)
201     {
202         Butterfly* butterfly = m_butterfly.get(this);
203         switch (indexingType()) {
204         case ALL_BLANK_INDEXING_TYPES:
205         case ALL_UNDECIDED_INDEXING_TYPES:
206             return false;
207         case ALL_INT32_INDEXING_TYPES:
208         case ALL_CONTIGUOUS_INDEXING_TYPES:
209             return i < butterfly->vectorLength() && butterfly->contiguous()[i];
210         case ALL_DOUBLE_INDEXING_TYPES: {
211             if (i >= butterfly->vectorLength())
212                 return false;
213             double value = butterfly->contiguousDouble()[i];
214             if (value != value)
215                 return false;
216             return true;
217         }
218         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
219             return i < butterfly->arrayStorage()->vectorLength() && butterfly->arrayStorage()->m_vector[i];
220         default:
221             RELEASE_ASSERT_NOT_REACHED();
222             return false;
223         }
224     }
225         
226     JSValue getIndexQuickly(unsigned i)
227     {
228         Butterfly* butterfly = m_butterfly.get(this);
229         switch (indexingType()) {
230         case ALL_INT32_INDEXING_TYPES:
231             return jsNumber(butterfly->contiguous()[i].get().asInt32());
232         case ALL_CONTIGUOUS_INDEXING_TYPES:
233             return butterfly->contiguous()[i].get();
234         case ALL_DOUBLE_INDEXING_TYPES:
235             return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble()[i]);
236         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
237             return butterfly->arrayStorage()->m_vector[i].get();
238         default:
239             RELEASE_ASSERT_NOT_REACHED();
240             return JSValue();
241         }
242     }
243         
244     JSValue tryGetIndexQuickly(unsigned i) const
245     {
246         Butterfly* butterfly = m_butterfly.get(this);
247         switch (indexingType()) {
248         case ALL_BLANK_INDEXING_TYPES:
249         case ALL_UNDECIDED_INDEXING_TYPES:
250             break;
251         case ALL_INT32_INDEXING_TYPES:
252             if (i < butterfly->publicLength()) {
253                 JSValue result = butterfly->contiguous()[i].get();
254                 ASSERT(result.isInt32() || !result);
255                 return result;
256             }
257             break;
258         case ALL_CONTIGUOUS_INDEXING_TYPES:
259             if (i < butterfly->publicLength())
260                 return butterfly->contiguous()[i].get();
261             break;
262         case ALL_DOUBLE_INDEXING_TYPES: {
263             if (i >= butterfly->publicLength())
264                 break;
265             double result = butterfly->contiguousDouble()[i];
266             if (result != result)
267                 break;
268             return JSValue(JSValue::EncodeAsDouble, result);
269         }
270         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
271             if (i < butterfly->arrayStorage()->vectorLength())
272                 return butterfly->arrayStorage()->m_vector[i].get();
273             break;
274         default:
275             RELEASE_ASSERT_NOT_REACHED();
276             break;
277         }
278         return JSValue();
279     }
280         
281     JSValue getDirectIndex(ExecState* exec, unsigned i)
282     {
283         if (JSValue result = tryGetIndexQuickly(i))
284             return result;
285         PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
286         if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot))
287             return slot.getValue(exec, i);
288         return JSValue();
289     }
290         
291     JSValue getIndex(ExecState* exec, unsigned i) const
292     {
293         if (JSValue result = tryGetIndexQuickly(i))
294             return result;
295         return get(exec, i);
296     }
297         
298     bool canSetIndexQuickly(unsigned i)
299     {
300         Butterfly* butterfly = m_butterfly.get(this);
301         switch (indexingType()) {
302         case ALL_BLANK_INDEXING_TYPES:
303         case ALL_UNDECIDED_INDEXING_TYPES:
304             return false;
305         case ALL_INT32_INDEXING_TYPES:
306         case ALL_DOUBLE_INDEXING_TYPES:
307         case ALL_CONTIGUOUS_INDEXING_TYPES:
308         case NonArrayWithArrayStorage:
309         case ArrayWithArrayStorage:
310             return i < butterfly->vectorLength();
311         case NonArrayWithSlowPutArrayStorage:
312         case ArrayWithSlowPutArrayStorage:
313             return i < butterfly->arrayStorage()->vectorLength()
314                 && !!butterfly->arrayStorage()->m_vector[i];
315         default:
316             RELEASE_ASSERT_NOT_REACHED();
317             return false;
318         }
319     }
320         
321     bool canSetIndexQuicklyForPutDirect(unsigned i)
322     {
323         switch (indexingType()) {
324         case ALL_BLANK_INDEXING_TYPES:
325         case ALL_UNDECIDED_INDEXING_TYPES:
326             return false;
327         case ALL_INT32_INDEXING_TYPES:
328         case ALL_DOUBLE_INDEXING_TYPES:
329         case ALL_CONTIGUOUS_INDEXING_TYPES:
330         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
331             return i < m_butterfly.get(this)->vectorLength();
332         default:
333             RELEASE_ASSERT_NOT_REACHED();
334             return false;
335         }
336     }
337         
338     void setIndexQuickly(VM& vm, unsigned i, JSValue v)
339     {
340         Butterfly* butterfly = m_butterfly.get(this);
341         switch (indexingType()) {
342         case ALL_INT32_INDEXING_TYPES: {
343             ASSERT(i < butterfly->vectorLength());
344             if (!v.isInt32()) {
345                 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
346                 return;
347             }
348             FALLTHROUGH;
349         }
350         case ALL_CONTIGUOUS_INDEXING_TYPES: {
351             ASSERT(i < butterfly->vectorLength());
352             butterfly->contiguous()[i].set(vm, this, v);
353             if (i >= butterfly->publicLength())
354                 butterfly->setPublicLength(i + 1);
355             break;
356         }
357         case ALL_DOUBLE_INDEXING_TYPES: {
358             ASSERT(i < butterfly->vectorLength());
359             if (!v.isNumber()) {
360                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
361                 return;
362             }
363             double value = v.asNumber();
364             if (value != value) {
365                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
366                 return;
367             }
368             butterfly->contiguousDouble()[i] = value;
369             if (i >= butterfly->publicLength())
370                 butterfly->setPublicLength(i + 1);
371             break;
372         }
373         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
374             ArrayStorage* storage = butterfly->arrayStorage();
375             WriteBarrier<Unknown>& x = storage->m_vector[i];
376             JSValue old = x.get();
377             x.set(vm, this, v);
378             if (!old) {
379                 ++storage->m_numValuesInVector;
380                 if (i >= storage->length())
381                     storage->setLength(i + 1);
382             }
383             break;
384         }
385         default:
386             RELEASE_ASSERT_NOT_REACHED();
387         }
388     }
389
390     void initializeIndex(VM& vm, unsigned i, JSValue v)
391     {
392         initializeIndex(vm, i, v, indexingType());
393     }
394
395     void initializeIndex(VM& vm, unsigned i, JSValue v, IndexingType indexingType)
396     {
397         Butterfly* butterfly = m_butterfly.get(this);
398         switch (indexingType) {
399         case ALL_UNDECIDED_INDEXING_TYPES: {
400             setIndexQuicklyToUndecided(vm, i, v);
401             break;
402         }
403         case ALL_INT32_INDEXING_TYPES: {
404             ASSERT(i < butterfly->publicLength());
405             ASSERT(i < butterfly->vectorLength());
406             if (!v.isInt32()) {
407                 convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v);
408                 break;
409             }
410             FALLTHROUGH;
411         }
412         case ALL_CONTIGUOUS_INDEXING_TYPES: {
413             ASSERT(i < butterfly->publicLength());
414             ASSERT(i < butterfly->vectorLength());
415             butterfly->contiguous()[i].set(vm, this, v);
416             break;
417         }
418         case ALL_DOUBLE_INDEXING_TYPES: {
419             ASSERT(i < butterfly->publicLength());
420             ASSERT(i < butterfly->vectorLength());
421             if (!v.isNumber()) {
422                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
423                 return;
424             }
425             double value = v.asNumber();
426             if (value != value) {
427                 convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v);
428                 return;
429             }
430             butterfly->contiguousDouble()[i] = value;
431             break;
432         }
433         case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
434             ArrayStorage* storage = butterfly->arrayStorage();
435             ASSERT(i < storage->length());
436             ASSERT(i < storage->m_numValuesInVector);
437             storage->m_vector[i].set(vm, this, v);
438             break;
439         }
440         default:
441             RELEASE_ASSERT_NOT_REACHED();
442         }
443     }
444         
445     bool hasSparseMap()
446     {
447         switch (indexingType()) {
448         case ALL_BLANK_INDEXING_TYPES:
449         case ALL_UNDECIDED_INDEXING_TYPES:
450         case ALL_INT32_INDEXING_TYPES:
451         case ALL_DOUBLE_INDEXING_TYPES:
452         case ALL_CONTIGUOUS_INDEXING_TYPES:
453             return false;
454         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
455             return !!m_butterfly.get(this)->arrayStorage()->m_sparseMap;
456         default:
457             RELEASE_ASSERT_NOT_REACHED();
458             return false;
459         }
460     }
461         
462     bool inSparseIndexingMode()
463     {
464         switch (indexingType()) {
465         case ALL_BLANK_INDEXING_TYPES:
466         case ALL_UNDECIDED_INDEXING_TYPES:
467         case ALL_INT32_INDEXING_TYPES:
468         case ALL_DOUBLE_INDEXING_TYPES:
469         case ALL_CONTIGUOUS_INDEXING_TYPES:
470             return false;
471         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
472             return m_butterfly.get(this)->arrayStorage()->inSparseMode();
473         default:
474             RELEASE_ASSERT_NOT_REACHED();
475             return false;
476         }
477     }
478         
479     void enterDictionaryIndexingMode(VM&);
480
481     // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
482     //  - the prototype chain is not consulted
483     //  - accessors are not called.
484     //  - attributes will be respected (after the call the property will exist with the given attributes)
485     //  - the property name is assumed to not be an index.
486     void putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0);
487     void putDirect(VM&, PropertyName, JSValue, PutPropertySlot&);
488     void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0);
489     void putDirectNonIndexAccessor(VM&, PropertyName, JSValue, unsigned attributes);
490     void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
491     JS_EXPORT_PRIVATE void putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes);
492
493     void putGetter(ExecState*, PropertyName, JSValue, unsigned attributes);
494     void putSetter(ExecState*, PropertyName, JSValue, unsigned attributes);
495
496     JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const;
497     JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
498     bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const;
499     bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const;
500     bool hasOwnProperty(ExecState*, PropertyName) const;
501     bool hasOwnProperty(ExecState*, unsigned) const;
502
503     JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
504     JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
505
506     JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
507     JS_EXPORT_PRIVATE JSValue ordinaryToPrimitive(ExecState*, PreferredPrimitiveType) const;
508
509     JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue);
510     bool hasInstance(ExecState*, JSValue);
511     static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
512
513     JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
514     JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
515     JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
516
517     JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*);
518     JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
519     JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
520
521     JS_EXPORT_PRIVATE JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
522     bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
523     JS_EXPORT_PRIVATE double toNumber(ExecState*) const;
524     JS_EXPORT_PRIVATE JSString* toString(ExecState*) const;
525
526     JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode);
527
528     // This get function only looks at the property map.
529     JSValue getDirect(VM& vm, PropertyName propertyName) const
530     {
531         Structure* structure = this->structure(vm);
532         PropertyOffset offset = structure->get(vm, propertyName);
533         checkOffset(offset, structure->inlineCapacity());
534         return offset != invalidOffset ? getDirect(offset) : JSValue();
535     }
536     
537     JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const
538     {
539         Structure* structure = this->structure(vm);
540         PropertyOffset offset = structure->get(vm, propertyName, attributes);
541         checkOffset(offset, structure->inlineCapacity());
542         return offset != invalidOffset ? getDirect(offset) : JSValue();
543     }
544
545     PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName)
546     {
547         Structure* structure = this->structure(vm);
548         PropertyOffset offset = structure->get(vm, propertyName);
549         checkOffset(offset, structure->inlineCapacity());
550         return offset;
551     }
552
553     PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes)
554     {
555         Structure* structure = this->structure(vm);
556         PropertyOffset offset = structure->get(vm, propertyName, attributes);
557         checkOffset(offset, structure->inlineCapacity());
558         return offset;
559     }
560
561     bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
562     ConstPropertyStorage inlineStorageUnsafe() const
563     {
564         return bitwise_cast<ConstPropertyStorage>(this + 1);
565     }
566     PropertyStorage inlineStorageUnsafe()
567     {
568         return bitwise_cast<PropertyStorage>(this + 1);
569     }
570     ConstPropertyStorage inlineStorage() const
571     {
572         ASSERT(hasInlineStorage());
573         return inlineStorageUnsafe();
574     }
575     PropertyStorage inlineStorage()
576     {
577         ASSERT(hasInlineStorage());
578         return inlineStorageUnsafe();
579     }
580         
581     const Butterfly* butterfly() const { return m_butterfly.get(this); }
582     Butterfly* butterfly() { return m_butterfly.get(this); }
583         
584     ConstPropertyStorage outOfLineStorage() const { return m_butterfly.get(this)->propertyStorage(); }
585     PropertyStorage outOfLineStorage() { return m_butterfly.get(this)->propertyStorage(); }
586
587     const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
588     {
589         if (isInlineOffset(offset))
590             return &inlineStorage()[offsetInInlineStorage(offset)];
591         return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
592     }
593
594     WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset)
595     {
596         if (isInlineOffset(offset))
597             return &inlineStorage()[offsetInInlineStorage(offset)];
598         return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
599     }
600
601     void transitionTo(VM&, Structure*);
602
603     JS_EXPORT_PRIVATE bool removeDirect(VM&, PropertyName); // Return true if anything is removed.
604     bool hasCustomProperties() { return structure()->didTransition(); }
605     bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
606     bool hasCustomGetterSetterProperties() { return structure()->hasCustomGetterSetterProperties(); }
607
608     // putOwnDataProperty has 'put' like semantics, however this method:
609     //  - assumes the object contains no own getter/setter properties.
610     //  - provides no special handling for __proto__
611     //  - does not walk the prototype chain (to check for accessors or non-writable properties).
612     // This is used by JSLexicalEnvironment.
613     bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&);
614
615     // Fast access to known property offsets.
616     JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
617     void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); }
618     void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
619
620     JS_EXPORT_PRIVATE void putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes);
621     JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
622     JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
623     JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes);
624     JS_EXPORT_PRIVATE void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes);
625
626     JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
627
628     bool isGlobalObject() const;
629     bool isErrorInstance() const;
630     bool isWithScope() const;
631
632     JS_EXPORT_PRIVATE void seal(VM&);
633     JS_EXPORT_PRIVATE void freeze(VM&);
634     JS_EXPORT_PRIVATE static bool preventExtensions(JSObject*, ExecState*);
635     JS_EXPORT_PRIVATE static bool isExtensible(JSObject*, ExecState*);
636     bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); }
637     bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); }
638 private:
639     ALWAYS_INLINE bool isExtensibleImpl() { return isStructureExtensible(); }
640 public:
641     // You should only call isStructureExtensible() when:
642     // - Performing this check in a way that isn't described in the specification 
643     //   as calling the virtual [[IsExtensible]] trap.
644     // - When you're guaranteed that object->methodTable()->isExtensible isn't
645     //   overridden.
646     ALWAYS_INLINE bool isStructureExtensible() { return structure()->isStructureExtensible(); }
647     // You should call this when performing [[IsExtensible]] trap in a place
648     // that is described in the specification. This performs the fully virtual
649     // [[IsExtensible]] trap.
650     bool isExtensible(ExecState*);
651     bool indexingShouldBeSparse()
652     {
653         return !isStructureExtensible()
654             || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
655     }
656
657     bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
658     void reifyAllStaticProperties(ExecState*);
659
660     JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize);
661     void setButterflyWithoutChangingStructure(VM&, Butterfly*);
662         
663     void setStructure(VM&, Structure*);
664     void setStructureAndButterfly(VM&, Structure*, Butterfly*);
665     void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*);
666     void setStructureAndReallocateStorageIfNecessary(VM&, Structure*);
667
668     JS_EXPORT_PRIVATE void convertToDictionary(VM&);
669
670     void flattenDictionaryObject(VM& vm)
671     {
672         structure(vm)->flattenDictionaryStructure(vm, this);
673     }
674     void shiftButterflyAfterFlattening(VM&, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter);
675
676     JSGlobalObject* globalObject() const
677     {
678         ASSERT(structure()->globalObject());
679         ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
680         return structure()->globalObject();
681     }
682         
683     void switchToSlowPutArrayStorage(VM&);
684         
685     // The receiver is the prototype in this case. The following:
686     //
687     // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...)
688     //
689     // is equivalent to:
690     //
691     // foo->attemptToInterceptPutByIndexOnHole(...);
692     bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow);
693         
694     // Returns 0 if int32 storage cannot be created - either because
695     // indexing should be sparse, we're having a bad time, or because
696     // we already have a more general form of storage (double,
697     // contiguous, array storage).
698     ContiguousJSValues ensureInt32(VM& vm)
699     {
700         if (LIKELY(hasInt32(indexingType())))
701             return m_butterfly.get(this)->contiguousInt32();
702             
703         return ensureInt32Slow(vm);
704     }
705         
706     // Returns 0 if double storage cannot be created - either because
707     // indexing should be sparse, we're having a bad time, or because
708     // we already have a more general form of storage (contiguous,
709     // or array storage).
710     ContiguousDoubles ensureDouble(VM& vm)
711     {
712         if (LIKELY(hasDouble(indexingType())))
713             return m_butterfly.get(this)->contiguousDouble();
714             
715         return ensureDoubleSlow(vm);
716     }
717         
718     // Returns 0 if contiguous storage cannot be created - either because
719     // indexing should be sparse or because we're having a bad time.
720     ContiguousJSValues ensureContiguous(VM& vm)
721     {
722         if (LIKELY(hasContiguous(indexingType())))
723             return m_butterfly.get(this)->contiguous();
724             
725         return ensureContiguousSlow(vm);
726     }
727
728     // Ensure that the object is in a mode where it has array storage. Use
729     // this if you're about to perform actions that would have required the
730     // object to be converted to have array storage, if it didn't have it
731     // already.
732     ArrayStorage* ensureArrayStorage(VM& vm)
733     {
734         if (LIKELY(hasAnyArrayStorage(indexingType())))
735             return m_butterfly.get(this)->arrayStorage();
736
737         return ensureArrayStorageSlow(vm);
738     }
739         
740     static size_t offsetOfInlineStorage();
741         
742     static ptrdiff_t butterflyOffset()
743     {
744         return OBJECT_OFFSETOF(JSObject, m_butterfly);
745     }
746         
747     void* butterflyAddress()
748     {
749         return &m_butterfly;
750     }
751
752     JSValue getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage);
753
754     DECLARE_EXPORT_INFO;
755
756 protected:
757     void finishCreation(VM& vm)
758     {
759         Base::finishCreation(vm);
760         ASSERT(inherits(info()));
761         ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
762         ASSERT(structure()->isObject());
763         ASSERT(classInfo());
764     }
765
766     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
767     {
768         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
769     }
770
771     // To instantiate objects you likely want JSFinalObject, below.
772     // To create derived types you likely want JSNonFinalObject, below.
773     JSObject(VM&, Structure*, Butterfly* = 0);
774         
775     void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize);
776     void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize);
777
778     // Call this if you know that the object is in a mode where it has array
779     // storage. This will assert otherwise.
780     ArrayStorage* arrayStorage()
781     {
782         ASSERT(hasAnyArrayStorage(indexingType()));
783         return m_butterfly.get(this)->arrayStorage();
784     }
785         
786     // Call this if you want to predicate some actions on whether or not the
787     // object is in a mode where it has array storage.
788     ArrayStorage* arrayStorageOrNull()
789     {
790         switch (indexingType()) {
791         case ALL_ARRAY_STORAGE_INDEXING_TYPES:
792             return m_butterfly.get(this)->arrayStorage();
793                 
794         default:
795             return 0;
796         }
797     }
798         
799     size_t butterflyTotalSize();
800     size_t butterflyPreCapacity();
801
802     Butterfly* createInitialUndecided(VM&, unsigned length);
803     ContiguousJSValues createInitialInt32(VM&, unsigned length);
804     ContiguousDoubles createInitialDouble(VM&, unsigned length);
805     ContiguousJSValues createInitialContiguous(VM&, unsigned length);
806         
807     void convertUndecidedForValue(VM&, JSValue);
808     void createInitialForValueAndSet(VM&, unsigned index, JSValue);
809     void convertInt32ForValue(VM&, JSValue);
810         
811     ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength);
812     ArrayStorage* createInitialArrayStorage(VM&);
813         
814     ContiguousJSValues convertUndecidedToInt32(VM&);
815     ContiguousDoubles convertUndecidedToDouble(VM&);
816     ContiguousJSValues convertUndecidedToContiguous(VM&);
817     ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition);
818     ArrayStorage* convertUndecidedToArrayStorage(VM&);
819         
820     ContiguousDoubles convertInt32ToDouble(VM&);
821     ContiguousJSValues convertInt32ToContiguous(VM&);
822     ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition);
823     ArrayStorage* convertInt32ToArrayStorage(VM&);
824     
825     ContiguousJSValues convertDoubleToContiguous(VM&);
826     ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition);
827     ArrayStorage* convertDoubleToArrayStorage(VM&);
828         
829     ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition);
830     ArrayStorage* convertContiguousToArrayStorage(VM&);
831
832         
833     ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&);
834         
835     bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException);
836
837     template<IndexingType indexingShape>
838     void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue);
839     void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
840
841     bool increaseVectorLength(VM&, unsigned newLength);
842     void deallocateSparseIndexMap();
843     bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException);
844     SparseArrayValueMap* allocateSparseIndexMap(VM&);
845         
846     void notifyPresenceOfIndexedAccessors(VM&);
847         
848     bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow);
849         
850     // Call this if you want setIndexQuickly to succeed and you're sure that
851     // the array is contiguous.
852     void ensureLength(VM& vm, unsigned length)
853     {
854         ASSERT(length < MAX_ARRAY_INDEX);
855         ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType()));
856
857         if (m_butterfly.get(this)->vectorLength() < length)
858             ensureLengthSlow(vm, length);
859             
860         if (m_butterfly.get(this)->publicLength() < length)
861             m_butterfly.get(this)->setPublicLength(length);
862     }
863         
864     // Call this if you want to shrink the butterfly backing store, and you're
865     // sure that the array is contiguous.
866     void reallocateAndShrinkButterfly(VM&, unsigned length);
867     
868     template<IndexingType indexingShape>
869     unsigned countElements(Butterfly*);
870         
871     // This is relevant to undecided, int32, double, and contiguous.
872     unsigned countElements();
873         
874 private:
875     friend class LLIntOffsetsExtractor;
876         
877     // Nobody should ever ask any of these questions on something already known to be a JSObject.
878     using JSCell::isAPIValueWrapper;
879     using JSCell::isGetterSetter;
880     void getObject();
881     void getString(ExecState* exec);
882     void isObject();
883     void isString();
884         
885     Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize);
886         
887     ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*);
888         
889     template<PutMode>
890     bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&);
891     bool canPerformFastPutInline(ExecState* exec, VM&, PropertyName);
892
893     JS_EXPORT_PRIVATE NEVER_INLINE void putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&);
894
895     bool getNonIndexPropertySlot(ExecState*, PropertyName, PropertySlot&);
896     bool getOwnNonIndexPropertySlot(VM&, Structure&, PropertyName, PropertySlot&);
897     JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset);
898     void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure&);
899
900     JS_EXPORT_PRIVATE const HashTableValue* findPropertyHashEntry(PropertyName) const;
901         
902     void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old);
903         
904     void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
905     bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
906     JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode);
907         
908     unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength);
909     unsigned getNewVectorLength(unsigned desiredLength);
910
911     ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength);
912         
913     JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue);
914     JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue);
915     JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue);
916         
917     void ensureLengthSlow(VM&, unsigned length);
918         
919     ContiguousJSValues ensureInt32Slow(VM&);
920     ContiguousDoubles ensureDoubleSlow(VM&);
921     ContiguousJSValues ensureContiguousSlow(VM&);
922     JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&);
923
924 protected:
925     CopyBarrier<Butterfly> m_butterfly;
926 #if USE(JSVALUE32_64)
927 private:
928     uint32_t m_padding;
929 #endif
930 };
931
932 // JSNonFinalObject is a type of JSObject that has some internal storage,
933 // but also preserves some space in the collector cell for additional
934 // data members in derived types.
935 class JSNonFinalObject : public JSObject {
936     friend class JSObject;
937
938 public:
939     typedef JSObject Base;
940
941     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
942     {
943         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
944     }
945
946 protected:
947     explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0)
948         : JSObject(vm, structure, butterfly)
949     {
950     }
951
952     void finishCreation(VM& vm)
953     {
954         Base::finishCreation(vm);
955         ASSERT(!this->structure()->hasInlineStorage());
956         ASSERT(classInfo());
957     }
958 };
959
960 class JSFinalObject;
961
962 // JSFinalObject is a type of JSObject that contains sufficent internal
963 // storage to fully make use of the colloctor cell containing it.
964 class JSFinalObject : public JSObject {
965     friend class JSObject;
966
967 public:
968     typedef JSObject Base;
969     static const unsigned StructureFlags = Base::StructureFlags;
970
971     static size_t allocationSize(size_t inlineCapacity)
972     {
973         return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>);
974     }
975
976     static inline const TypeInfo typeInfo() { return TypeInfo(FinalObjectType, StructureFlags); }
977     static const IndexingType defaultIndexingType = NonArray;
978         
979     static const unsigned defaultSize = 64;
980     static inline unsigned defaultInlineCapacity()
981     {
982         return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
983     }
984
985     static const unsigned maxSize = 512;
986     static inline unsigned maxInlineCapacity()
987     {
988         return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>);
989     }
990
991     static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr);
992     static JSFinalObject* create(VM&, Structure*);
993     static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity)
994     {
995         return Structure::create(vm, globalObject, prototype, typeInfo(), info(), defaultIndexingType, inlineCapacity);
996     }
997
998     JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
999
1000     DECLARE_EXPORT_INFO;
1001
1002 protected:
1003     void visitChildrenCommon(SlotVisitor&);
1004         
1005     void finishCreation(VM& vm)
1006     {
1007         Base::finishCreation(vm);
1008         ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity());
1009         ASSERT(classInfo());
1010     }
1011
1012 private:
1013     friend class LLIntOffsetsExtractor;
1014
1015     explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr)
1016         : JSObject(vm, structure, butterfly)
1017     {
1018     }
1019 };
1020
1021 JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*);
1022
1023 inline JSFinalObject* JSFinalObject::create(
1024     ExecState* exec, Structure* structure, Butterfly* butterfly)
1025 {
1026     JSFinalObject* finalObject = new (
1027         NotNull, 
1028         allocateCell<JSFinalObject>(
1029             *exec->heap(),
1030             allocationSize(structure->inlineCapacity())
1031         )
1032     ) JSFinalObject(exec->vm(), structure, butterfly);
1033     finalObject->finishCreation(exec->vm());
1034     return finalObject;
1035 }
1036
1037 inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure)
1038 {
1039     JSFinalObject* finalObject = new (NotNull, allocateCell<JSFinalObject>(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure);
1040     finalObject->finishCreation(vm);
1041     return finalObject;
1042 }
1043
1044 inline bool isJSFinalObject(JSCell* cell)
1045 {
1046     return cell->classInfo() == JSFinalObject::info();
1047 }
1048
1049 inline bool isJSFinalObject(JSValue value)
1050 {
1051     return value.isCell() && isJSFinalObject(value.asCell());
1052 }
1053
1054 inline size_t JSObject::offsetOfInlineStorage()
1055 {
1056     return sizeof(JSObject);
1057 }
1058
1059 inline bool JSObject::isGlobalObject() const
1060 {
1061     return type() == GlobalObjectType;
1062 }
1063
1064 inline bool JSObject::isErrorInstance() const
1065 {
1066     return type() == ErrorInstanceType;
1067 }
1068
1069 inline bool JSObject::isWithScope() const
1070 {
1071     return type() == WithScopeType;
1072 }
1073
1074 inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly)
1075 {
1076     ASSERT(structure);
1077     ASSERT(!butterfly == (!structure->outOfLineCapacity() && !structure->hasIndexingHeader(this)));
1078     m_butterfly.set(vm, this, butterfly);
1079     setStructure(vm, structure);
1080 }
1081
1082 inline void JSObject::setStructure(VM& vm, Structure* structure)
1083 {
1084     ASSERT(structure);
1085     ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this)));
1086     JSCell::setStructure(vm, structure);
1087 }
1088
1089 inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly)
1090 {
1091     m_butterfly.set(vm, this, butterfly);
1092 }
1093
1094 inline CallType getCallData(JSValue value, CallData& callData)
1095 {
1096     CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone;
1097     ASSERT(result == CallTypeNone || value.isValidCallee());
1098     return result;
1099 }
1100
1101 inline ConstructType getConstructData(JSValue value, ConstructData& constructData)
1102 {
1103     ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone;
1104     ASSERT(result == ConstructTypeNone || value.isValidCallee());
1105     return result;
1106 }
1107
1108 inline JSObject* asObject(JSCell* cell)
1109 {
1110     ASSERT(cell->isObject());
1111     return jsCast<JSObject*>(cell);
1112 }
1113
1114 inline JSObject* asObject(JSValue value)
1115 {
1116     return asObject(value.asCell());
1117 }
1118
1119 inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly)
1120     : JSCell(vm, structure)
1121     , m_butterfly(vm, this, butterfly)
1122 {
1123     vm.heap.ascribeOwner(this, butterfly);
1124 }
1125
1126 inline JSValue JSObject::prototype() const
1127 {
1128     return structure()->storedPrototype();
1129 }
1130
1131 // It is safe to call this method with a PropertyName that is actually an index,
1132 // but if so will always return false (doesn't search index storage).
1133 ALWAYS_INLINE bool JSObject::getOwnNonIndexPropertySlot(VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot)
1134 {
1135     unsigned attributes;
1136     PropertyOffset offset = structure.get(vm, propertyName, attributes);
1137     if (!isValidOffset(offset))
1138         return false;
1139
1140     // getPropertySlot relies on this method never returning index properties!
1141     ASSERT(!parseIndex(propertyName));
1142
1143     JSValue value = getDirect(offset);
1144     if (value.isCell()) {
1145         ASSERT(value);
1146         JSCell* cell = value.asCell();
1147         JSType type = cell->type();
1148         switch (type) {
1149         case GetterSetterType:
1150             fillGetterPropertySlot(slot, value, attributes, offset);
1151             return true;
1152         case CustomGetterSetterType:
1153             fillCustomGetterPropertySlot(slot, value, attributes, structure);
1154             return true;
1155         default:
1156             break;
1157         }
1158     }
1159     
1160     slot.setValue(this, attributes, value, offset);
1161     return true;
1162 }
1163
1164 ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JSValue customGetterSetter, unsigned attributes, Structure& structure)
1165 {
1166     if (structure.isDictionary()) {
1167         slot.setCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter());
1168         return;
1169     }
1170     slot.setCacheableCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter());
1171 }
1172
1173 // It may seem crazy to inline a function this large, especially a virtual function,
1174 // but it makes a big difference to property lookup that derived classes can inline their
1175 // base class call to this.
1176 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1177 {
1178     VM& vm = exec->vm();
1179     Structure& structure = *object->structure(vm);
1180     if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
1181         return true;
1182     if (Optional<uint32_t> index = parseIndex(propertyName))
1183         return getOwnPropertySlotByIndex(object, exec, index.value(), slot);
1184     return false;
1185 }
1186
1187 // It may seem crazy to inline a function this large but it makes a big difference
1188 // since this is function very hot in variable lookup
1189 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1190 {
1191     VM& vm = exec->vm();
1192     auto& structureIDTable = vm.heap.structureIDTable();
1193     JSObject* object = this;
1194     while (true) {
1195         if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) {
1196             // If propertyName is an index then we may have missed it (as this loop is using
1197             // getOwnNonIndexPropertySlot), so we cannot safely call the overridden getOwnPropertySlot
1198             // (lest we return a property from a prototype that is shadowed). Check now for an index,
1199             // if so we need to start afresh from this object.
1200             if (Optional<uint32_t> index = parseIndex(propertyName))
1201                 return getPropertySlot(exec, index.value(), slot);
1202             // Safe to continue searching from current position; call getNonIndexPropertySlot to avoid
1203             // parsing the int again.
1204             return object->getNonIndexPropertySlot(exec, propertyName, slot);
1205         }
1206         Structure& structure = *structureIDTable.get(object->structureID());
1207         if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
1208             return true;
1209         JSValue prototype = structure.storedPrototype();
1210         if (!prototype.isObject())
1211             break;
1212         object = asObject(prototype);
1213     }
1214
1215     if (Optional<uint32_t> index = parseIndex(propertyName))
1216         return getPropertySlot(exec, index.value(), slot);
1217     return false;
1218 }
1219
1220 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
1221 {
1222     VM& vm = exec->vm();
1223     auto& structureIDTable = vm.heap.structureIDTable();
1224     JSObject* object = this;
1225     while (true) {
1226         Structure& structure = *structureIDTable.get(object->structureID());
1227         if (structure.classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot))
1228             return true;
1229         JSValue prototype = structure.storedPrototype();
1230         if (!prototype.isObject())
1231             return false;
1232         object = asObject(prototype);
1233     }
1234 }
1235
1236 ALWAYS_INLINE bool JSObject::getNonIndexPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1237 {
1238     // This method only supports non-index PropertyNames.
1239     ASSERT(!parseIndex(propertyName));
1240
1241     VM& vm = exec->vm();
1242     auto& structureIDTable = vm.heap.structureIDTable();
1243     JSObject* object = this;
1244     while (true) {
1245         Structure& structure = *structureIDTable.get(object->structureID());
1246         if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) {
1247             if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot))
1248                 return true;
1249         } else if (structure.classInfo()->methodTable.getOwnPropertySlot(object, exec, propertyName, slot))
1250             return true;
1251         JSValue prototype = structure.storedPrototype();
1252         if (!prototype.isObject())
1253             return false;
1254         object = asObject(prototype);
1255     }
1256 }
1257
1258 inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const
1259 {
1260     PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
1261     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
1262         return slot.getValue(exec, propertyName);
1263     
1264     return jsUndefined();
1265 }
1266
1267 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
1268 {
1269     PropertySlot slot(this, PropertySlot::InternalMethodType::Get);
1270     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
1271         return slot.getValue(exec, propertyName);
1272
1273     return jsUndefined();
1274 }
1275
1276 template<JSObject::PutMode mode>
1277 ALWAYS_INLINE bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot)
1278 {
1279     ASSERT(value);
1280     ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
1281     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1282     ASSERT(!parseIndex(propertyName));
1283
1284     Structure* structure = this->structure(vm);
1285     if (structure->isDictionary()) {
1286         ASSERT(!structure->hasInferredTypes());
1287         
1288         unsigned currentAttributes;
1289         PropertyOffset offset = structure->get(vm, propertyName, currentAttributes);
1290         if (offset != invalidOffset) {
1291             if ((mode == PutModePut) && currentAttributes & ReadOnly)
1292                 return false;
1293
1294             putDirect(vm, offset, value);
1295             structure->didReplaceProperty(offset);
1296             slot.setExistingProperty(this, offset);
1297
1298             if ((attributes & Accessor) != (currentAttributes & Accessor) || (attributes & CustomAccessor) != (currentAttributes & CustomAccessor)) {
1299                 ASSERT(!(attributes & ReadOnly));
1300                 setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
1301             }
1302             return true;
1303         }
1304
1305         if ((mode == PutModePut) && !isStructureExtensible())
1306             return false;
1307
1308         DeferGC deferGC(vm.heap);
1309         Butterfly* newButterfly = butterfly();
1310         if (this->structure()->putWillGrowOutOfLineStorage())
1311             newButterfly = growOutOfLineStorage(vm, this->structure()->outOfLineCapacity(), this->structure()->suggestedNewOutOfLineStorageCapacity());
1312         offset = this->structure()->addPropertyWithoutTransition(vm, propertyName, attributes);
1313         setStructureAndButterfly(vm, this->structure(), newButterfly);
1314
1315         validateOffset(offset);
1316         ASSERT(this->structure()->isValidOffset(offset));
1317         putDirect(vm, offset, value);
1318         slot.setNewProperty(this, offset);
1319         if (attributes & ReadOnly)
1320             this->structure()->setContainsReadOnlyProperties();
1321         return true;
1322     }
1323
1324     PropertyOffset offset;
1325     size_t currentCapacity = this->structure()->outOfLineCapacity();
1326     Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(
1327         structure, propertyName, attributes, offset);
1328     if (newStructure) {
1329         newStructure->willStoreValueForExistingTransition(
1330             vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
1331         
1332         DeferGC deferGC(vm.heap);
1333         Butterfly* newButterfly = butterfly();
1334         if (currentCapacity != newStructure->outOfLineCapacity()) {
1335             ASSERT(newStructure != this->structure());
1336             newButterfly = growOutOfLineStorage(vm, currentCapacity, newStructure->outOfLineCapacity());
1337         }
1338
1339         validateOffset(offset);
1340         ASSERT(newStructure->isValidOffset(offset));
1341         setStructureAndButterfly(vm, newStructure, newButterfly);
1342         putDirect(vm, offset, value);
1343         slot.setNewProperty(this, offset);
1344         return true;
1345     }
1346
1347     unsigned currentAttributes;
1348     bool hasInferredType;
1349     offset = structure->get(vm, propertyName, currentAttributes, hasInferredType);
1350     if (offset != invalidOffset) {
1351         if ((mode == PutModePut) && currentAttributes & ReadOnly)
1352             return false;
1353
1354         structure->didReplaceProperty(offset);
1355         if (UNLIKELY(hasInferredType)) {
1356             structure->willStoreValueForReplace(
1357                 vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
1358         }
1359
1360         slot.setExistingProperty(this, offset);
1361         putDirect(vm, offset, value);
1362
1363         if ((attributes & Accessor) != (currentAttributes & Accessor)) {
1364             ASSERT(!(attributes & ReadOnly));
1365             setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes));
1366         }
1367         return true;
1368     }
1369
1370     if ((mode == PutModePut) && !isStructureExtensible())
1371         return false;
1372
1373     // We want the structure transition watchpoint to fire after this object has switched
1374     // structure. This allows adaptive watchpoints to observe if the new structure is the one
1375     // we want.
1376     DeferredStructureTransitionWatchpointFire deferredWatchpointFire;
1377     
1378     newStructure = Structure::addPropertyTransition(
1379         vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire);
1380     newStructure->willStoreValueForNewTransition(
1381         vm, propertyName, value, slot.context() == PutPropertySlot::PutById);
1382     
1383     validateOffset(offset);
1384     ASSERT(newStructure->isValidOffset(offset));
1385     setStructureAndReallocateStorageIfNecessary(vm, newStructure);
1386
1387     putDirect(vm, offset, value);
1388     slot.setNewProperty(this, offset);
1389     if (attributes & ReadOnly)
1390         newStructure->setContainsReadOnlyProperties();
1391     return true;
1392 }
1393
1394 inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure)
1395 {
1396     ASSERT(oldCapacity <= newStructure->outOfLineCapacity());
1397     
1398     if (oldCapacity == newStructure->outOfLineCapacity()) {
1399         setStructure(vm, newStructure);
1400         return;
1401     }
1402
1403     DeferGC deferGC(vm.heap); 
1404     Butterfly* newButterfly = growOutOfLineStorage(
1405         vm, oldCapacity, newStructure->outOfLineCapacity());
1406     setStructureAndButterfly(vm, newStructure, newButterfly);
1407 }
1408
1409 inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure)
1410 {
1411     setStructureAndReallocateStorageIfNecessary(
1412         vm, structure(vm)->outOfLineCapacity(), newStructure);
1413 }
1414
1415 inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1416 {
1417     ASSERT(value);
1418     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1419     ASSERT(!structure()->hasGetterSetterProperties());
1420     ASSERT(!structure()->hasCustomGetterSetterProperties());
1421
1422     return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot);
1423 }
1424
1425 inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1426 {
1427     ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
1428     ASSERT(!value.isCustomGetterSetter());
1429     PutPropertySlot slot(this);
1430     putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
1431 }
1432
1433 inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1434 {
1435     ASSERT(!value.isGetterSetter());
1436     ASSERT(!value.isCustomGetterSetter());
1437     putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot);
1438 }
1439
1440 inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
1441 {
1442     DeferGC deferGC(vm.heap);
1443     ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
1444     ASSERT(!value.isCustomGetterSetter());
1445     Butterfly* newButterfly = m_butterfly.get(this);
1446     if (structure()->putWillGrowOutOfLineStorage())
1447         newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
1448     Structure* structure = this->structure();
1449     PropertyOffset offset = structure->addPropertyWithoutTransition(vm, propertyName, attributes);
1450     bool shouldOptimize = false;
1451     structure->willStoreValueForNewTransition(vm, propertyName, value, shouldOptimize);
1452     setStructureAndButterfly(vm, structure, newButterfly);
1453     putDirect(vm, offset, value);
1454 }
1455
1456 ALWAYS_INLINE JSObject* Register::object() const
1457 {
1458     return asObject(jsValue());
1459 }
1460
1461 ALWAYS_INLINE Register& Register::operator=(JSObject* object)
1462 {
1463     u.value = JSValue::encode(JSValue(object));
1464     return *this;
1465 }
1466
1467 inline size_t offsetInButterfly(PropertyOffset offset)
1468 {
1469     return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1470 }
1471
1472 inline size_t JSObject::butterflyPreCapacity()
1473 {
1474     if (UNLIKELY(hasIndexingHeader()))
1475         return butterfly()->indexingHeader()->preCapacity(structure());
1476     return 0;
1477 }
1478
1479 inline size_t JSObject::butterflyTotalSize()
1480 {
1481     Structure* structure = this->structure();
1482     Butterfly* butterfly = this->butterfly();
1483     size_t preCapacity;
1484     size_t indexingPayloadSizeInBytes;
1485     bool hasIndexingHeader = this->hasIndexingHeader();
1486
1487     if (UNLIKELY(hasIndexingHeader)) {
1488         preCapacity = butterfly->indexingHeader()->preCapacity(structure);
1489         indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure);
1490     } else {
1491         preCapacity = 0;
1492         indexingPayloadSizeInBytes = 0;
1493     }
1494
1495     return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes);
1496 }
1497
1498 inline int indexRelativeToBase(PropertyOffset offset)
1499 {
1500     if (isOutOfLineOffset(offset))
1501         return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1502     ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
1503     return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
1504 }
1505
1506 inline int offsetRelativeToBase(PropertyOffset offset)
1507 {
1508     if (isOutOfLineOffset(offset))
1509         return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage();
1510     return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
1511 }
1512
1513 // Returns the maximum offset (away from zero) a load instruction will encode.
1514 inline size_t maxOffsetRelativeToBase(PropertyOffset offset)
1515 {
1516     ptrdiff_t addressOffset = offsetRelativeToBase(offset);
1517 #if USE(JSVALUE32_64)
1518     if (addressOffset >= 0)
1519         return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag);
1520 #endif
1521     return static_cast<size_t>(addressOffset);
1522 }
1523
1524 COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment);
1525
1526 ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name)
1527 {
1528     return Identifier::fromString(&vm, name);
1529 }
1530
1531 ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name)
1532 {
1533     return name;
1534 }
1535
1536 bool validateAndApplyPropertyDescriptor(ExecState*, JSObject*, PropertyName, bool isExtensible,
1537     const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException);
1538
1539 // Helper for defining native functions, if you're not using a static hash table.
1540 // Use this macro from within finishCreation() methods in prototypes. This assumes
1541 // you've defined variables called exec, globalObject, and vm, and they
1542 // have the expected meanings.
1543 #define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \
1544     putDirectNativeFunction(\
1545         vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \
1546         (intrinsic), (attributes))
1547
1548 #define JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length, intrinsic) \
1549     putDirectNativeFunctionWithoutTransition(\
1550         vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \
1551         (intrinsic), (attributes))
1552
1553 // As above, but this assumes that the function you're defining doesn't have an
1554 // intrinsic.
1555 #define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \
1556     JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic)
1557
1558 #define JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length) \
1559     JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, (attributes), (length), NoIntrinsic)
1560
1561 // Identical helpers but for builtins. Note that currently, we don't support builtins that are
1562 // also intrinsics, but we probably will do that eventually.
1563 #define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \
1564     putDirectBuiltinFunction(\
1565         vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
1566
1567 #define JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(jsName, generatorName, attributes) \
1568     putDirectBuiltinFunctionWithoutTransition(\
1569         vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes))
1570
1571 // Helper for defining native getters on properties.
1572 #define JSC_NATIVE_INTRINSIC_GETTER(jsName, cppName, attributes, intrinsic)  \
1573     putDirectNativeIntrinsicGetter(\
1574         vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \
1575         (intrinsic), ((attributes) | Accessor))
1576
1577 #define JSC_NATIVE_GETTER(jsName, cppName, attributes) \
1578     JSC_NATIVE_INTRINSIC_GETTER((jsName), (cppName), (attributes), NoIntrinsic)
1579
1580
1581 } // namespace JSC
1582
1583 #endif // JSObject_h