X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/f9bf01c6616d5ddcf65b13b33cedf9e387ff7a63..HEAD:/runtime/JSObject.h diff --git a/runtime/JSObject.h b/runtime/JSObject.h index 2b31a65..5c9622c 100644 --- a/runtime/JSObject.h +++ b/runtime/JSObject.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003-2009, 2012-2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,391 +24,1175 @@ #define JSObject_h #include "ArgList.h" +#include "ArrayConventions.h" +#include "ArrayStorage.h" +#include "Butterfly.h" +#include "CallFrame.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" -#include "CallFrame.h" +#include "CopyWriteBarrier.h" +#include "CustomGetterSetter.h" +#include "DeferGC.h" +#include "Heap.h" +#include "HeapInlines.h" +#include "IndexingHeaderInlines.h" #include "JSCell.h" -#include "JSNumberCell.h" -#include "MarkStack.h" #include "PropertySlot.h" +#include "PropertyStorage.h" +#include "PutDirectIndexMode.h" #include "PutPropertySlot.h" -#include "ScopeChain.h" + #include "Structure.h" -#include "JSGlobalData.h" +#include "VM.h" +#include "JSString.h" +#include "SparseArrayValueMap.h" #include namespace JSC { - inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value) +inline JSCell* getJSFunction(JSValue value) +{ + if (value.isCell() && (value.asCell()->type() == JSFunctionType)) + return value.asCell(); + return 0; +} + +class GetterSetter; +class InternalFunction; +class JSFunction; +class LLIntOffsetsExtractor; +class MarkedBlock; +class PropertyDescriptor; +class PropertyNameArray; +class Structure; +struct HashTable; +struct HashTableValue; + +JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&); +extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; + +COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute); +COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute); +COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute); +COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute); +COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute); +COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute); + +class JSFinalObject; + +class JSObject : public JSCell { + friend class BatchedTransitionOptimizer; + friend class JIT; + friend class JSCell; + friend class JSFinalObject; + friend class MarkedBlock; + JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); + + enum PutMode { + PutModePut, + PutModeDefineOwnProperty, + }; + +public: + typedef JSCell Base; + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + + JS_EXPORT_PRIVATE static String className(const JSObject*); + JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*); + + JSValue prototype() const; + JS_EXPORT_PRIVATE void setPrototype(VM&, JSValue prototype); + JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype); + + bool mayInterceptIndexedAccesses() { - if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr)) - return value.asCell(); - return 0; + return structure()->mayInterceptIndexedAccesses(); } - - class HashEntry; - class InternalFunction; - class PropertyDescriptor; - class PropertyNameArray; - class Structure; - struct HashTable; - - // ECMA 262-3 8.6.1 - // Property attributes - enum Attribute { - None = 0, - ReadOnly = 1 << 1, // property can be only read, not written - DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) - DontDelete = 1 << 3, // property can't be deleted - Function = 1 << 4, // property is a function - only used by static hashtables - Getter = 1 << 5, // property is a getter - Setter = 1 << 6 // property is a setter - }; + + JSValue get(ExecState*, PropertyName) const; + JSValue get(ExecState*, unsigned propertyName) const; - typedef EncodedJSValue* PropertyStorage; - typedef const EncodedJSValue* ConstPropertyStorage; + bool fastGetOwnPropertySlot(ExecState*, VM&, Structure&, PropertyName, PropertySlot&); + bool getPropertySlot(ExecState*, PropertyName, PropertySlot&); + bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); - class JSObject : public JSCell { - friend class BatchedTransitionOptimizer; - friend class JIT; - friend class JSCell; + static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); - public: - explicit JSObject(NonNullPassRefPtr); + // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot + // currently returns incorrect results for the DOM window (with non-own properties) + // being returned. Once this is fixed we should migrate code & remove this method. + JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); - virtual void markChildren(MarkStack&); - ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack); + JS_EXPORT_PRIVATE bool allowsAccessFrom(ExecState*); - // The inline virtual destructor cannot be the first virtual function declared - // in the class as it results in the vtable being generated as a weak symbol - virtual ~JSObject(); + unsigned getArrayLength() const + { + if (!hasIndexedProperties(indexingType())) + return 0; + return m_butterfly->publicLength(); + } + + unsigned getVectorLength() + { + if (!hasIndexedProperties(indexingType())) + return 0; + return m_butterfly->vectorLength(); + } + + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + ALWAYS_INLINE void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) + { + if (canSetIndexQuickly(propertyName)) { + setIndexQuickly(exec->vm(), propertyName, value); + return; + } + methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow); + } + + // This is similar to the putDirect* methods: + // - the prototype chain is not consulted + // - accessors are not called. + // - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default). + // This method creates a property with attributes writable, enumerable and configurable all set to true. + bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode) + { + if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) { + setIndexQuickly(exec->vm(), propertyName, value); + return true; + } + return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode); + } + bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value) + { + return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect); + } - JSValue prototype() const; - void setPrototype(JSValue prototype); + // A non-throwing version of putDirect and putDirectIndex. + JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue); + + bool hasIndexingHeader() const + { + return structure()->hasIndexingHeader(this); + } + + bool canGetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->vectorLength()) + return false; + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + return false; + return true; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } - void setStructure(NonNullPassRefPtr); - Structure* inheritorID(); + JSValue getIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_INT32_INDEXING_TYPES: + return jsNumber(m_butterfly->contiguous()[i].get().asInt32()); + case ALL_CONTIGUOUS_INDEXING_TYPES: + return m_butterfly->contiguous()[i].get(); + case ALL_DOUBLE_INDEXING_TYPES: + return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->m_vector[i].get(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return JSValue(); + } + } + + JSValue tryGetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + break; + case ALL_INT32_INDEXING_TYPES: + if (i < m_butterfly->publicLength()) { + JSValue result = m_butterfly->contiguous()[i].get(); + ASSERT(result.isInt32() || !result); + return result; + } + break; + case ALL_CONTIGUOUS_INDEXING_TYPES: + if (i < m_butterfly->publicLength()) + return m_butterfly->contiguous()[i].get(); + break; + case ALL_DOUBLE_INDEXING_TYPES: { + if (i >= m_butterfly->publicLength()) + break; + double result = m_butterfly->contiguousDouble()[i]; + if (result != result) + break; + return JSValue(JSValue::EncodeAsDouble, result); + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + if (i < m_butterfly->arrayStorage()->vectorLength()) + return m_butterfly->arrayStorage()->m_vector[i].get(); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + break; + } + return JSValue(); + } + + JSValue getDirectIndex(ExecState* exec, unsigned i) + { + if (JSValue result = tryGetIndexQuickly(i)) + return result; + PropertySlot slot(this); + if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) + return slot.getValue(exec, i); + return JSValue(); + } + + JSValue getIndex(ExecState* exec, unsigned i) + { + if (JSValue result = tryGetIndexQuickly(i)) + return result; + return get(exec, i); + } + + bool canSetIndexQuickly(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case NonArrayWithArrayStorage: + case ArrayWithArrayStorage: + return i < m_butterfly->vectorLength(); + case NonArrayWithSlowPutArrayStorage: + case ArrayWithSlowPutArrayStorage: + return i < m_butterfly->arrayStorage()->vectorLength() + && !!m_butterfly->arrayStorage()->m_vector[i]; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + bool canSetIndexQuicklyForPutDirect(unsigned i) + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + return false; + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return i < m_butterfly->vectorLength(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + void setIndexQuickly(VM& vm, unsigned i, JSValue v) + { + switch (indexingType()) { + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + FALLTHROUGH; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(vm, this, v); + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + WriteBarrier& x = storage->m_vector[i]; + JSValue old = x.get(); + x.set(vm, this, v); + if (!old) { + ++storage->m_numValuesInVector; + if (i >= storage->length()) + storage->setLength(i + 1); + } + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } - virtual UString className() const; + void initializeIndex(VM& vm, unsigned i, JSValue v) + { + initializeIndex(vm, i, v, indexingType()); + } - JSValue get(ExecState*, const Identifier& propertyName) const; - JSValue get(ExecState*, unsigned propertyName) const; + void initializeIndex(VM& vm, unsigned i, JSValue v, IndexingType indexingType) + { + switch (indexingType) { + case ALL_UNDECIDED_INDEXING_TYPES: { + setIndexQuicklyToUndecided(vm, i, v); + break; + } + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isInt32()) { + convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); + break; + } + FALLTHROUGH; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + m_butterfly->contiguous()[i].set(vm, this, v); + break; + } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); + if (!v.isNumber()) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + double value = v.asNumber(); + if (value != value) { + convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); + return; + } + m_butterfly->contiguousDouble()[i] = value; + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = m_butterfly->arrayStorage(); + ASSERT(i < storage->length()); + ASSERT(i < storage->m_numValuesInVector); + storage->m_vector[i].set(vm, this, v); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + + bool hasSparseMap() + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return false; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return !!m_butterfly->arrayStorage()->m_sparseMap; + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + bool inSparseIndexingMode() + { + switch (indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + case ALL_INT32_INDEXING_TYPES: + case ALL_DOUBLE_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: + return false; + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage()->inSparseMode(); + default: + RELEASE_ASSERT_NOT_REACHED(); + return false; + } + } + + void enterDictionaryIndexingMode(VM&); - bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); - bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); - bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); + // putDirect is effectively an unchecked vesion of 'defineOwnProperty': + // - the prototype chain is not consulted + // - accessors are not called. + // - attributes will be respected (after the call the property will exist with the given attributes) + // - the property name is assumed to not be an index. + void putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); + void putDirect(VM&, PropertyName, JSValue, PutPropertySlot&); + void putDirectWithoutTransition(VM&, PropertyName, JSValue, unsigned attributes = 0); + void putDirectNonIndexAccessor(VM&, PropertyName, JSValue, unsigned attributes); + void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes); + JS_EXPORT_PRIVATE void putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes); - virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); - virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); - virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); + void putGetter(ExecState*, PropertyName, JSValue); + void putSetter(ExecState*, PropertyName, JSValue); - virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); - virtual void put(ExecState*, unsigned propertyName, JSValue value); + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const; + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; + bool hasOwnProperty(ExecState*, PropertyName) const; + bool hasOwnProperty(ExecState*, unsigned) const; - virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); - virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); - virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes); + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); - bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const; + JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); - bool hasProperty(ExecState*, const Identifier& propertyName) const; - bool hasProperty(ExecState*, unsigned propertyName) const; - bool hasOwnProperty(ExecState*, const Identifier& propertyName) const; + bool hasInstance(ExecState*, JSValue); + static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); - virtual bool deleteProperty(ExecState*, const Identifier& propertyName); - virtual bool deleteProperty(ExecState*, unsigned propertyName); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + JS_EXPORT_PRIVATE static uint32_t getEnumerableLength(ExecState*, JSObject*); + JS_EXPORT_PRIVATE static void getStructurePropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty); + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JS_EXPORT_PRIVATE double toNumber(ExecState*) const; + JS_EXPORT_PRIVATE JSString* toString(ExecState*) const; - virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); - virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties); + JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode); - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; + // This get function only looks at the property map. + JSValue getDirect(VM& vm, PropertyName propertyName) const + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName); + checkOffset(offset, structure->inlineCapacity()); + return offset != invalidOffset ? getDirect(offset) : JSValue(); + } + + JSValue getDirect(VM& vm, PropertyName propertyName, unsigned& attributes) const + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); + return offset != invalidOffset ? getDirect(offset) : JSValue(); + } - virtual JSObject* toThisObject(ExecState*) const; - virtual JSObject* unwrappedObject(); + PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName); + checkOffset(offset, structure->inlineCapacity()); + return offset; + } - bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; + PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) + { + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); + return offset; + } - // This get function only looks at the property map. - JSValue getDirect(const Identifier& propertyName) const - { - size_t offset = m_structure->get(propertyName); - return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); - } + bool hasInlineStorage() const { return structure()->hasInlineStorage(); } + ConstPropertyStorage inlineStorageUnsafe() const + { + return bitwise_cast(this + 1); + } + PropertyStorage inlineStorageUnsafe() + { + return bitwise_cast(this + 1); + } + ConstPropertyStorage inlineStorage() const + { + ASSERT(hasInlineStorage()); + return inlineStorageUnsafe(); + } + PropertyStorage inlineStorage() + { + ASSERT(hasInlineStorage()); + return inlineStorageUnsafe(); + } + + const Butterfly* butterfly() const { return m_butterfly.get(); } + Butterfly* butterfly() { return m_butterfly.get(); } + + ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } + PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } - JSValue* getDirectLocation(const Identifier& propertyName) - { - size_t offset = m_structure->get(propertyName); - return offset != WTF::notFound ? locationForOffset(offset) : 0; - } + const WriteBarrierBase* locationForOffset(PropertyOffset offset) const + { + if (isInlineOffset(offset)) + return &inlineStorage()[offsetInInlineStorage(offset)]; + return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; + } - JSValue* getDirectLocation(const Identifier& propertyName, unsigned& attributes) - { - JSCell* specificFunction; - size_t offset = m_structure->get(propertyName, attributes, specificFunction); - return offset != WTF::notFound ? locationForOffset(offset) : 0; - } + WriteBarrierBase* locationForOffset(PropertyOffset offset) + { + if (isInlineOffset(offset)) + return &inlineStorage()[offsetInInlineStorage(offset)]; + return &outOfLineStorage()[offsetInOutOfLineStorage(offset)]; + } - size_t offsetForLocation(JSValue* location) const - { - return location - reinterpret_cast(propertyStorage()); - } + void transitionTo(VM&, Structure*); + + JS_EXPORT_PRIVATE bool removeDirect(VM&, PropertyName); // Return true if anything is removed. + bool hasCustomProperties() { return structure()->didTransition(); } + bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); } + bool hasCustomGetterSetterProperties() { return structure()->hasCustomGetterSetterProperties(); } + + // putOwnDataProperty has 'put' like semantics, however this method: + // - assumes the object contains no own getter/setter properties. + // - provides no special handling for __proto__ + // - does not walk the prototype chain (to check for accessors or non-writable properties). + // This is used by JSLexicalEnvironment. + bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); + + // Fast access to known property offsets. + JSValue getDirect(PropertyOffset offset) const { return locationForOffset(offset)->get(); } + void putDirect(VM& vm, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(vm, this, value); } + void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); } + + JS_EXPORT_PRIVATE void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + JS_EXPORT_PRIVATE JSFunction* putDirectBuiltinFunction(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); + JSFunction* putDirectBuiltinFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, FunctionExecutable*, unsigned attributes); + JS_EXPORT_PRIVATE void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); + + bool isGlobalObject() const; + bool isVariableObject() const; + bool isStaticScopeObject() const; + bool isNameScopeObject() const; + bool isCatchScopeObject() const; + bool isFunctionNameScopeObject() const; + bool isActivationObject() const; + bool isErrorInstance() const; + bool isWithScope() const; + + JS_EXPORT_PRIVATE void seal(VM&); + JS_EXPORT_PRIVATE void freeze(VM&); + JS_EXPORT_PRIVATE void preventExtensions(VM&); + bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); } + bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); } + bool isExtensible() { return structure()->isExtensible(); } + bool indexingShouldBeSparse() + { + return !isExtensible() + || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); + } - void transitionTo(Structure*); + bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } + void reifyStaticFunctionsForDelete(ExecState* exec); - void removeDirect(const Identifier& propertyName); - bool hasCustomProperties() { return !m_structure->isEmpty(); } - bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } + JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize); + void setButterflyWithoutChangingStructure(VM&, Butterfly*); + + void setStructure(VM&, Structure*); + void setStructureAndButterfly(VM&, Structure*, Butterfly*); + void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*); + void setStructureAndReallocateStorageIfNecessary(VM&, Structure*); - void putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); - void putDirect(const Identifier& propertyName, JSValue value, unsigned attr = 0); + void convertToDictionary(VM& vm) + { + setStructure(vm, Structure::toCacheableDictionaryTransition(vm, structure(vm))); + } - void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr = 0); - void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); - void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void flattenDictionaryObject(VM& vm) + { + structure(vm)->flattenDictionaryStructure(vm, this); + } + void shiftButterflyAfterFlattening(VM&, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter); - void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); - void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); - void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); + JSGlobalObject* globalObject() const + { + ASSERT(structure()->globalObject()); + ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); + return structure()->globalObject(); + } + + void switchToSlowPutArrayStorage(VM&); + + // The receiver is the prototype in this case. The following: + // + // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...) + // + // is equivalent to: + // + // foo->attemptToInterceptPutByIndexOnHole(...); + bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + + // Returns 0 if int32 storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (double, + // contiguous, array storage). + ContiguousJSValues ensureInt32(VM& vm) + { + if (LIKELY(hasInt32(indexingType()))) + return m_butterfly->contiguousInt32(); + + return ensureInt32Slow(vm); + } + + // Returns 0 if double storage cannot be created - either because + // indexing should be sparse, we're having a bad time, or because + // we already have a more general form of storage (contiguous, + // or array storage). + ContiguousDoubles ensureDouble(VM& vm) + { + if (LIKELY(hasDouble(indexingType()))) + return m_butterfly->contiguousDouble(); + + return ensureDoubleSlow(vm); + } + + // Returns 0 if contiguous storage cannot be created - either because + // indexing should be sparse or because we're having a bad time. + ContiguousJSValues ensureContiguous(VM& vm) + { + if (LIKELY(hasContiguous(indexingType()))) + return m_butterfly->contiguous(); + + return ensureContiguousSlow(vm); + } - // Fast access to known property offsets. - JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); } - void putDirectOffset(size_t offset, JSValue value) { propertyStorage()[offset] = JSValue::encode(value); } + // Ensure that the object is in a mode where it has array storage. Use + // this if you're about to perform actions that would have required the + // object to be converted to have array storage, if it didn't have it + // already. + ArrayStorage* ensureArrayStorage(VM& vm) + { + if (LIKELY(hasAnyArrayStorage(indexingType()))) + return m_butterfly->arrayStorage(); - void fillGetterPropertySlot(PropertySlot&, JSValue* location); + return ensureArrayStorageSlow(vm); + } + + static size_t offsetOfInlineStorage(); + + static ptrdiff_t butterflyOffset() + { + return OBJECT_OFFSETOF(JSObject, m_butterfly); + } + + void* butterflyAddress() + { + return &m_butterfly; + } - virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); - virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); - virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); - virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName); - virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); + DECLARE_EXPORT_INFO; - virtual bool isGlobalObject() const { return false; } - virtual bool isVariableObject() const { return false; } - virtual bool isActivationObject() const { return false; } - virtual bool isWatchdogException() const { return false; } - virtual bool isNotAnObjectErrorStub() const { return false; } +protected: + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(inherits(info())); + ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT(structure()->isObject()); + ASSERT(classInfo()); + } - void allocatePropertyStorage(size_t oldSize, size_t newSize); - void allocatePropertyStorageInline(size_t oldSize, size_t newSize); - bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } - static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; - static const unsigned nonInlineBaseStorageCapacity = 16; + // To instantiate objects you likely want JSFinalObject, below. + // To create derived types you likely want JSNonFinalObject, below. + JSObject(VM&, Structure*, Butterfly* = 0); + + void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize); + void copyButterfly(CopyVisitor&, Butterfly*, size_t storageSize); - static PassRefPtr createStructure(JSValue prototype) - { - return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + // Call this if you know that the object is in a mode where it has array + // storage. This will assert otherwise. + ArrayStorage* arrayStorage() + { + ASSERT(hasAnyArrayStorage(indexingType())); + return m_butterfly->arrayStorage(); + } + + // Call this if you want to predicate some actions on whether or not the + // object is in a mode where it has array storage. + ArrayStorage* arrayStorageOrNull() + { + switch (indexingType()) { + case ALL_ARRAY_STORAGE_INDEXING_TYPES: + return m_butterfly->arrayStorage(); + + default: + return 0; } + } + + size_t butterflyTotalSize(); + size_t butterflyPreCapacity(); - void flattenDictionaryObject() - { - m_structure->flattenDictionaryStructure(this); - } + Butterfly* createInitialUndecided(VM&, unsigned length); + ContiguousJSValues createInitialInt32(VM&, unsigned length); + ContiguousDoubles createInitialDouble(VM&, unsigned length); + ContiguousJSValues createInitialContiguous(VM&, unsigned length); + + void convertUndecidedForValue(VM&, JSValue); + void createInitialForValueAndSet(VM&, unsigned index, JSValue); + void convertInt32ForValue(VM&, JSValue); + + ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength); + ArrayStorage* createInitialArrayStorage(VM&); + + ContiguousJSValues convertUndecidedToInt32(VM&); + ContiguousDoubles convertUndecidedToDouble(VM&); + ContiguousJSValues convertUndecidedToContiguous(VM&); + ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertUndecidedToArrayStorage(VM&); + + ContiguousDoubles convertInt32ToDouble(VM&); + ContiguousJSValues convertInt32ToContiguous(VM&); + ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertInt32ToArrayStorage(VM&); + + ContiguousJSValues convertDoubleToContiguous(VM&); + ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertDoubleToArrayStorage(VM&); + + ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition); + ArrayStorage* convertContiguousToArrayStorage(VM&); - protected: - static const unsigned StructureFlags = 0; + + ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM&); + + bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); - void putAnonymousValue(unsigned index, JSValue value) - { - ASSERT(index < m_structure->anonymousSlotCount()); - *locationForOffset(index) = value; - } - JSValue getAnonymousValue(unsigned index) const - { - ASSERT(index < m_structure->anonymousSlotCount()); - return *locationForOffset(index); - } + template + void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); + void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); + + bool increaseVectorLength(VM&, unsigned newLength); + void deallocateSparseIndexMap(); + bool defineOwnIndexedProperty(ExecState*, unsigned, const PropertyDescriptor&, bool throwException); + SparseArrayValueMap* allocateSparseIndexMap(VM&); + + void notifyPresenceOfIndexedAccessors(VM&); + + bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); + + // Call this if you want setIndexQuickly to succeed and you're sure that + // the array is contiguous. + void ensureLength(VM& vm, unsigned length) + { + ASSERT(length < MAX_ARRAY_INDEX); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); + + if (m_butterfly->vectorLength() < length) + ensureLengthSlow(vm, length); + + if (m_butterfly->publicLength() < length) + m_butterfly->setPublicLength(length); + } + + // Call this if you want to shrink the butterfly backing store, and you're + // sure that the array is contiguous. + void reallocateAndShrinkButterfly(VM&, unsigned length); + + template + unsigned countElements(Butterfly*); + + // This is relevant to undecided, int32, double, and contiguous. + unsigned countElements(); + +private: + friend class LLIntOffsetsExtractor; + + // Nobody should ever ask any of these questions on something already known to be a JSObject. + using JSCell::isAPIValueWrapper; + using JSCell::isGetterSetter; + void getObject(); + void getString(ExecState* exec); + void isObject(); + void isString(); + + Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize); + + ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); + + template + bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&); - private: - // Nobody should ever ask any of these questions on something already known to be a JSObject. - using JSCell::isAPIValueWrapper; - using JSCell::isGetterSetter; - using JSCell::toObject; - void getObject(); - void getString(ExecState* exec); - void isObject(); - void isString(); -#if USE(JSVALUE32) - void isNumber(); + bool inlineGetOwnPropertySlot(VM&, Structure&, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset); + void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure&); + + const HashTableValue* findPropertyHashEntry(PropertyName) const; + + void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); + + void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*); + JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode); + + unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); + unsigned getNewVectorLength(unsigned desiredLength); + + ArrayStorage* constructConvertedArrayStorageWithoutCopyingElements(VM&, unsigned neededLength); + + JS_EXPORT_PRIVATE void setIndexQuicklyToUndecided(VM&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); + JS_EXPORT_PRIVATE void convertDoubleToContiguousWhilePerformingSetIndex(VM&, unsigned index, JSValue); + + void ensureLengthSlow(VM&, unsigned length); + + ContiguousJSValues ensureInt32Slow(VM&); + ContiguousDoubles ensureDoubleSlow(VM&); + ContiguousJSValues ensureContiguousSlow(VM&); + JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&); + +protected: + CopyWriteBarrier m_butterfly; +#if USE(JSVALUE32_64) +private: + uint32_t m_padding; #endif +}; - ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } - PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } +// JSNonFinalObject is a type of JSObject that has some internal storage, +// but also preserves some space in the collector cell for additional +// data members in derived types. +class JSNonFinalObject : public JSObject { + friend class JSObject; - const JSValue* locationForOffset(size_t offset) const - { - return reinterpret_cast(&propertyStorage()[offset]); - } +public: + typedef JSObject Base; - JSValue* locationForOffset(size_t offset) - { - return reinterpret_cast(&propertyStorage()[offset]); - } + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); + } - void putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); - void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); - void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); +protected: + explicit JSNonFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = 0) + : JSObject(vm, structure, butterfly) + { + } - bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(!this->structure()->hasInlineStorage()); + ASSERT(classInfo()); + } +}; - const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; - Structure* createInheritorID(); +class JSFinalObject; - union { - PropertyStorage m_externalStorage; - EncodedJSValue m_inlineStorage[inlineStorageCapacity]; - }; +// JSFinalObject is a type of JSObject that contains sufficent internal +// storage to fully make use of the colloctor cell containing it. +class JSFinalObject : public JSObject { + friend class JSObject; - RefPtr m_inheritorID; - }; - -inline JSObject* asObject(JSCell* cell) +public: + typedef JSObject Base; + static const unsigned StructureFlags = Base::StructureFlags; + + static size_t allocationSize(size_t inlineCapacity) + { + return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase); + } + + static const unsigned defaultSize = 64; + static inline unsigned defaultInlineCapacity() + { + return (defaultSize - allocationSize(0)) / sizeof(WriteBarrier); + } + + static const unsigned maxSize = 512; + static inline unsigned maxInlineCapacity() + { + return (maxSize - allocationSize(0)) / sizeof(WriteBarrier); + } + + static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr); + static JSFinalObject* create(VM&, Structure*); + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), info(), NonArray, inlineCapacity); + } + + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); + + DECLARE_EXPORT_INFO; + +protected: + void visitChildrenCommon(SlotVisitor&); + + void finishCreation(VM& vm) + { + Base::finishCreation(vm); + ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity()); + ASSERT(classInfo()); + } + +private: + friend class LLIntOffsetsExtractor; + + explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr) + : JSObject(vm, structure, butterfly) + { + } +}; + +inline JSFinalObject* JSFinalObject::create( + ExecState* exec, Structure* structure, Butterfly* butterfly) { - ASSERT(cell->isObject()); - return static_cast(cell); + JSFinalObject* finalObject = new ( + NotNull, + allocateCell( + *exec->heap(), + allocationSize(structure->inlineCapacity()) + ) + ) JSFinalObject(exec->vm(), structure, butterfly); + finalObject->finishCreation(exec->vm()); + return finalObject; } -inline JSObject* asObject(JSValue value) +inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure) { - return asObject(value.asCell()); + JSFinalObject* finalObject = new (NotNull, allocateCell(vm.heap, allocationSize(structure->inlineCapacity()))) JSFinalObject(vm, structure); + finalObject->finishCreation(vm); + return finalObject; } -inline JSObject::JSObject(NonNullPassRefPtr structure) - : JSCell(structure.releaseRef()) // ~JSObject balances this ref() +inline bool isJSFinalObject(JSCell* cell) { - ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); - ASSERT(m_structure->isEmpty()); - ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); -#if USE(JSVALUE64) || USE(JSVALUE32_64) - ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); -#endif + return cell->classInfo() == JSFinalObject::info(); } -inline JSObject::~JSObject() +inline bool isJSFinalObject(JSValue value) { - ASSERT(m_structure); - if (!isUsingInlineStorage()) - delete [] m_externalStorage; - m_structure->deref(); + return value.isCell() && isJSFinalObject(value.asCell()); } -inline JSValue JSObject::prototype() const +inline size_t JSObject::offsetOfInlineStorage() { - return m_structure->storedPrototype(); + return sizeof(JSObject); } -inline void JSObject::setPrototype(JSValue prototype) +inline bool JSObject::isGlobalObject() const { - ASSERT(prototype); - RefPtr newStructure = Structure::changePrototypeTransition(m_structure, prototype); - setStructure(newStructure.release()); + return type() == GlobalObjectType; } -inline void JSObject::setStructure(NonNullPassRefPtr structure) +inline bool JSObject::isVariableObject() const { - m_structure->deref(); - m_structure = structure.releaseRef(); // ~JSObject balances this ref() + return type() == GlobalObjectType || type() == ActivationObjectType; } -inline Structure* JSObject::inheritorID() +inline bool JSObject::isStaticScopeObject() const { - if (m_inheritorID) - return m_inheritorID.get(); - return createInheritorID(); + JSType type = this->type(); + return type == NameScopeObjectType || type == ActivationObjectType; } -inline bool Structure::isUsingInlineStorage() const +inline bool JSObject::isNameScopeObject() const { - return (propertyStorageCapacity() == JSObject::inlineStorageCapacity); + return type() == NameScopeObjectType; } -inline bool JSCell::inherits(const ClassInfo* info) const +inline bool JSObject::isActivationObject() const { - for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { - if (ci == info) - return true; - } - return false; + return type() == ActivationObjectType; } -// this method is here to be after the inline declaration of JSCell::inherits -inline bool JSValue::inherits(const ClassInfo* classInfo) const +inline bool JSObject::isErrorInstance() const { - return isCell() && asCell()->inherits(classInfo); + return type() == ErrorInstanceType; } -ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +inline bool JSObject::isWithScope() const { - if (JSValue* location = getDirectLocation(propertyName)) { - if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) - fillGetterPropertySlot(slot, location); - else - slot.setValueSlot(this, location, offsetForLocation(location)); - return true; - } + return type() == WithScopeType; +} - // non-standard Netscape extension - if (propertyName == exec->propertyNames().underscoreProto) { - slot.setValue(prototype()); - return true; - } +inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly) +{ + ASSERT(structure); + ASSERT(!butterfly == (!structure->outOfLineCapacity() && !structure->hasIndexingHeader(this))); + m_butterfly.set(vm, this, butterfly); + setStructure(vm, structure); +} - return false; +inline void JSObject::setStructure(VM& vm, Structure* structure) +{ + ASSERT(structure); + ASSERT(!m_butterfly == !(structure->outOfLineCapacity() || structure->hasIndexingHeader(this))); + JSCell::setStructure(vm, structure); +} + +inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly) +{ + m_butterfly.set(vm, this, butterfly); +} + +inline CallType getCallData(JSValue value, CallData& callData) +{ + CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; +} + +inline ConstructType getConstructData(JSValue value, ConstructData& constructData) +{ + ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; +} + +inline JSObject* asObject(JSCell* cell) +{ + ASSERT(cell->isObject()); + return jsCast(cell); +} + +inline JSObject* asObject(JSValue value) +{ + return asObject(value.asCell()); +} + +inline JSObject::JSObject(VM& vm, Structure* structure, Butterfly* butterfly) + : JSCell(vm, structure) + , m_butterfly(vm, this, butterfly) +{ + vm.heap.ascribeOwner(this, butterfly); +} + +inline JSValue JSObject::prototype() const +{ + return structure()->storedPrototype(); +} + +ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) +{ + unsigned attributes; + PropertyOffset offset = structure.get(vm, propertyName, attributes); + if (!isValidOffset(offset)) + return false; + + JSValue value = getDirect(offset); + if (structure.hasGetterSetterProperties() && value.isGetterSetter()) + fillGetterPropertySlot(slot, value, attributes, offset); + else if (structure.hasCustomGetterSetterProperties() && value.isCustomGetterSetter()) + fillCustomGetterPropertySlot(slot, value, attributes, structure); + else + slot.setValue(this, attributes, value, offset); + + return true; +} + +ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JSValue customGetterSetter, unsigned attributes, Structure& structure) +{ + if (structure.isDictionary()) { + slot.setCustom(this, attributes, jsCast(customGetterSetter)->getter()); + return; + } + slot.setCacheableCustom(this, attributes, jsCast(customGetterSetter)->getter()); } // It may seem crazy to inline a function this large, especially a virtual function, // but it makes a big difference to property lookup that derived classes can inline their // base class call to this. -ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - return inlineGetOwnPropertySlot(exec, propertyName, slot); + VM& vm = exec->vm(); + Structure& structure = *object->structure(vm); + if (object->inlineGetOwnPropertySlot(vm, structure, propertyName, slot)) + return true; + if (Optional index = parseIndex(propertyName)) + return getOwnPropertySlotByIndex(object, exec, index.value(), slot); + return false; } -ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::fastGetOwnPropertySlot(ExecState* exec, VM& vm, Structure& structure, PropertyName propertyName, PropertySlot& slot) { - if (!structure()->typeInfo().overridesGetOwnPropertySlot()) - return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); - return getOwnPropertySlot(exec, propertyName, slot); + if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(inlineTypeFlags()))) + return inlineGetOwnPropertySlot(vm, structure, propertyName, slot); + return structure.classInfo()->methodTable.getOwnPropertySlot(this, exec, propertyName, slot); } // It may seem crazy to inline a function this large but it makes a big difference // since this is function very hot in variable lookup -ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { + VM& vm = exec->vm(); + auto& structureIDTable = vm.heap.structureIDTable(); JSObject* object = this; while (true) { - if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) + Structure& structure = *structureIDTable.get(object->structureID()); + if (object->fastGetOwnPropertySlot(exec, vm, structure, propertyName, slot)) return true; - JSValue prototype = object->prototype(); + JSValue prototype = structure.storedPrototype(); if (!prototype.isObject()) - return false; + break; object = asObject(prototype); } + + if (Optional index = parseIndex(propertyName)) + return getPropertySlot(exec, index.value(), slot); + return false; } ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { + VM& vm = exec->vm(); + auto& structureIDTable = vm.heap.structureIDTable(); JSObject* object = this; while (true) { - if (object->getOwnPropertySlot(exec, propertyName, slot)) + Structure& structure = *structureIDTable.get(object->structureID()); + if (structure.classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot)) return true; - JSValue prototype = object->prototype(); + JSValue prototype = structure.storedPrototype(); if (!prototype.isObject()) return false; object = asObject(prototype); } } -inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const +inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const { PropertySlot slot(this); if (const_cast(this)->getPropertySlot(exec, propertyName, slot)) @@ -426,265 +1210,278 @@ inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const return jsUndefined(); } -inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) +template +inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot) { ASSERT(value); + ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(!parseIndex(propertyName)); - if (m_structure->isDictionary()) { + Structure* structure = this->structure(vm); + if (structure->isDictionary()) { unsigned currentAttributes; - JSCell* currentSpecificFunction; - size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); - if (offset != WTF::notFound) { - if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) - m_structure->despecifyDictionaryFunction(propertyName); - if (checkReadOnly && currentAttributes & ReadOnly) - return; - putDirectOffset(offset, value); - if (!specificFunction && !currentSpecificFunction) - slot.setExistingProperty(this, offset); - return; + PropertyOffset offset = structure->get(vm, propertyName, currentAttributes); + if (offset != invalidOffset) { + if ((mode == PutModePut) && currentAttributes & ReadOnly) + return false; + + putDirect(vm, offset, value); + structure->didReplaceProperty(offset); + slot.setExistingProperty(this, offset); + + if ((attributes & Accessor) != (currentAttributes & Accessor)) { + ASSERT(!(attributes & ReadOnly)); + setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); + } + return true; } - size_t currentCapacity = m_structure->propertyStorageCapacity(); - offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); + if ((mode == PutModePut) && !isExtensible()) + return false; + + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = butterfly(); + if (this->structure()->putWillGrowOutOfLineStorage()) + newButterfly = growOutOfLineStorage(vm, this->structure()->outOfLineCapacity(), this->structure()->suggestedNewOutOfLineStorageCapacity()); + offset = this->structure()->addPropertyWithoutTransition(vm, propertyName, attributes); + setStructureAndButterfly(vm, this->structure(), newButterfly); - ASSERT(offset < m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); - // See comment on setNewProperty call below. - if (!specificFunction) - slot.setNewProperty(this, offset); - return; + validateOffset(offset); + ASSERT(this->structure()->isValidOffset(offset)); + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + this->structure()->setContainsReadOnlyProperties(); + return true; } - size_t offset; - size_t currentCapacity = m_structure->propertyStorageCapacity(); - if (RefPtr structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) { - if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + PropertyOffset offset; + size_t currentCapacity = this->structure()->outOfLineCapacity(); + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, offset)) { + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = butterfly(); + if (currentCapacity != structure->outOfLineCapacity()) { + ASSERT(structure != this->structure()); + newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); + } - ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // See comment on setNewProperty call below. - if (!specificFunction) - slot.setNewProperty(this, offset); - return; + validateOffset(offset); + ASSERT(structure->isValidOffset(offset)); + setStructureAndButterfly(vm, structure, newButterfly); + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + return true; } unsigned currentAttributes; - JSCell* currentSpecificFunction; - offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); - if (offset != WTF::notFound) { - if (checkReadOnly && currentAttributes & ReadOnly) - return; + offset = structure->get(vm, propertyName, currentAttributes); + if (offset != invalidOffset) { + if ((mode == PutModePut) && currentAttributes & ReadOnly) + return false; - if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) { - setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. - return; - } - putDirectOffset(offset, value); + structure->didReplaceProperty(offset); slot.setExistingProperty(this, offset); - return; - } + putDirect(vm, offset, value); - // If we have a specific function, we may have got to this point if there is - // already a transition with the correct property name and attributes, but - // specialized to a different function. In this case we just want to give up - // and despecialize the transition. - // In this case we clear the value of specificFunction which will result - // in us adding a non-specific transition, and any subsequent lookup in - // Structure::addPropertyTransitionToExistingStructure will just use that. - if (specificFunction && m_structure->hasTransition(propertyName, attributes)) - specificFunction = 0; - - RefPtr structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); + if ((attributes & Accessor) != (currentAttributes & Accessor)) { + ASSERT(!(attributes & ReadOnly)); + setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); + } + return true; + } - if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + if ((mode == PutModePut) && !isExtensible()) + return false; - ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. - if (!specificFunction) - slot.setNewProperty(this, offset); + structure = Structure::addPropertyTransition(vm, structure, propertyName, attributes, offset, slot.context()); + + validateOffset(offset); + ASSERT(structure->isValidOffset(offset)); + setStructureAndReallocateStorageIfNecessary(vm, structure); + + putDirect(vm, offset, value); + slot.setNewProperty(this, offset); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); + return true; } -inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure) { - ASSERT(value); - ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(oldCapacity <= newStructure->outOfLineCapacity()); + + if (oldCapacity == newStructure->outOfLineCapacity()) { + setStructure(vm, newStructure); + return; + } - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = growOutOfLineStorage( + vm, oldCapacity, newStructure->outOfLineCapacity()); + setStructureAndButterfly(vm, newStructure, newButterfly); } -inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure) { - PutPropertySlot slot; - putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); + setStructureAndReallocateStorageIfNecessary( + vm, structure(vm)->outOfLineCapacity(), newStructure); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(!structure()->hasGetterSetterProperties()); + ASSERT(!structure()->hasCustomGetterSetterProperties()); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); + return putDirectInternal(vm, propertyName, value, 0, slot); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) { - PutPropertySlot slot; - putDirectInternal(propertyName, value, attributes, false, slot, 0); + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + ASSERT(!value.isCustomGetterSetter()); + PutPropertySlot slot(this); + putDirectInternal(vm, propertyName, value, attributes, slot); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); + ASSERT(!value.isGetterSetter()); + ASSERT(!value.isCustomGetterSetter()); + putDirectInternal(vm, propertyName, value, 0, slot); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr) +inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) { - PutPropertySlot slot; - putDirectInternal(propertyName, value, attr, false, slot, value); + DeferGC deferGC(vm.heap); + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + ASSERT(!value.isCustomGetterSetter()); + Butterfly* newButterfly = m_butterfly.get(); + if (structure()->putWillGrowOutOfLineStorage()) + newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); + PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes); + setStructureAndButterfly(vm, structure(), newButterfly); + putDirect(vm, offset, value); } -inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes) +inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const { - size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + return methodTable()->defaultValue(this, exec, preferredType); } -inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes) +ALWAYS_INLINE JSObject* Register::object() const { - size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + return asObject(jsValue()); } -inline void JSObject::transitionTo(Structure* newStructure) +ALWAYS_INLINE Register& Register::operator=(JSObject* object) { - if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) - allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); - setStructure(newStructure); + u.value = JSValue::encode(JSValue(object)); + return *this; } -inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const +inline size_t offsetInButterfly(PropertyOffset offset) { - return defaultValue(exec, preferredType); + return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); } -inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const +inline size_t JSObject::butterflyPreCapacity() { - PropertySlot slot(asValue()); - return get(exec, propertyName, slot); + if (UNLIKELY(hasIndexingHeader())) + return butterfly()->indexingHeader()->preCapacity(structure()); + return 0; } -inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const +inline size_t JSObject::butterflyTotalSize() { - if (UNLIKELY(!isCell())) { - JSObject* prototype = synthesizePrototype(exec); - if (propertyName == exec->propertyNames().underscoreProto) - return prototype; - if (!prototype->getPropertySlot(exec, propertyName, slot)) - return jsUndefined(); - return slot.getValue(exec, propertyName); + Structure* structure = this->structure(); + Butterfly* butterfly = this->butterfly(); + size_t preCapacity; + size_t indexingPayloadSizeInBytes; + bool hasIndexingHeader = this->hasIndexingHeader(); + + if (UNLIKELY(hasIndexingHeader)) { + preCapacity = butterfly->indexingHeader()->preCapacity(structure); + indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); + } else { + preCapacity = 0; + indexingPayloadSizeInBytes = 0; } - JSCell* cell = asCell(); - while (true) { - if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) - return slot.getValue(exec, propertyName); - JSValue prototype = asObject(cell)->prototype(); - if (!prototype.isObject()) - return jsUndefined(); - cell = asObject(prototype); - } -} -inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const -{ - PropertySlot slot(asValue()); - return get(exec, propertyName, slot); + return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes); } -inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const +// Helpers for patching code where you want to emit a load or store and +// the base is: +// For inline offsets: a pointer to the out-of-line storage pointer. +// For out-of-line offsets: the base of the out-of-line storage. +inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset) { - if (UNLIKELY(!isCell())) { - JSObject* prototype = synthesizePrototype(exec); - if (!prototype->getPropertySlot(exec, propertyName, slot)) - return jsUndefined(); - return slot.getValue(exec, propertyName); - } - JSCell* cell = const_cast(asCell()); - while (true) { - if (cell->getOwnPropertySlot(exec, propertyName, slot)) - return slot.getValue(exec, propertyName); - JSValue prototype = asObject(cell)->prototype(); - if (!prototype.isObject()) - return jsUndefined(); - cell = prototype.asCell(); - } + if (isOutOfLineOffset(offset)) + return sizeof(EncodedJSValue) * offsetInButterfly(offset); + return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); } -inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +// Returns the maximum offset (away from zero) a load instruction will encode. +inline size_t maxOffsetRelativeToPatchedStorage(PropertyOffset offset) { - if (UNLIKELY(!isCell())) { - synthesizeObject(exec)->put(exec, propertyName, value, slot); - return; - } - asCell()->put(exec, propertyName, value, slot); + ptrdiff_t addressOffset = static_cast(offsetRelativeToPatchedStorage(offset)); +#if USE(JSVALUE32_64) + if (addressOffset >= 0) + return static_cast(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); +#endif + return static_cast(addressOffset); } -inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) +inline int indexRelativeToBase(PropertyOffset offset) { - if (UNLIKELY(!isCell())) { - synthesizeObject(exec)->put(exec, propertyName, value); - return; - } - asCell()->put(exec, propertyName, value); + if (isOutOfLineOffset(offset)) + return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); + ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue))); + return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset); } -ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize) +inline int offsetRelativeToBase(PropertyOffset offset) { - ASSERT(newSize > oldSize); - - // It's important that this function not rely on m_structure, since - // we might be in the middle of a transition. - bool wasInline = (oldSize == JSObject::inlineStorageCapacity); - - PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage); - PropertyStorage newPropertyStorage = new EncodedJSValue[newSize]; - - for (unsigned i = 0; i < oldSize; ++i) - newPropertyStorage[i] = oldPropertyStorage[i]; + if (isOutOfLineOffset(offset)) + return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage(); + return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); +} - if (!wasInline) - delete [] oldPropertyStorage; +COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase)), JSObject_inline_storage_has_correct_alignment); - m_externalStorage = newPropertyStorage; +ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) +{ + return Identifier::fromString(&vm, name); } -ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) +ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) { - JSCell::markChildren(markStack); - - markStack.append(prototype()); - - PropertyStorage storage = propertyStorage(); - size_t storageSize = m_structure->propertyStorageSize(); - markStack.appendValues(reinterpret_cast(storage), storageSize); + return name; } +// Helper for defining native functions, if you're not using a static hash table. +// Use this macro from within finishCreation() methods in prototypes. This assumes +// you've defined variables called exec, globalObject, and vm, and they +// have the expected meanings. +#define JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, attributes, length, intrinsic) \ + putDirectNativeFunction(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ + (intrinsic), (attributes)) + +// As above, but this assumes that the function you're defining doesn't have an +// intrinsic. +#define JSC_NATIVE_FUNCTION(jsName, cppName, attributes, length) \ + JSC_NATIVE_INTRINSIC_FUNCTION(jsName, cppName, (attributes), (length), NoIntrinsic) + +// Identical helpers but for builtins. Note that currently, we don't support builtins that are +// also intrinsics, but we probably will do that eventually. +#define JSC_BUILTIN_FUNCTION(jsName, generatorName, attributes) \ + putDirectBuiltinFunction(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) + } // namespace JSC #endif // JSObject_h