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