diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSObject.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSObject.h | 1196 |
1 files changed, 608 insertions, 588 deletions
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index 02b137b35..6140094fe 100644 --- a/Source/JavaScriptCore/runtime/JSObject.h +++ b/Source/JavaScriptCore/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, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 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 @@ -20,17 +20,18 @@ * */ -#ifndef JSObject_h -#define JSObject_h +#pragma once #include "ArgList.h" #include "ArrayConventions.h" #include "ArrayStorage.h" +#include "AuxiliaryBarrier.h" #include "Butterfly.h" +#include "CPU.h" #include "CallFrame.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" -#include "CopyWriteBarrier.h" +#include "CustomGetterSetter.h" #include "DeferGC.h" #include "Heap.h" #include "IndexingHeaderInlines.h" @@ -39,7 +40,7 @@ #include "PropertyStorage.h" #include "PutDirectIndexMode.h" #include "PutPropertySlot.h" - +#include "RuntimeType.h" #include "Structure.h" #include "VM.h" #include "JSString.h" @@ -47,41 +48,43 @@ #include <wtf/StdLibExtras.h> namespace JSC { +namespace DOMJIT { +class Signature; +} inline JSCell* getJSFunction(JSValue value) { - if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType)) + if (value.isCell() && (value.asCell()->type() == JSFunctionType)) return value.asCell(); return 0; } -JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*); - -inline JSCell* getCallableObject(JSValue value) -{ - if (!value.isCell()) - return 0; - return getCallableObjectSlow(value.asCell()); -} - class GetterSetter; -class HashEntry; class InternalFunction; +class JSFunction; class LLIntOffsetsExtractor; class MarkedBlock; class PropertyDescriptor; class PropertyNameArray; class Structure; +class ThrowScope; struct HashTable; - -JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&); -extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; +struct HashTableValue; + +JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, ThrowScope&, const String&); +extern JS_EXPORTDATA const char* const NonExtensibleObjectPropertyDefineError; +extern JS_EXPORTDATA const char* const ReadonlyPropertyWriteError; +extern JS_EXPORTDATA const char* const ReadonlyPropertyChangeError; +extern JS_EXPORTDATA const char* const UnableToDeletePropertyError; +extern JS_EXPORTDATA const char* const UnconfigurablePropertyChangeAccessMechanismError; +extern JS_EXPORTDATA const char* const UnconfigurablePropertyChangeConfigurabilityError; +extern JS_EXPORTDATA const char* const UnconfigurablePropertyChangeEnumerabilityError; +extern JS_EXPORTDATA const char* const UnconfigurablePropertyChangeWritabilityError; 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; @@ -92,7 +95,7 @@ class JSObject : public JSCell { friend class JSCell; friend class JSFinalObject; friend class MarkedBlock; - JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(VM&, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); enum PutMode { PutModePut, @@ -101,15 +104,55 @@ class JSObject : public JSCell { public: typedef JSCell Base; - + + // This is a super dangerous method for JITs. Sometimes the JITs will want to create either a + // JSFinalObject or a JSArray. This is the method that will do that. + static JSObject* createRawObject(ExecState* exec, Structure* structure, Butterfly* = nullptr); + + JS_EXPORT_PRIVATE static size_t estimatedSize(JSCell*); JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); - JS_EXPORT_PRIVATE static void copyBackingStore(JSCell*, CopyVisitor&, CopyToken); + JS_EXPORT_PRIVATE static void heapSnapshot(JSCell*, HeapSnapshotBuilder&); JS_EXPORT_PRIVATE static String className(const JSObject*); - - JSValue prototype() const; - void setPrototype(VM&, JSValue prototype); - bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype); + JS_EXPORT_PRIVATE static String calculatedClassName(JSObject*); + + // This function is what Object.prototype.toString() will use to get the name of + // an object when using Symbol.toStringTag fails. For the most part there is no + // difference between this and className(). The main use case is for new JS language + // objects to set the default tag to "Object". + JS_EXPORT_PRIVATE static String toStringName(const JSObject*, ExecState*); + + // This is the fully virtual [[GetPrototypeOf]] internal function defined + // in the ECMAScript 6 specification. Use this when doing a [[GetPrototypeOf]] + // operation as dictated in the specification. + JSValue getPrototype(VM&, ExecState*); + JS_EXPORT_PRIVATE static JSValue getPrototype(JSObject*, ExecState*); + // This gets the prototype directly off of the structure. This does not do + // dynamic dispatch on the getPrototype method table method. It is not valid + // to use this when performing a [[GetPrototypeOf]] operation in the specification. + // It is valid to use though when you know that you want to directly get it + // without consulting the method table. This is akin to getting the [[Prototype]] + // internal field directly as described in the specification. + JSValue getPrototypeDirect() const; + + // This sets the prototype without checking for cycles and without + // doing dynamic dispatch on [[SetPrototypeOf]] operation in the specification. + // It is not valid to use this when performing a [[SetPrototypeOf]] operation in + // the specification. It is valid to use though when you know that you want to directly + // set it without consulting the method table and when you definitely won't + // introduce a cycle in the prototype chain. This is akin to setting the + // [[Prototype]] internal field directly as described in the specification. + JS_EXPORT_PRIVATE void setPrototypeDirect(VM&, JSValue prototype); +private: + // This is OrdinarySetPrototypeOf in the specification. Section 9.1.2.1 + // https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof + JS_EXPORT_PRIVATE bool setPrototypeWithCycleCheck(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet); +public: + // This is the fully virtual [[SetPrototypeOf]] internal function defined + // in the ECMAScript 6 specification. Use this when doing a [[SetPrototypeOf]] + // operation as dictated in the specification. + bool setPrototype(VM&, ExecState*, JSValue prototype, bool shouldThrowIfCantSet = false); + JS_EXPORT_PRIVATE static bool setPrototype(JSObject*, ExecState*, JSValue prototype, bool shouldThrowIfCantSet); bool mayInterceptIndexedAccesses() { @@ -119,9 +162,10 @@ public: JSValue get(ExecState*, PropertyName) const; JSValue get(ExecState*, unsigned propertyName) const; - bool fastGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&); bool getPropertySlot(ExecState*, PropertyName, PropertySlot&); bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, CallbackWhenNoException) const; + template<typename CallbackWhenNoException> typename std::result_of<CallbackWhenNoException(bool, PropertySlot&)>::type getPropertySlot(ExecState*, PropertyName, PropertySlot&, CallbackWhenNoException) const; static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&); JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&); @@ -129,34 +173,35 @@ public: // 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. - bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); - - bool allowsAccessFrom(ExecState*); + JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); unsigned getArrayLength() const { - if (!hasIndexedProperties(structure()->indexingType())) + if (!hasIndexedProperties(indexingType())) return 0; - return m_butterfly->publicLength(); + return m_butterfly.get()->publicLength(); } unsigned getVectorLength() { - if (!hasIndexedProperties(structure()->indexingType())) + if (!hasIndexedProperties(indexingType())) return 0; - return m_butterfly->vectorLength(); + return m_butterfly.get()->vectorLength(); } + + static bool putInline(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + + JS_EXPORT_PRIVATE static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); + // putByIndex assumes that the receiver is this JSCell object. + JS_EXPORT_PRIVATE static bool putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); - JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); - - void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) + ALWAYS_INLINE bool putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) { if (canSetIndexQuickly(propertyName)) { setIndexQuickly(exec->vm(), propertyName, value); - return; + return true; } - methodTable()->putByIndex(this, exec, propertyName, value, shouldThrow); + return methodTable(exec->vm())->putByIndex(this, exec, propertyName, value, shouldThrow); } // This is similar to the putDirect* methods: @@ -178,7 +223,7 @@ public: } // A non-throwing version of putDirect and putDirectIndex. - JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue); + JS_EXPORT_PRIVATE bool putDirectMayBeIndex(ExecState*, PropertyName, JSValue); bool hasIndexingHeader() const { @@ -187,23 +232,24 @@ public: bool canGetIndexQuickly(unsigned i) { - switch (structure()->indexingType()) { + Butterfly* butterfly = m_butterfly.get(); + 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]; + return i < butterfly->vectorLength() && butterfly->contiguous()[i]; case ALL_DOUBLE_INDEXING_TYPES: { - if (i >= m_butterfly->vectorLength()) + if (i >= butterfly->vectorLength()) return false; - double value = m_butterfly->contiguousDouble()[i]; + double value = 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]; + return i < butterfly->arrayStorage()->vectorLength() && butterfly->arrayStorage()->m_vector[i]; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -212,49 +258,51 @@ public: JSValue getIndexQuickly(unsigned i) { - switch (structure()->indexingType()) { + Butterfly* butterfly = m_butterfly.get(); + switch (indexingType()) { case ALL_INT32_INDEXING_TYPES: - return jsNumber(m_butterfly->contiguous()[i].get().asInt32()); + return jsNumber(butterfly->contiguous()[i].get().asInt32()); case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->contiguous()[i].get(); + return butterfly->contiguous()[i].get(); case ALL_DOUBLE_INDEXING_TYPES: - return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); + return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble()[i]); case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->m_vector[i].get(); + return butterfly->arrayStorage()->m_vector[i].get(); default: RELEASE_ASSERT_NOT_REACHED(); return JSValue(); } } - JSValue tryGetIndexQuickly(unsigned i) + JSValue tryGetIndexQuickly(unsigned i) const { - switch (structure()->indexingType()) { + Butterfly* butterfly = m_butterfly.get(); + 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(); + if (i < butterfly->publicLength()) { + JSValue result = 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(); + if (i < butterfly->publicLength()) + return butterfly->contiguous()[i].get(); break; case ALL_DOUBLE_INDEXING_TYPES: { - if (i >= m_butterfly->publicLength()) + if (i >= butterfly->publicLength()) break; - double result = m_butterfly->contiguousDouble()[i]; + double result = 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(); + if (i < butterfly->arrayStorage()->vectorLength()) + return butterfly->arrayStorage()->m_vector[i].get(); break; default: RELEASE_ASSERT_NOT_REACHED(); @@ -267,13 +315,13 @@ public: { if (JSValue result = tryGetIndexQuickly(i)) return result; - PropertySlot slot(this); - if (methodTable()->getOwnPropertySlotByIndex(this, exec, i, slot)) + PropertySlot slot(this, PropertySlot::InternalMethodType::Get); + if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) return slot.getValue(exec, i); return JSValue(); } - JSValue getIndex(ExecState* exec, unsigned i) + JSValue getIndex(ExecState* exec, unsigned i) const { if (JSValue result = tryGetIndexQuickly(i)) return result; @@ -282,7 +330,8 @@ public: bool canSetIndexQuickly(unsigned i) { - switch (structure()->indexingType()) { + Butterfly* butterfly = m_butterfly.get(); + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return false; @@ -291,11 +340,11 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: case NonArrayWithArrayStorage: case ArrayWithArrayStorage: - return i < m_butterfly->vectorLength(); + return i < butterfly->vectorLength(); case NonArrayWithSlowPutArrayStorage: case ArrayWithSlowPutArrayStorage: - return i < m_butterfly->arrayStorage()->vectorLength() - && !!m_butterfly->arrayStorage()->m_vector[i]; + return i < butterfly->arrayStorage()->vectorLength() + && !!butterfly->arrayStorage()->m_vector[i]; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -304,7 +353,7 @@ public: bool canSetIndexQuicklyForPutDirect(unsigned i) { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return false; @@ -312,7 +361,7 @@ public: case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return i < m_butterfly->vectorLength(); + return i < m_butterfly.get()->vectorLength(); default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -321,9 +370,10 @@ public: void setIndexQuickly(VM& vm, unsigned i, JSValue v) { - switch (structure()->indexingType()) { + Butterfly* butterfly = m_butterfly.get(); + switch (indexingType()) { case ALL_INT32_INDEXING_TYPES: { - ASSERT(i < m_butterfly->vectorLength()); + ASSERT(i < butterfly->vectorLength()); if (!v.isInt32()) { convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -331,14 +381,14 @@ public: 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); + ASSERT(i < butterfly->vectorLength()); + butterfly->contiguous()[i].set(vm, this, v); + if (i >= butterfly->publicLength()) + butterfly->setPublicLength(i + 1); break; } case ALL_DOUBLE_INDEXING_TYPES: { - ASSERT(i < m_butterfly->vectorLength()); + ASSERT(i < butterfly->vectorLength()); if (!v.isNumber()) { convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -348,13 +398,13 @@ public: convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; } - m_butterfly->contiguousDouble()[i] = value; - if (i >= m_butterfly->publicLength()) - m_butterfly->setPublicLength(i + 1); + butterfly->contiguousDouble()[i] = value; + if (i >= butterfly->publicLength()) + butterfly->setPublicLength(i + 1); break; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = m_butterfly->arrayStorage(); + ArrayStorage* storage = butterfly->arrayStorage(); WriteBarrier<Unknown>& x = storage->m_vector[i]; JSValue old = x.get(); x.set(vm, this, v); @@ -369,17 +419,25 @@ public: RELEASE_ASSERT_NOT_REACHED(); } } - + void initializeIndex(VM& vm, unsigned i, JSValue v) { - switch (structure()->indexingType()) { + initializeIndex(vm, i, v, indexingType()); + } + + // NOTE: Clients of this method may call it more than once for any index, and this is supposed + // to work. + ALWAYS_INLINE void initializeIndex(VM& vm, unsigned i, JSValue v, IndexingType indexingType) + { + Butterfly* butterfly = m_butterfly.get(); + 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()); + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); if (!v.isInt32()) { convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); break; @@ -387,14 +445,14 @@ public: FALLTHROUGH; } case ALL_CONTIGUOUS_INDEXING_TYPES: { - ASSERT(i < m_butterfly->publicLength()); - ASSERT(i < m_butterfly->vectorLength()); - m_butterfly->contiguous()[i].set(vm, this, v); + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); + butterfly->contiguous()[i].set(vm, this, v); break; } case ALL_DOUBLE_INDEXING_TYPES: { - ASSERT(i < m_butterfly->publicLength()); - ASSERT(i < m_butterfly->vectorLength()); + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); if (!v.isNumber()) { convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -404,11 +462,11 @@ public: convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; } - m_butterfly->contiguousDouble()[i] = value; + butterfly->contiguousDouble()[i] = value; break; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = m_butterfly->arrayStorage(); + ArrayStorage* storage = butterfly->arrayStorage(); ASSERT(i < storage->length()); ASSERT(i < storage->m_numValuesInVector); storage->m_vector[i].set(vm, this, v); @@ -419,9 +477,57 @@ public: } } + void initializeIndexWithoutBarrier(unsigned i, JSValue v) + { + initializeIndexWithoutBarrier(i, v, indexingType()); + } + + // This version of initializeIndex is for cases where you know that you will not need any + // barriers. This implies not having any data format conversions. + ALWAYS_INLINE void initializeIndexWithoutBarrier(unsigned i, JSValue v, IndexingType indexingType) + { + Butterfly* butterfly = m_butterfly.get(); + switch (indexingType) { + case ALL_UNDECIDED_INDEXING_TYPES: { + RELEASE_ASSERT_NOT_REACHED(); + break; + } + case ALL_INT32_INDEXING_TYPES: { + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); + RELEASE_ASSERT(v.isInt32()); + FALLTHROUGH; + } + case ALL_CONTIGUOUS_INDEXING_TYPES: { + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); + butterfly->contiguous()[i].setWithoutWriteBarrier(v); + break; + } + case ALL_DOUBLE_INDEXING_TYPES: { + ASSERT(i < butterfly->publicLength()); + ASSERT(i < butterfly->vectorLength()); + RELEASE_ASSERT(v.isNumber()); + double value = v.asNumber(); + RELEASE_ASSERT(value == value); + butterfly->contiguousDouble()[i] = value; + break; + } + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = butterfly->arrayStorage(); + ASSERT(i < storage->length()); + ASSERT(i < storage->m_numValuesInVector); + storage->m_vector[i].setWithoutWriteBarrier(v); + break; + } + default: + RELEASE_ASSERT_NOT_REACHED(); + } + } + bool hasSparseMap() { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -429,7 +535,7 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->m_sparseMap; + return !!m_butterfly.get()->arrayStorage()->m_sparseMap; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -438,7 +544,7 @@ public: bool inSparseIndexingMode() { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -446,7 +552,7 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->inSparseMode(); + return m_butterfly.get()->arrayStorage()->inSparseMode(); default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -460,65 +566,79 @@ public: // - 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&); + bool putDirect(VM&, PropertyName, JSValue, unsigned attributes = 0); + bool 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); + bool putDirectNonIndexAccessor(VM&, PropertyName, JSValue, unsigned attributes); + bool putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes); + JS_EXPORT_PRIVATE bool putDirectCustomAccessor(VM&, PropertyName, JSValue, unsigned attributes); + + bool putGetter(ExecState*, PropertyName, JSValue, unsigned attributes); + bool putSetter(ExecState*, PropertyName, JSValue, unsigned attributes); JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const; JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; + bool hasPropertyGeneric(ExecState*, PropertyName, PropertySlot::InternalMethodType) const; + bool hasPropertyGeneric(ExecState*, unsigned propertyName, PropertySlot::InternalMethodType) const; + bool hasOwnProperty(ExecState*, PropertyName, PropertySlot&) const; bool hasOwnProperty(ExecState*, PropertyName) const; + bool hasOwnProperty(ExecState*, unsigned) const; JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName); JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); + JSValue ordinaryToPrimitive(ExecState*, PreferredPrimitiveType) const; - bool hasInstance(ExecState*, JSValue); + JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue); + JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue); static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); 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); - JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) 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); + + JS_EXPORT_PRIVATE 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; JS_EXPORT_PRIVATE static JSValue toThis(JSCell*, ExecState*, ECMAMode); - bool getPropertySpecificValue(ExecState*, PropertyName, JSCell*& specificFunction) const; - // This get function only looks at the property map. JSValue getDirect(VM& vm, PropertyName propertyName) const { - PropertyOffset offset = structure()->get(vm, propertyName); - checkOffset(offset, structure()->inlineCapacity()); + 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 { - JSCell* specific; - PropertyOffset offset = structure()->get(vm, propertyName, attributes, specific); - checkOffset(offset, structure()->inlineCapacity()); + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); return offset != invalidOffset ? getDirect(offset) : JSValue(); } PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) { - PropertyOffset offset = structure()->get(vm, propertyName); - checkOffset(offset, structure()->inlineCapacity()); + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName); + checkOffset(offset, structure->inlineCapacity()); return offset; } PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) { - JSCell* specific; - PropertyOffset offset = structure()->get(vm, propertyName, attributes, specific); - checkOffset(offset, structure()->inlineCapacity()); + Structure* structure = this->structure(vm); + PropertyOffset offset = structure->get(vm, propertyName, attributes); + checkOffset(offset, structure->inlineCapacity()); return offset; } @@ -545,8 +665,8 @@ public: 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(); } + ConstPropertyStorage outOfLineStorage() const { return m_butterfly.get()->propertyStorage(); } + PropertyStorage outOfLineStorage() { return m_butterfly.get()->propertyStorage(); } const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const { @@ -564,61 +684,88 @@ public: void transitionTo(VM&, Structure*); - 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 JSActivation. + // This is used by JSLexicalEnvironment. bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); + bool putOwnDataPropertyMayBeIndex(ExecState*, 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 putDirectWithoutBarrier(PropertyOffset offset, JSValue value) { locationForOffset(offset)->setWithoutWriteBarrier(value); } void putDirectUndefined(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); } - void putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); - void putDirectNativeFunctionWithoutTransition(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + JS_EXPORT_PRIVATE bool putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes); + JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, unsigned attributes); + JS_EXPORT_PRIVATE bool putDirectNativeFunction(VM&, JSGlobalObject*, const PropertyName&, unsigned functionLength, NativeFunction, Intrinsic, const DOMJIT::Signature*, 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 isEnvironmentRecord() const; bool isGlobalObject() const; - bool isVariableObject() const; - bool isStaticScopeObject() const; - bool isNameScopeObject() const; - bool isActivationObject() const; + bool isJSLexicalEnvironment() const; + bool isGlobalLexicalEnvironment() const; + bool isStrictEvalActivation() const; + bool isWithScope() const; + bool isErrorInstance() const; - void seal(VM&); - void freeze(VM&); - JS_EXPORT_PRIVATE void preventExtensions(VM&); - bool isSealed(VM& vm) { return structure()->isSealed(vm); } - bool isFrozen(VM& vm) { return structure()->isFrozen(vm); } - bool isExtensible() { return structure()->isExtensible(); } + JS_EXPORT_PRIVATE void seal(VM&); + JS_EXPORT_PRIVATE void freeze(VM&); + JS_EXPORT_PRIVATE static bool preventExtensions(JSObject*, ExecState*); + JS_EXPORT_PRIVATE static bool isExtensible(JSObject*, ExecState*); + bool isSealed(VM& vm) { return structure(vm)->isSealed(vm); } + bool isFrozen(VM& vm) { return structure(vm)->isFrozen(vm); } +private: + ALWAYS_INLINE bool isExtensibleImpl() { return isStructureExtensible(); } +public: + // You should only call isStructureExtensible() when: + // - Performing this check in a way that isn't described in the specification + // as calling the virtual [[IsExtensible]] trap. + // - When you're guaranteed that object->methodTable()->isExtensible isn't + // overridden. + ALWAYS_INLINE bool isStructureExtensible() { return structure()->isStructureExtensible(); } + // You should call this when performing [[IsExtensible]] trap in a place + // that is described in the specification. This performs the fully virtual + // [[IsExtensible]] trap. + bool isExtensible(ExecState*); bool indexingShouldBeSparse() { - return !isExtensible() + return !isStructureExtensible() || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero(); } - bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } - void reifyStaticFunctionsForDelete(ExecState* exec); + bool staticPropertiesReified() { return structure()->staticPropertiesReified(); } + void reifyAllStaticProperties(ExecState*); - JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize); - void setButterflyWithoutChangingStructure(VM&, Butterfly*); - + JS_EXPORT_PRIVATE Butterfly* allocateMoreOutOfLineStorage(VM&, size_t oldSize, size_t newSize); + + // Call this when you do not need to change the structure. + void setButterfly(VM&, Butterfly*); + + // Call this if you do need to change the structure, or if you changed something about a structure + // in-place. + void nukeStructureAndSetButterfly(VM&, StructureID, Butterfly*); + void setStructure(VM&, Structure*); - void setStructureAndButterfly(VM&, Structure*, Butterfly*); - void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*); - void setStructureAndReallocateStorageIfNecessary(VM&, Structure*); + + JS_EXPORT_PRIVATE void convertToDictionary(VM&); void flattenDictionaryObject(VM& vm) { - structure()->flattenDictionaryStructure(vm, this); + structure(vm)->flattenDictionaryStructure(vm, this); } + void shiftButterflyAfterFlattening(const GCSafeConcurrentJSLocker&, VM&, Structure* structure, size_t outOfLineCapacityAfter); JSGlobalObject* globalObject() const { @@ -627,6 +774,13 @@ public: return structure()->globalObject(); } + JSGlobalObject* globalObject(VM& vm) const + { + ASSERT(structure(vm)->globalObject()); + ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); + return structure(vm)->globalObject(); + } + void switchToSlowPutArrayStorage(VM&); // The receiver is the prototype in this case. The following: @@ -636,7 +790,7 @@ public: // is equivalent to: // // foo->attemptToInterceptPutByIndexOnHole(...); - bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow); + bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow, bool& putResult); // Returns 0 if int32 storage cannot be created - either because // indexing should be sparse, we're having a bad time, or because @@ -644,8 +798,8 @@ public: // contiguous, array storage). ContiguousJSValues ensureInt32(VM& vm) { - if (LIKELY(hasInt32(structure()->indexingType()))) - return m_butterfly->contiguousInt32(); + if (LIKELY(hasInt32(indexingType()))) + return m_butterfly.get()->contiguousInt32(); return ensureInt32Slow(vm); } @@ -656,8 +810,8 @@ public: // or array storage). ContiguousDoubles ensureDouble(VM& vm) { - if (LIKELY(hasDouble(structure()->indexingType()))) - return m_butterfly->contiguousDouble(); + if (LIKELY(hasDouble(indexingType()))) + return m_butterfly.get()->contiguousDouble(); return ensureDoubleSlow(vm); } @@ -666,32 +820,21 @@ public: // indexing should be sparse or because we're having a bad time. ContiguousJSValues ensureContiguous(VM& vm) { - if (LIKELY(hasContiguous(structure()->indexingType()))) - return m_butterfly->contiguous(); + if (LIKELY(hasContiguous(indexingType()))) + return m_butterfly.get()->contiguous(); return ensureContiguousSlow(vm); } - - // Same as ensureContiguous(), except that if the indexed storage is in - // double mode, then it does a rage conversion to contiguous: it - // attempts to convert each double to an int32. - ContiguousJSValues rageEnsureContiguous(VM& vm) - { - if (LIKELY(hasContiguous(structure()->indexingType()))) - return m_butterfly->contiguous(); - - return rageEnsureContiguousSlow(vm); - } - + // 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(hasArrayStorage(structure()->indexingType()))) - return m_butterfly->arrayStorage(); - + if (LIKELY(hasAnyArrayStorage(indexingType()))) + return m_butterfly.get()->arrayStorage(); + return ensureArrayStorageSlow(vm); } @@ -707,18 +850,18 @@ public: return &m_butterfly; } + JS_EXPORT_PRIVATE JSValue getMethod(ExecState*, CallData&, CallType&, const Identifier&, const String& errorMessage); + DECLARE_EXPORT_INFO; protected: void finishCreation(VM& vm) { Base::finishCreation(vm); - ASSERT(inherits(info())); - ASSERT(!structure()->outOfLineCapacity()); - ASSERT(structure()->isEmpty()); - ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT(inherits(vm, info())); + ASSERT(getPrototypeDirect().isNull() || Heap::heap(this) == Heap::heap(getPrototypeDirect())); ASSERT(structure()->isObject()); - ASSERT(classInfo()); + ASSERT(classInfo(vm)); } static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) @@ -729,31 +872,38 @@ protected: // 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); + + // Visits the butterfly unless there is a race. Returns the structure if there was no race. + Structure* visitButterfly(SlotVisitor&); + + Structure* visitButterflyImpl(SlotVisitor&); + + void markAuxiliaryAndVisitOutOfLineProperties(SlotVisitor&, Butterfly*, Structure*, PropertyOffset lastOffset); // Call this if you know that the object is in a mode where it has array // storage. This will assert otherwise. ArrayStorage* arrayStorage() { - ASSERT(hasArrayStorage(structure()->indexingType())); - return m_butterfly->arrayStorage(); + ASSERT(hasAnyArrayStorage(indexingType())); + return m_butterfly.get()->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 (structure()->indexingType()) { + switch (indexingType()) { case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage(); + return m_butterfly.get()->arrayStorage(); default: return 0; } } + size_t butterflyTotalSize(); + size_t butterflyPreCapacity(); + Butterfly* createInitialUndecided(VM&, unsigned length); ContiguousJSValues createInitialInt32(VM&, unsigned length); ContiguousDoubles createInitialDouble(VM&, unsigned length); @@ -763,29 +913,25 @@ protected: void createInitialForValueAndSet(VM&, unsigned index, JSValue); void convertInt32ForValue(VM&, JSValue); + static Butterfly* createArrayStorageButterfly(VM&, JSCell* intendedOwner, Structure*, unsigned length, unsigned vectorLength, Butterfly* oldButterfly = nullptr); ArrayStorage* createArrayStorage(VM&, unsigned length, unsigned vectorLength); ArrayStorage* createInitialArrayStorage(VM&); ContiguousJSValues convertUndecidedToInt32(VM&); ContiguousDoubles convertUndecidedToDouble(VM&); ContiguousJSValues convertUndecidedToContiguous(VM&); - ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertUndecidedToArrayStorage(VM&, NonPropertyTransition); ArrayStorage* convertUndecidedToArrayStorage(VM&); ContiguousDoubles convertInt32ToDouble(VM&); ContiguousJSValues convertInt32ToContiguous(VM&); - ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertInt32ToArrayStorage(VM&, NonPropertyTransition); ArrayStorage* convertInt32ToArrayStorage(VM&); ContiguousJSValues convertDoubleToContiguous(VM&); - ContiguousJSValues rageConvertDoubleToContiguous(VM&); - ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertDoubleToArrayStorage(VM&, NonPropertyTransition); ArrayStorage* convertDoubleToArrayStorage(VM&); - ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition, unsigned neededLength); ArrayStorage* convertContiguousToArrayStorage(VM&, NonPropertyTransition); ArrayStorage* convertContiguousToArrayStorage(VM&); @@ -795,8 +941,8 @@ protected: bool defineOwnNonIndexProperty(ExecState*, PropertyName, const PropertyDescriptor&, bool throwException); template<IndexingType indexingShape> - void putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); - void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); + bool putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned propertyName, JSValue); + bool putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*); bool increaseVectorLength(VM&, unsigned newLength); void deallocateSparseIndexMap(); @@ -805,125 +951,35 @@ protected: void notifyPresenceOfIndexedAccessors(VM&); - bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow); + bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow, bool& putResult); // Call this if you want setIndexQuickly to succeed and you're sure that // the array is contiguous. - void ensureLength(VM& vm, unsigned length) + bool WARN_UNUSED_RETURN ensureLength(VM& vm, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); - - if (m_butterfly->vectorLength() < length) - ensureLengthSlow(vm, length); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); + + if (m_butterfly.get()->vectorLength() < length) { + if (!ensureLengthSlow(vm, length)) + return false; + } - if (m_butterfly->publicLength() < length) - m_butterfly->setPublicLength(length); + if (m_butterfly.get()->publicLength() < length) + m_butterfly.get()->setPublicLength(length); + return true; } + // 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<IndexingType indexingShape> unsigned countElements(Butterfly*); // This is relevant to undecided, int32, double, and contiguous. unsigned countElements(); - // This strange method returns a pointer to the start of the indexed data - // as if it contained JSValues. But it won't always contain JSValues. - // Make sure you cast this to the appropriate type before using. - template<IndexingType indexingType> - ContiguousJSValues indexingData() - { - switch (indexingType) { - case ALL_INT32_INDEXING_TYPES: - case ALL_DOUBLE_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->contiguous(); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->vector(); - - default: - CRASH(); - return ContiguousJSValues(); - } - } - - ContiguousJSValues currentIndexingData() - { - switch (structure()->indexingType()) { - case ALL_INT32_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->contiguous(); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->vector(); - - default: - CRASH(); - return ContiguousJSValues(); - } - } - - JSValue getHolyIndexQuickly(unsigned i) - { - ASSERT(i < m_butterfly->vectorLength()); - switch (structure()->indexingType()) { - case ALL_INT32_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->contiguous()[i].get(); - case ALL_DOUBLE_INDEXING_TYPES: { - double value = m_butterfly->contiguousDouble()[i]; - if (value == value) - return JSValue(JSValue::EncodeAsDouble, value); - return JSValue(); - } - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly->arrayStorage()->m_vector[i].get(); - default: - CRASH(); - return JSValue(); - } - } - - template<IndexingType indexingType> - unsigned relevantLength() - { - switch (indexingType) { - case ALL_INT32_INDEXING_TYPES: - case ALL_DOUBLE_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->publicLength(); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return std::min( - m_butterfly->arrayStorage()->length(), - m_butterfly->arrayStorage()->vectorLength()); - - default: - CRASH(); - return 0; - } - } - - unsigned currentRelevantLength() - { - switch (structure()->indexingType()) { - case ALL_INT32_INDEXING_TYPES: - case ALL_DOUBLE_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: - return m_butterfly->publicLength(); - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return std::min( - m_butterfly->arrayStorage()->length(), - m_butterfly->arrayStorage()->vectorLength()); - - default: - CRASH(); - return 0; - } - } - private: friend class LLIntOffsetsExtractor; @@ -935,50 +991,54 @@ private: void isObject(); void isString(); - Butterfly* createInitialIndexedStorage(VM&, unsigned length, size_t elementSize); + Butterfly* createInitialIndexedStorage(VM&, unsigned length); ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); template<PutMode> - bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*); + bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&); + bool canPerformFastPutInline(ExecState* exec, VM&, PropertyName); + + JS_EXPORT_PRIVATE NEVER_INLINE bool putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&); - bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&); + bool getNonIndexPropertySlot(ExecState*, PropertyName, PropertySlot&); + bool getOwnNonIndexPropertySlot(VM&, Structure*, PropertyName, PropertySlot&); JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset); + void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure*); - const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const; + JS_EXPORT_PRIVATE bool getOwnStaticPropertySlot(VM&, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE const HashTableValue* findPropertyHashEntry(VM&, PropertyName) const; - void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); + bool putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); - void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + bool 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 indexBias, unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength); unsigned getNewVectorLength(unsigned desiredLength); - JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&); - 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); + bool ensureLengthSlow(VM&, unsigned length); ContiguousJSValues ensureInt32Slow(VM&); ContiguousDoubles ensureDoubleSlow(VM&); ContiguousJSValues ensureContiguousSlow(VM&); - ContiguousJSValues rageEnsureContiguousSlow(VM&); - ArrayStorage* ensureArrayStorageSlow(VM&); - - enum DoubleToContiguousMode { EncodeValueAsDouble, RageConvertDoubleToValue }; - template<DoubleToContiguousMode mode> - ContiguousJSValues genericConvertDoubleToContiguous(VM&); - ContiguousJSValues ensureContiguousSlow(VM&, DoubleToContiguousMode); - + JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&); + + PropertyOffset prepareToPutDirectWithoutTransition(VM&, PropertyName, unsigned attributes, StructureID, Structure*); + protected: - CopyWriteBarrier<Butterfly> m_butterfly; + AuxiliaryBarrier<Butterfly*> m_butterfly; +#if USE(JSVALUE32_64) +private: + uint32_t m_padding; +#endif }; // JSNonFinalObject is a type of JSObject that has some internal storage, @@ -1004,8 +1064,8 @@ protected: void finishCreation(VM& vm) { Base::finishCreation(vm); - ASSERT(!this->structure()->totalStorageCapacity()); - ASSERT(classInfo()); + ASSERT(!this->structure()->hasInlineStorage()); + ASSERT(classInfo(vm)); } }; @@ -1018,11 +1078,15 @@ class JSFinalObject : public JSObject { public: typedef JSObject Base; + static const unsigned StructureFlags = Base::StructureFlags; static size_t allocationSize(size_t inlineCapacity) { return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>); } + + static inline const TypeInfo typeInfo() { return TypeInfo(FinalObjectType, StructureFlags); } + static const IndexingType defaultIndexingType = NonArray; static const unsigned defaultSize = 64; static inline unsigned defaultInlineCapacity() @@ -1036,11 +1100,11 @@ public: return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); } - static JSFinalObject* create(ExecState*, Structure*); + 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); + return Structure::create(vm, globalObject, prototype, typeInfo(), info(), defaultIndexingType, inlineCapacity); } JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); @@ -1054,21 +1118,37 @@ protected: { Base::finishCreation(vm); ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity()); - ASSERT(classInfo()); + ASSERT(classInfo(vm)); } private: friend class LLIntOffsetsExtractor; - explicit JSFinalObject(VM& vm, Structure* structure) - : JSObject(vm, structure) + explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr) + : JSObject(vm, structure, butterfly) { + memset(inlineStorageUnsafe(), 0, structure->inlineCapacity() * sizeof(EncodedJSValue)); } - - static const unsigned StructureFlags = JSObject::StructureFlags; }; -inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure) +JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*); + +inline JSObject* JSObject::createRawObject( + ExecState* exec, Structure* structure, Butterfly* butterfly) +{ + JSObject* finalObject = new ( + NotNull, + allocateCell<JSFinalObject>( + *exec->heap(), + JSFinalObject::allocationSize(structure->inlineCapacity()) + ) + ) JSObject(exec->vm(), structure, butterfly); + finalObject->finishCreation(exec->vm()); + return finalObject; +} + +inline JSFinalObject* JSFinalObject::create( + ExecState* exec, Structure* structure, Butterfly* butterfly) { JSFinalObject* finalObject = new ( NotNull, @@ -1076,7 +1156,7 @@ inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structur *exec->heap(), allocationSize(structure->inlineCapacity()) ) - ) JSFinalObject(exec->vm(), structure); + ) JSFinalObject(exec->vm(), structure, butterfly); finalObject->finishCreation(exec->vm()); return finalObject; } @@ -1090,7 +1170,7 @@ inline JSFinalObject* JSFinalObject::create(VM& vm, Structure* structure) inline bool isJSFinalObject(JSCell* cell) { - return cell->classInfo() == JSFinalObject::info(); + return cell->type() == FinalObjectType; } inline bool isJSFinalObject(JSValue value) @@ -1105,43 +1185,39 @@ inline size_t JSObject::offsetOfInlineStorage() inline bool JSObject::isGlobalObject() const { - return structure()->typeInfo().type() == GlobalObjectType; + return type() == GlobalObjectType; } -inline bool JSObject::isVariableObject() const +inline bool JSObject::isJSLexicalEnvironment() const { - return structure()->typeInfo().type() >= VariableObjectType; + return type() == LexicalEnvironmentType || type() == ModuleEnvironmentType; } - -inline bool JSObject::isStaticScopeObject() const +inline bool JSObject::isGlobalLexicalEnvironment() const { - JSType type = structure()->typeInfo().type(); - return type == NameScopeObjectType || type == ActivationObjectType; + return type() == GlobalLexicalEnvironmentType; } - -inline bool JSObject::isNameScopeObject() const +inline bool JSObject::isStrictEvalActivation() const { - return structure()->typeInfo().type() == NameScopeObjectType; + return type() == StrictEvalActivationType; } -inline bool JSObject::isActivationObject() const +inline bool JSObject::isEnvironmentRecord() const { - return structure()->typeInfo().type() == ActivationObjectType; + bool result = GlobalObjectType <= type() && type() <= StrictEvalActivationType; + ASSERT((isGlobalObject() || isJSLexicalEnvironment() || isGlobalLexicalEnvironment() || isStrictEvalActivation()) == result); + return result; } inline bool JSObject::isErrorInstance() const { - return structure()->typeInfo().type() == ErrorInstanceType; + return type() == ErrorInstanceType; } -inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly) +inline bool JSObject::isWithScope() const { - ASSERT(structure); - ASSERT(!butterfly == (!structure->outOfLineCapacity() && !structure->hasIndexingHeader(this))); - m_butterfly.set(vm, this, butterfly); - setStructure(vm, structure); + return type() == WithScopeType; } inline void JSObject::setStructure(VM& vm, Structure* structure) @@ -1151,22 +1227,42 @@ inline void JSObject::setStructure(VM& vm, Structure* structure) JSCell::setStructure(vm, structure); } -inline void JSObject::setButterflyWithoutChangingStructure(VM& vm, Butterfly* butterfly) +inline void JSObject::setButterfly(VM& vm, Butterfly* butterfly) { + if (isX86() || vm.heap.mutatorShouldBeFenced()) { + WTF::storeStoreFence(); + m_butterfly.set(vm, this, butterfly); + WTF::storeStoreFence(); + return; + } + + m_butterfly.set(vm, this, butterfly); +} + +inline void JSObject::nukeStructureAndSetButterfly(VM& vm, StructureID oldStructureID, Butterfly* butterfly) +{ + if (isX86() || vm.heap.mutatorShouldBeFenced()) { + setStructureIDDirectly(nuke(oldStructureID)); + WTF::storeStoreFence(); + m_butterfly.set(vm, this, butterfly); + WTF::storeStoreFence(); + return; + } + 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()); + CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallType::None; + ASSERT(result == CallType::None || 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()); + ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructType::None; + ASSERT(result == ConstructType::None || value.isValidCallee()); return result; } @@ -1185,29 +1281,68 @@ 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 +inline JSValue JSObject::getPrototypeDirect() const { return structure()->storedPrototype(); } -ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) +inline JSValue JSObject::getPrototype(VM& vm, ExecState* exec) +{ + auto getPrototypeMethod = methodTable(vm)->getPrototype; + MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype; + if (LIKELY(getPrototypeMethod == defaultGetPrototype)) + return getPrototypeDirect(); + return getPrototypeMethod(this, exec); +} + +// It is safe to call this method with a PropertyName that is actually an index, +// but if so will always return false (doesn't search index storage). +ALWAYS_INLINE bool JSObject::getOwnNonIndexPropertySlot(VM& vm, Structure* structure, PropertyName propertyName, PropertySlot& slot) { unsigned attributes; - JSCell* specific; - PropertyOffset offset = structure()->get(exec->vm(), propertyName, attributes, specific); - if (LIKELY(isValidOffset(offset))) { - JSValue value = getDirect(offset); - if (structure()->hasGetterSetterProperties() && value.isGetterSetter()) + PropertyOffset offset = structure->get(vm, propertyName, attributes); + if (!isValidOffset(offset)) { + if (!TypeInfo::hasStaticPropertyTable(inlineTypeFlags())) + return false; + return getOwnStaticPropertySlot(vm, propertyName, slot); + } + + // getPropertySlot relies on this method never returning index properties! + ASSERT(!parseIndex(propertyName)); + + JSValue value = getDirect(offset); + if (value.isCell()) { + ASSERT(value); + JSCell* cell = value.asCell(); + JSType type = cell->type(); + switch (type) { + case GetterSetterType: fillGetterPropertySlot(slot, value, attributes, offset); - else - slot.setValue(this, attributes, value, offset); - return true; + return true; + case CustomGetterSetterType: + fillCustomGetterPropertySlot(slot, value, attributes, structure); + return true; + default: + break; + } + } + + slot.setValue(this, attributes, value, offset); + return true; +} + +ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JSValue customGetterSetter, unsigned attributes, Structure* structure) +{ + if (structure->isUncacheableDictionary()) { + slot.setCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); + return; } - return getOwnPropertySlotSlow(exec, propertyName, slot); + // This access is cacheable because Structure requires an attributeChangedTransition + // if this property stops being an accessor. + slot.setCacheableCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter(), jsCast<CustomGetterSetter*>(customGetterSetter)->domJIT()); } // It may seem crazy to inline a function this large, especially a virtual function, @@ -1215,47 +1350,52 @@ ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyN // base class call to this. ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - return object->inlineGetOwnPropertySlot(exec, propertyName, slot); -} - -ALWAYS_INLINE bool JSObject::fastGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) -{ - if (!structure()->typeInfo().overridesGetOwnPropertySlot()) - return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); - return methodTable()->getOwnPropertySlot(this, exec, propertyName, slot); + VM& vm = exec->vm(); + Structure* structure = object->structure(vm); + if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) + return true; + if (std::optional<uint32_t> index = parseIndex(propertyName)) + return getOwnPropertySlotByIndex(object, exec, index.value(), slot); + return false; } // 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, PropertyName propertyName, PropertySlot& slot) { + VM& vm = exec->vm(); + auto& structureIDTable = vm.heap.structureIDTable(); JSObject* object = this; while (true) { - if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) + if (UNLIKELY(TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) { + // If propertyName is an index then we may have missed it (as this loop is using + // getOwnNonIndexPropertySlot), so we cannot safely call the overridden getOwnPropertySlot + // (lest we return a property from a prototype that is shadowed). Check now for an index, + // if so we need to start afresh from this object. + if (std::optional<uint32_t> index = parseIndex(propertyName)) + return getPropertySlot(exec, index.value(), slot); + // Safe to continue searching from current position; call getNonIndexPropertySlot to avoid + // parsing the int again. + return object->getNonIndexPropertySlot(exec, propertyName, slot); + } + ASSERT(object->type() != ProxyObjectType); + Structure* structure = structureIDTable.get(object->structureID()); + if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) return true; - JSValue prototype = object->prototype(); + JSValue prototype = structure->storedPrototype(); if (!prototype.isObject()) - return false; + break; object = asObject(prototype); } -} -ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) -{ - JSObject* object = this; - while (true) { - if (object->methodTable()->getOwnPropertySlotByIndex(object, exec, propertyName, slot)) - return true; - JSValue prototype = object->prototype(); - if (!prototype.isObject()) - return false; - object = asObject(prototype); - } + if (std::optional<uint32_t> index = parseIndex(propertyName)) + return getPropertySlot(exec, index.value(), slot); + return false; } inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot(this); + PropertySlot slot(this, PropertySlot::InternalMethodType::Get); if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); @@ -1264,210 +1404,60 @@ inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const { - PropertySlot slot(this); + PropertySlot slot(this, PropertySlot::InternalMethodType::Get); if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); return jsUndefined(); } -template<JSObject::PutMode mode> -inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot, JSCell* specificFunction) +inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); - ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex); - - if (structure()->isDictionary()) { - unsigned currentAttributes; - JSCell* currentSpecificFunction; - PropertyOffset offset = structure()->get(vm, propertyName, currentAttributes, currentSpecificFunction); - if (offset != invalidOffset) { - // If there is currently a specific function, and there now either isn't, - // or the new value is different, then despecify. - if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) - structure()->despecifyDictionaryFunction(vm, propertyName); - if ((mode == PutModePut) && currentAttributes & ReadOnly) - return false; - - putDirect(vm, offset, value); - // At this point, the objects structure only has a specific value set if previously there - // had been one set, and if the new value being specified is the same (otherwise we would - // have despecified, above). So, if currentSpecificFunction is not set, or if the new - // value is different (or there is no new value), then the slot now has no value - and - // as such it is cachable. - // If there was previously a value, and the new value is the same, then we cannot cache. - if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) - slot.setExistingProperty(this, offset); - return true; - } - - if ((mode == PutModePut) && !isExtensible()) - return false; - - DeferGC deferGC(vm.heap); - Butterfly* newButterfly = butterfly(); - if (structure()->putWillGrowOutOfLineStorage()) - newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); - offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes, specificFunction); - setStructureAndButterfly(vm, structure(), newButterfly); - - validateOffset(offset); - ASSERT(structure()->isValidOffset(offset)); - putDirect(vm, offset, value); - // See comment on setNewProperty call below. - if (!specificFunction) - slot.setNewProperty(this, offset); - if (attributes & ReadOnly) - structure()->setContainsReadOnlyProperties(); - return true; - } - - PropertyOffset offset; - size_t currentCapacity = structure()->outOfLineCapacity(); - if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { - DeferGC deferGC(vm.heap); - Butterfly* newButterfly = butterfly(); - if (currentCapacity != structure->outOfLineCapacity()) { - ASSERT(structure != this->structure()); - newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); - } - - validateOffset(offset); - ASSERT(structure->isValidOffset(offset)); - setStructureAndButterfly(vm, structure, newButterfly); - putDirect(vm, offset, value); - // This is a new property; transitions with specific values are not currently cachable, - // so leave the slot in an uncachable state. - if (!specificFunction) - slot.setNewProperty(this, offset); - return true; - } - - unsigned currentAttributes; - JSCell* currentSpecificFunction; - offset = structure()->get(vm, propertyName, currentAttributes, currentSpecificFunction); - if (offset != invalidOffset) { - if ((mode == PutModePut) && currentAttributes & ReadOnly) - return false; - - // There are three possibilities here: - // (1) There is an existing specific value set, and we're overwriting with *the same value*. - // * Do nothing - no need to despecify, but that means we can't cache (a cached - // put could write a different value). Leave the slot in an uncachable state. - // (2) There is a specific value currently set, but we're writing a different value. - // * First, we have to despecify. Having done so, this is now a regular slot - // with no specific value, so go ahead & cache like normal. - // (3) Normal case, there is no specific value set. - // * Go ahead & cache like normal. - if (currentSpecificFunction) { - // case (1) Do the put, then return leaving the slot uncachable. - if (specificFunction == currentSpecificFunction) { - putDirect(vm, offset, value); - return true; - } - // case (2) Despecify, fall through to (3). - setStructure(vm, Structure::despecifyFunctionTransition(vm, structure(), propertyName)); - } - - // case (3) set the slot, do the put, return. - slot.setExistingProperty(this, offset); - putDirect(vm, offset, value); - return true; - } - - if ((mode == PutModePut) && !isExtensible()) - return false; - - Structure* structure = Structure::addPropertyTransition(vm, this->structure(), propertyName, attributes, specificFunction, offset, slot.context()); - - validateOffset(offset); - ASSERT(structure->isValidOffset(offset)); - setStructureAndReallocateStorageIfNecessary(vm, structure); - - putDirect(vm, offset, value); - // This is a new property; transitions with specific values are not currently cachable, - // so leave the slot in an uncachable state. - if (!specificFunction) - slot.setNewProperty(this, offset); - if (attributes & ReadOnly) - structure->setContainsReadOnlyProperties(); - return true; -} - -inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsigned oldCapacity, Structure* newStructure) -{ - ASSERT(oldCapacity <= newStructure->outOfLineCapacity()); - - if (oldCapacity == newStructure->outOfLineCapacity()) { - setStructure(vm, newStructure); - return; - } + ASSERT(!structure()->hasGetterSetterProperties()); + ASSERT(!structure()->hasCustomGetterSetterProperties()); - DeferGC deferGC(vm.heap); - Butterfly* newButterfly = growOutOfLineStorage( - vm, oldCapacity, newStructure->outOfLineCapacity()); - setStructureAndButterfly(vm, newStructure, newButterfly); + return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot); } -inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure) -{ - setStructureAndReallocateStorageIfNecessary( - vm, structure()->outOfLineCapacity(), newStructure); -} - -inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +inline bool JSObject::putOwnDataPropertyMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); ASSERT(!structure()->hasGetterSetterProperties()); + ASSERT(!structure()->hasCustomGetterSetterProperties()); + + if (std::optional<uint32_t> index = parseIndex(propertyName)) + return putDirectIndex(exec, index.value(), value, 0, PutDirectIndexLikePutDirect); - return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)); + return putDirectInternal<PutModePut>(exec->vm(), propertyName, value, 0, slot); } -inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) { ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + ASSERT(!value.isCustomGetterSetter()); PutPropertySlot slot(this); - putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value)); + return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot); } -inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +inline bool JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(!value.isGetterSetter()); - putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot, getCallableObject(value)); -} - -inline void JSObject::putDirectWithoutTransition(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) -{ - DeferGC deferGC(vm.heap); - ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); - Butterfly* newButterfly = m_butterfly.get(); - if (structure()->putWillGrowOutOfLineStorage()) - newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); - PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes, getCallableObject(value)); - setStructureAndButterfly(vm, structure(), newButterfly); - putDirect(vm, offset, value); -} - -inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const -{ - return methodTable()->defaultValue(this, exec, preferredType); + ASSERT(!value.isCustomGetterSetter()); + return putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot); } -ALWAYS_INLINE JSObject* Register::function() const +ALWAYS_INLINE JSObject* Register::object() const { - if (!jsValue()) - return 0; return asObject(jsValue()); } -ALWAYS_INLINE Register Register::withCallee(JSObject* callee) +ALWAYS_INLINE Register& Register::operator=(JSObject* object) { - Register r; - r = JSValue(callee); - return r; + u.value = JSValue::encode(JSValue(object)); + return *this; } inline size_t offsetInButterfly(PropertyOffset offset) @@ -1475,26 +1465,30 @@ inline size_t offsetInButterfly(PropertyOffset offset) return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); } -// 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) +inline size_t JSObject::butterflyPreCapacity() { - if (isOutOfLineOffset(offset)) - return sizeof(EncodedJSValue) * offsetInButterfly(offset); - return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); + if (UNLIKELY(hasIndexingHeader())) + return butterfly()->indexingHeader()->preCapacity(structure()); + return 0; } -// Returns the maximum offset (away from zero) a load instruction will encode. -inline size_t maxOffsetRelativeToPatchedStorage(PropertyOffset offset) +inline size_t JSObject::butterflyTotalSize() { - ptrdiff_t addressOffset = static_cast<ptrdiff_t>(offsetRelativeToPatchedStorage(offset)); -#if USE(JSVALUE32_64) - if (addressOffset >= 0) - return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); -#endif - return static_cast<size_t>(addressOffset); + 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; + } + + return Butterfly::totalSize(preCapacity, structure->outOfLineCapacity(), hasIndexingHeader, indexingPayloadSizeInBytes); } inline int indexRelativeToBase(PropertyOffset offset) @@ -1512,11 +1506,28 @@ inline int offsetRelativeToBase(PropertyOffset offset) return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue); } +// Returns the maximum offset (away from zero) a load instruction will encode. +inline size_t maxOffsetRelativeToBase(PropertyOffset offset) +{ + ptrdiff_t addressOffset = offsetRelativeToBase(offset); +#if USE(JSVALUE32_64) + if (addressOffset >= 0) + return static_cast<size_t>(addressOffset) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag); +#endif + return static_cast<size_t>(addressOffset); +} + COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment); +template<unsigned charactersCount> +ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char (&characters)[charactersCount]) +{ + return Identifier::fromString(&vm, characters); +} + ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) { - return Identifier(&vm, name); + return Identifier::fromString(&vm, name); } ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) @@ -1524,6 +1535,11 @@ ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) return name; } +bool validateAndApplyPropertyDescriptor(ExecState*, JSObject*, PropertyName, bool isExtensible, + const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException); + +JS_EXPORT_PRIVATE NEVER_INLINE bool ordinarySetSlow(ExecState*, JSObject*, PropertyName, JSValue, JSValue receiver, bool shouldThrow); + // 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 @@ -1533,33 +1549,37 @@ ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) vm, globalObject, makeIdentifier(vm, (jsName)), (length), cppName, \ (intrinsic), (attributes)) +#define JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length, intrinsic) \ + putDirectNativeFunctionWithoutTransition(\ + 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) -ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, PropertyName propertyName) const -{ - if (m_propertyType == TypeValue) - return JSValue::decode(m_data.value); - if (m_propertyType == TypeCustomIndex) - return JSValue::decode(m_data.customIndex.getIndexValue(exec, JSValue::encode(slotBase()), JSValue::encode(m_thisValue), m_data.customIndex.index)); - if (m_propertyType == TypeGetter) - return functionGetter(exec); - return JSValue::decode(m_data.custom.getValue(exec, JSValue::encode(slotBase()), JSValue::encode(m_thisValue), propertyName)); -} +#define JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length) \ + JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, (attributes), (length), NoIntrinsic) -ALWAYS_INLINE JSValue PropertySlot::getValue(ExecState* exec, unsigned propertyName) const -{ - if (m_propertyType == TypeValue) - return JSValue::decode(m_data.value); - if (m_propertyType == TypeCustomIndex) - return JSValue::decode(m_data.customIndex.getIndexValue(exec, JSValue::encode(slotBase()), JSValue::encode(m_thisValue), m_data.customIndex.index)); - if (m_propertyType == TypeGetter) - return functionGetter(exec); - return JSValue::decode(m_data.custom.getValue(exec, JSValue::encode(slotBase()), JSValue::encode(m_thisValue), Identifier::from(exec, propertyName))); -} +// 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 +#define JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(jsName, generatorName, attributes) \ + putDirectBuiltinFunctionWithoutTransition(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) -#endif // JSObject_h +// Helper for defining native getters on properties. +#define JSC_NATIVE_INTRINSIC_GETTER(jsName, cppName, attributes, intrinsic) \ + putDirectNativeIntrinsicGetter(\ + vm, globalObject, makeIdentifier(vm, (jsName)), (cppName), \ + (intrinsic), ((attributes) | Accessor)) + +#define JSC_NATIVE_GETTER(jsName, cppName, attributes) \ + JSC_NATIVE_INTRINSIC_GETTER((jsName), (cppName), (attributes), NoIntrinsic) + + +} // namespace JSC |