diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/runtime/JSObject.h | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSObject.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSObject.h | 860 |
1 files changed, 435 insertions, 425 deletions
diff --git a/Source/JavaScriptCore/runtime/JSObject.h b/Source/JavaScriptCore/runtime/JSObject.h index f62952eb3..02b137b35 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-2009, 2012-2016 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2013 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 @@ -30,11 +30,9 @@ #include "CallFrame.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" -#include "CopyBarrier.h" -#include "CustomGetterSetter.h" +#include "CopyWriteBarrier.h" #include "DeferGC.h" #include "Heap.h" -#include "HeapInlines.h" #include "IndexingHeaderInlines.h" #include "JSCell.h" #include "PropertySlot.h" @@ -52,21 +50,29 @@ namespace JSC { inline JSCell* getJSFunction(JSValue value) { - if (value.isCell() && (value.asCell()->type() == JSFunctionType)) + if (value.isCell() && (value.asCell()->structure()->typeInfo().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; struct HashTable; -struct HashTableValue; JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&); extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; @@ -75,6 +81,7 @@ COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribu 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; @@ -85,7 +92,7 @@ class JSObject : public JSCell { friend class JSCell; friend class JSFinalObject; friend class MarkedBlock; - JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject*, PropertyName, PropertySlot&); + JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject*, PropertyName, PropertySlot&); enum PutMode { PutModePut, @@ -94,17 +101,15 @@ class JSObject : public JSCell { public: typedef JSCell Base; - - 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 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); + void setPrototype(VM&, JSValue prototype); + bool setPrototypeWithCycleCheck(ExecState*, JSValue prototype); bool mayInterceptIndexedAccesses() { @@ -114,6 +119,7 @@ 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&); @@ -123,36 +129,34 @@ 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. - JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); + bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&); - JS_EXPORT_PRIVATE bool allowsAccessFrom(ExecState*); + bool allowsAccessFrom(ExecState*); unsigned getArrayLength() const { - if (!hasIndexedProperties(indexingType())) + if (!hasIndexedProperties(structure()->indexingType())) return 0; - return m_butterfly.get(this)->publicLength(); + return m_butterfly->publicLength(); } unsigned getVectorLength() { - if (!hasIndexedProperties(indexingType())) + if (!hasIndexedProperties(structure()->indexingType())) return 0; - return m_butterfly.get(this)->vectorLength(); + return m_butterfly->vectorLength(); } - - static void putInline(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&); - + 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) + 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); + methodTable()->putByIndex(this, exec, propertyName, value, shouldThrow); } // This is similar to the putDirect* methods: @@ -183,24 +187,23 @@ public: bool canGetIndexQuickly(unsigned i) { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType()) { + switch (structure()->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 < butterfly->vectorLength() && butterfly->contiguous()[i]; + return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i]; case ALL_DOUBLE_INDEXING_TYPES: { - if (i >= butterfly->vectorLength()) + if (i >= m_butterfly->vectorLength()) return false; - double value = butterfly->contiguousDouble()[i]; + double value = m_butterfly->contiguousDouble()[i]; if (value != value) return false; return true; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return i < butterfly->arrayStorage()->vectorLength() && butterfly->arrayStorage()->m_vector[i]; + return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i]; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -209,51 +212,49 @@ public: JSValue getIndexQuickly(unsigned i) { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_INT32_INDEXING_TYPES: - return jsNumber(butterfly->contiguous()[i].get().asInt32()); + return jsNumber(m_butterfly->contiguous()[i].get().asInt32()); case ALL_CONTIGUOUS_INDEXING_TYPES: - return butterfly->contiguous()[i].get(); + return m_butterfly->contiguous()[i].get(); case ALL_DOUBLE_INDEXING_TYPES: - return JSValue(JSValue::EncodeAsDouble, butterfly->contiguousDouble()[i]); + return JSValue(JSValue::EncodeAsDouble, m_butterfly->contiguousDouble()[i]); case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return butterfly->arrayStorage()->m_vector[i].get(); + return m_butterfly->arrayStorage()->m_vector[i].get(); default: RELEASE_ASSERT_NOT_REACHED(); return JSValue(); } } - JSValue tryGetIndexQuickly(unsigned i) const + JSValue tryGetIndexQuickly(unsigned i) { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: break; case ALL_INT32_INDEXING_TYPES: - if (i < butterfly->publicLength()) { - JSValue result = butterfly->contiguous()[i].get(); + 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 < butterfly->publicLength()) - return butterfly->contiguous()[i].get(); + if (i < m_butterfly->publicLength()) + return m_butterfly->contiguous()[i].get(); break; case ALL_DOUBLE_INDEXING_TYPES: { - if (i >= butterfly->publicLength()) + if (i >= m_butterfly->publicLength()) break; - double result = butterfly->contiguousDouble()[i]; + double result = m_butterfly->contiguousDouble()[i]; if (result != result) break; return JSValue(JSValue::EncodeAsDouble, result); } case ALL_ARRAY_STORAGE_INDEXING_TYPES: - if (i < butterfly->arrayStorage()->vectorLength()) - return butterfly->arrayStorage()->m_vector[i].get(); + if (i < m_butterfly->arrayStorage()->vectorLength()) + return m_butterfly->arrayStorage()->m_vector[i].get(); break; default: RELEASE_ASSERT_NOT_REACHED(); @@ -266,13 +267,13 @@ public: { if (JSValue result = tryGetIndexQuickly(i)) return result; - PropertySlot slot(this, PropertySlot::InternalMethodType::Get); - if (methodTable(exec->vm())->getOwnPropertySlotByIndex(this, exec, i, slot)) + PropertySlot slot(this); + if (methodTable()->getOwnPropertySlotByIndex(this, exec, i, slot)) return slot.getValue(exec, i); return JSValue(); } - JSValue getIndex(ExecState* exec, unsigned i) const + JSValue getIndex(ExecState* exec, unsigned i) { if (JSValue result = tryGetIndexQuickly(i)) return result; @@ -281,8 +282,7 @@ public: bool canSetIndexQuickly(unsigned i) { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return false; @@ -291,11 +291,11 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: case NonArrayWithArrayStorage: case ArrayWithArrayStorage: - return i < butterfly->vectorLength(); + return i < m_butterfly->vectorLength(); case NonArrayWithSlowPutArrayStorage: case ArrayWithSlowPutArrayStorage: - return i < butterfly->arrayStorage()->vectorLength() - && !!butterfly->arrayStorage()->m_vector[i]; + return i < m_butterfly->arrayStorage()->vectorLength() + && !!m_butterfly->arrayStorage()->m_vector[i]; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -304,7 +304,7 @@ public: bool canSetIndexQuicklyForPutDirect(unsigned i) { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return false; @@ -312,7 +312,7 @@ public: case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return i < m_butterfly.get(this)->vectorLength(); + return i < m_butterfly->vectorLength(); default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -321,10 +321,9 @@ public: void setIndexQuickly(VM& vm, unsigned i, JSValue v) { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_INT32_INDEXING_TYPES: { - ASSERT(i < butterfly->vectorLength()); + ASSERT(i < m_butterfly->vectorLength()); if (!v.isInt32()) { convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -332,14 +331,14 @@ public: FALLTHROUGH; } case ALL_CONTIGUOUS_INDEXING_TYPES: { - ASSERT(i < butterfly->vectorLength()); - butterfly->contiguous()[i].set(vm, this, v); - if (i >= butterfly->publicLength()) - butterfly->setPublicLength(i + 1); + 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 < butterfly->vectorLength()); + ASSERT(i < m_butterfly->vectorLength()); if (!v.isNumber()) { convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -349,13 +348,13 @@ public: convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; } - butterfly->contiguousDouble()[i] = value; - if (i >= butterfly->publicLength()) - butterfly->setPublicLength(i + 1); + m_butterfly->contiguousDouble()[i] = value; + if (i >= m_butterfly->publicLength()) + m_butterfly->setPublicLength(i + 1); break; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = butterfly->arrayStorage(); + ArrayStorage* storage = m_butterfly->arrayStorage(); WriteBarrier<Unknown>& x = storage->m_vector[i]; JSValue old = x.get(); x.set(vm, this, v); @@ -370,23 +369,17 @@ public: RELEASE_ASSERT_NOT_REACHED(); } } - + void initializeIndex(VM& vm, unsigned i, JSValue v) { - initializeIndex(vm, i, v, indexingType()); - } - - void initializeIndex(VM& vm, unsigned i, JSValue v, IndexingType indexingType) - { - Butterfly* butterfly = m_butterfly.get(this); - switch (indexingType) { + switch (structure()->indexingType()) { case ALL_UNDECIDED_INDEXING_TYPES: { setIndexQuicklyToUndecided(vm, i, v); break; } case ALL_INT32_INDEXING_TYPES: { - ASSERT(i < butterfly->publicLength()); - ASSERT(i < butterfly->vectorLength()); + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); if (!v.isInt32()) { convertInt32ToDoubleOrContiguousWhilePerformingSetIndex(vm, i, v); break; @@ -394,14 +387,14 @@ public: FALLTHROUGH; } case ALL_CONTIGUOUS_INDEXING_TYPES: { - ASSERT(i < butterfly->publicLength()); - ASSERT(i < butterfly->vectorLength()); - butterfly->contiguous()[i].set(vm, this, v); + 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 < butterfly->publicLength()); - ASSERT(i < butterfly->vectorLength()); + ASSERT(i < m_butterfly->publicLength()); + ASSERT(i < m_butterfly->vectorLength()); if (!v.isNumber()) { convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; @@ -411,11 +404,11 @@ public: convertDoubleToContiguousWhilePerformingSetIndex(vm, i, v); return; } - butterfly->contiguousDouble()[i] = value; + m_butterfly->contiguousDouble()[i] = value; break; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = butterfly->arrayStorage(); + ArrayStorage* storage = m_butterfly->arrayStorage(); ASSERT(i < storage->length()); ASSERT(i < storage->m_numValuesInVector); storage->m_vector[i].set(vm, this, v); @@ -428,7 +421,7 @@ public: bool hasSparseMap() { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -436,7 +429,7 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return !!m_butterfly.get(this)->arrayStorage()->m_sparseMap; + return m_butterfly->arrayStorage()->m_sparseMap; default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -445,7 +438,7 @@ public: bool inSparseIndexingMode() { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -453,7 +446,7 @@ public: case ALL_CONTIGUOUS_INDEXING_TYPES: return false; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly.get(this)->arrayStorage()->inSparseMode(); + return m_butterfly->arrayStorage()->inSparseMode(); default: RELEASE_ASSERT_NOT_REACHED(); return false; @@ -472,22 +465,16 @@ public: 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); - - void putGetter(ExecState*, PropertyName, JSValue, unsigned attributes); - void putSetter(ExecState*, PropertyName, JSValue, unsigned attributes); 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; 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); - JS_EXPORT_PRIVATE bool hasInstance(ExecState*, JSValue value, JSValue hasInstanceValue); bool hasInstance(ExecState*, JSValue); static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty); @@ -495,10 +482,6 @@ public: JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - 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); - JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; JS_EXPORT_PRIVATE double toNumber(ExecState*) const; @@ -506,36 +489,36 @@ public: 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 { - Structure* structure = this->structure(vm); - PropertyOffset offset = structure->get(vm, propertyName); - checkOffset(offset, structure->inlineCapacity()); + 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()); + JSCell* specific; + PropertyOffset offset = structure()->get(vm, propertyName, attributes, specific); + checkOffset(offset, structure()->inlineCapacity()); return offset != invalidOffset ? getDirect(offset) : JSValue(); } PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName) { - Structure* structure = this->structure(vm); - PropertyOffset offset = structure->get(vm, propertyName); - checkOffset(offset, structure->inlineCapacity()); + PropertyOffset offset = structure()->get(vm, propertyName); + checkOffset(offset, structure()->inlineCapacity()); return offset; } PropertyOffset getDirectOffset(VM& vm, PropertyName propertyName, unsigned& attributes) { - Structure* structure = this->structure(vm); - PropertyOffset offset = structure->get(vm, propertyName, attributes); - checkOffset(offset, structure->inlineCapacity()); + JSCell* specific; + PropertyOffset offset = structure()->get(vm, propertyName, attributes, specific); + checkOffset(offset, structure()->inlineCapacity()); return offset; } @@ -559,11 +542,11 @@ public: return inlineStorageUnsafe(); } - const Butterfly* butterfly() const { return m_butterfly.get(this); } - Butterfly* butterfly() { return m_butterfly.get(this); } + const Butterfly* butterfly() const { return m_butterfly.get(); } + Butterfly* butterfly() { return m_butterfly.get(); } - ConstPropertyStorage outOfLineStorage() const { return m_butterfly.get(this)->propertyStorage(); } - PropertyStorage outOfLineStorage() { return m_butterfly.get(this)->propertyStorage(); } + ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); } + PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); } const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const { @@ -581,16 +564,15 @@ public: void transitionTo(VM&, Structure*); - JS_EXPORT_PRIVATE bool removeDirect(VM&, PropertyName); // Return true if anything is removed. + 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. + // This is used by JSActivation. bool putOwnDataProperty(VM&, PropertyName, JSValue, PutPropertySlot&); // Fast access to known property offsets. @@ -598,23 +580,23 @@ public: 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 putDirectNativeIntrinsicGetter(VM&, JSGlobalObject*, Identifier, NativeFunction, Intrinsic, unsigned attributes); - 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); + 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 static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow); bool isGlobalObject() const; + bool isVariableObject() const; + bool isStaticScopeObject() const; + bool isNameScopeObject() const; + bool isActivationObject() const; bool isErrorInstance() const; - bool isWithScope() const; - JS_EXPORT_PRIVATE void seal(VM&); - JS_EXPORT_PRIVATE void freeze(VM&); + void seal(VM&); + 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 isSealed(VM& vm) { return structure()->isSealed(vm); } + bool isFrozen(VM& vm) { return structure()->isFrozen(vm); } bool isExtensible() { return structure()->isExtensible(); } bool indexingShouldBeSparse() { @@ -623,7 +605,7 @@ public: } bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } - void reifyAllStaticProperties(ExecState*); + void reifyStaticFunctionsForDelete(ExecState* exec); JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(VM&, size_t oldSize, size_t newSize); void setButterflyWithoutChangingStructure(VM&, Butterfly*); @@ -633,13 +615,10 @@ public: void setStructureAndReallocateStorageIfNecessary(VM&, unsigned oldCapacity, Structure*); void setStructureAndReallocateStorageIfNecessary(VM&, Structure*); - JS_EXPORT_PRIVATE void convertToDictionary(VM&); - void flattenDictionaryObject(VM& vm) { - structure(vm)->flattenDictionaryStructure(vm, this); + structure()->flattenDictionaryStructure(vm, this); } - void shiftButterflyAfterFlattening(VM&, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter); JSGlobalObject* globalObject() const { @@ -665,8 +644,8 @@ public: // contiguous, array storage). ContiguousJSValues ensureInt32(VM& vm) { - if (LIKELY(hasInt32(indexingType()))) - return m_butterfly.get(this)->contiguousInt32(); + if (LIKELY(hasInt32(structure()->indexingType()))) + return m_butterfly->contiguousInt32(); return ensureInt32Slow(vm); } @@ -677,8 +656,8 @@ public: // or array storage). ContiguousDoubles ensureDouble(VM& vm) { - if (LIKELY(hasDouble(indexingType()))) - return m_butterfly.get(this)->contiguousDouble(); + if (LIKELY(hasDouble(structure()->indexingType()))) + return m_butterfly->contiguousDouble(); return ensureDoubleSlow(vm); } @@ -687,21 +666,32 @@ public: // indexing should be sparse or because we're having a bad time. ContiguousJSValues ensureContiguous(VM& vm) { - if (LIKELY(hasContiguous(indexingType()))) - return m_butterfly.get(this)->contiguous(); + if (LIKELY(hasContiguous(structure()->indexingType()))) + return m_butterfly->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(hasAnyArrayStorage(indexingType()))) - return m_butterfly.get(this)->arrayStorage(); - + if (LIKELY(hasArrayStorage(structure()->indexingType()))) + return m_butterfly->arrayStorage(); + return ensureArrayStorageSlow(vm); } @@ -717,8 +707,6 @@ public: return &m_butterfly; } - JSValue getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage); - DECLARE_EXPORT_INFO; protected: @@ -726,6 +714,8 @@ protected: { Base::finishCreation(vm); ASSERT(inherits(info())); + ASSERT(!structure()->outOfLineCapacity()); + ASSERT(structure()->isEmpty()); ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); ASSERT(structure()->isObject()); ASSERT(classInfo()); @@ -747,26 +737,23 @@ protected: // storage. This will assert otherwise. ArrayStorage* arrayStorage() { - ASSERT(hasAnyArrayStorage(indexingType())); - return m_butterfly.get(this)->arrayStorage(); + ASSERT(hasArrayStorage(structure()->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()) { + switch (structure()->indexingType()) { case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return m_butterfly.get(this)->arrayStorage(); + return m_butterfly->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); @@ -782,18 +769,23 @@ protected: 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&); @@ -820,25 +812,118 @@ protected: void ensureLength(VM& vm, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); - - if (m_butterfly.get(this)->vectorLength() < length) + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); + + if (m_butterfly->vectorLength() < length) ensureLengthSlow(vm, length); - if (m_butterfly.get(this)->publicLength() < length) - m_butterfly.get(this)->setPublicLength(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<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; @@ -855,16 +940,12 @@ private: ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(VM&, ArrayStorage*); template<PutMode> - bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&); + bool putDirectInternal(VM&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*); - JS_EXPORT_PRIVATE NEVER_INLINE void putInlineSlow(ExecState*, PropertyName, JSValue, PutPropertySlot&); - - bool getNonIndexPropertySlot(ExecState*, PropertyName, PropertySlot&); - bool getOwnNonIndexPropertySlot(VM&, Structure&, PropertyName, PropertySlot&); + bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&); JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, JSValue, unsigned, PropertyOffset); - void fillCustomGetterPropertySlot(PropertySlot&, JSValue, unsigned, Structure&); - JS_EXPORT_PRIVATE const HashTableValue* findPropertyHashEntry(PropertyName) const; + const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const; void putIndexedDescriptor(ExecState*, SparseArrayEntry*, const PropertyDescriptor&, PropertyDescriptor& old); @@ -875,6 +956,8 @@ private: unsigned getNewVectorLength(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); @@ -886,14 +969,16 @@ private: ContiguousJSValues ensureInt32Slow(VM&); ContiguousDoubles ensureDoubleSlow(VM&); ContiguousJSValues ensureContiguousSlow(VM&); - JS_EXPORT_PRIVATE ArrayStorage* ensureArrayStorageSlow(VM&); - + ContiguousJSValues rageEnsureContiguousSlow(VM&); + ArrayStorage* ensureArrayStorageSlow(VM&); + + enum DoubleToContiguousMode { EncodeValueAsDouble, RageConvertDoubleToValue }; + template<DoubleToContiguousMode mode> + ContiguousJSValues genericConvertDoubleToContiguous(VM&); + ContiguousJSValues ensureContiguousSlow(VM&, DoubleToContiguousMode); + protected: - CopyBarrier<Butterfly> m_butterfly; -#if USE(JSVALUE32_64) -private: - uint32_t m_padding; -#endif + CopyWriteBarrier<Butterfly> m_butterfly; }; // JSNonFinalObject is a type of JSObject that has some internal storage, @@ -919,7 +1004,7 @@ protected: void finishCreation(VM& vm) { Base::finishCreation(vm); - ASSERT(!this->structure()->hasInlineStorage()); + ASSERT(!this->structure()->totalStorageCapacity()); ASSERT(classInfo()); } }; @@ -933,15 +1018,11 @@ 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() @@ -955,11 +1036,11 @@ public: return (maxSize - allocationSize(0)) / sizeof(WriteBarrier<Unknown>); } - static JSFinalObject* create(ExecState*, Structure*, Butterfly* = nullptr); + static JSFinalObject* create(ExecState*, Structure*); static JSFinalObject* create(VM&, Structure*); static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype, unsigned inlineCapacity) { - return Structure::create(vm, globalObject, prototype, typeInfo(), info(), defaultIndexingType, inlineCapacity); + return Structure::create(vm, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), info(), NonArray, inlineCapacity); } JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); @@ -979,16 +1060,15 @@ protected: private: friend class LLIntOffsetsExtractor; - explicit JSFinalObject(VM& vm, Structure* structure, Butterfly* butterfly = nullptr) - : JSObject(vm, structure, butterfly) + explicit JSFinalObject(VM& vm, Structure* structure) + : JSObject(vm, structure) { } -}; -JS_EXPORT_PRIVATE EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState*); + static const unsigned StructureFlags = JSObject::StructureFlags; +}; -inline JSFinalObject* JSFinalObject::create( - ExecState* exec, Structure* structure, Butterfly* butterfly) +inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure) { JSFinalObject* finalObject = new ( NotNull, @@ -996,7 +1076,7 @@ inline JSFinalObject* JSFinalObject::create( *exec->heap(), allocationSize(structure->inlineCapacity()) ) - ) JSFinalObject(exec->vm(), structure, butterfly); + ) JSFinalObject(exec->vm(), structure); finalObject->finishCreation(exec->vm()); return finalObject; } @@ -1025,17 +1105,35 @@ inline size_t JSObject::offsetOfInlineStorage() inline bool JSObject::isGlobalObject() const { - return type() == GlobalObjectType; + return structure()->typeInfo().type() == GlobalObjectType; } -inline bool JSObject::isErrorInstance() const +inline bool JSObject::isVariableObject() const { - return type() == ErrorInstanceType; + return structure()->typeInfo().type() >= VariableObjectType; } -inline bool JSObject::isWithScope() const + +inline bool JSObject::isStaticScopeObject() const +{ + JSType type = structure()->typeInfo().type(); + return type == NameScopeObjectType || type == ActivationObjectType; +} + + +inline bool JSObject::isNameScopeObject() const { - return type() == WithScopeType; + return structure()->typeInfo().type() == NameScopeObjectType; +} + +inline bool JSObject::isActivationObject() const +{ + return structure()->typeInfo().type() == ActivationObjectType; +} + +inline bool JSObject::isErrorInstance() const +{ + return structure()->typeInfo().type() == ErrorInstanceType; } inline void JSObject::setStructureAndButterfly(VM& vm, Structure* structure, Butterfly* butterfly) @@ -1095,46 +1193,21 @@ inline JSValue JSObject::prototype() const return structure()->storedPrototype(); } -// 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) +ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { unsigned attributes; - PropertyOffset offset = structure.get(vm, propertyName, attributes); - if (!isValidOffset(offset)) - return false; - - // 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: + JSCell* specific; + PropertyOffset offset = structure()->get(exec->vm(), propertyName, attributes, specific); + if (LIKELY(isValidOffset(offset))) { + JSValue value = getDirect(offset); + if (structure()->hasGetterSetterProperties() && value.isGetterSetter()) fillGetterPropertySlot(slot, value, attributes, offset); - return true; - case CustomGetterSetterType: - fillCustomGetterPropertySlot(slot, value, attributes, structure); - return true; - default: - break; - } + else + slot.setValue(this, attributes, value, offset); + return true; } - - 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*>(customGetterSetter)->getter()); - return; - } - slot.setCacheableCustom(this, attributes, jsCast<CustomGetterSetter*>(customGetterSetter)->getter()); + return getOwnPropertySlotSlow(exec, propertyName, slot); } // It may seem crazy to inline a function this large, especially a virtual function, @@ -1142,80 +1215,38 @@ ALWAYS_INLINE void JSObject::fillCustomGetterPropertySlot(PropertySlot& slot, JS // base class call to this. ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - Structure& structure = *object->structure(vm); - if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) - return true; - if (Optional<uint32_t> index = parseIndex(propertyName)) - return getOwnPropertySlotByIndex(object, exec, index.value(), slot); - return false; + return object->inlineGetOwnPropertySlot(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, PropertyName propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::fastGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - auto& structureIDTable = vm.heap.structureIDTable(); - JSObject* object = this; - while (true) { - 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 (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); - } - Structure& structure = *structureIDTable.get(object->structureID()); - if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) - return true; - JSValue prototype = structure.storedPrototype(); - if (!prototype.isObject()) - break; - object = asObject(prototype); - } - - if (Optional<uint32_t> index = parseIndex(propertyName)) - return getPropertySlot(exec, index.value(), slot); - return false; + if (!structure()->typeInfo().overridesGetOwnPropertySlot()) + return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); + return methodTable()->getOwnPropertySlot(this, exec, propertyName, slot); } -ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& 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, PropertyName propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - auto& structureIDTable = vm.heap.structureIDTable(); JSObject* object = this; while (true) { - Structure& structure = *structureIDTable.get(object->structureID()); - if (structure.classInfo()->methodTable.getOwnPropertySlotByIndex(object, exec, propertyName, slot)) + if (object->fastGetOwnPropertySlot(exec, propertyName, slot)) return true; - JSValue prototype = structure.storedPrototype(); + JSValue prototype = object->prototype(); if (!prototype.isObject()) return false; object = asObject(prototype); } } -ALWAYS_INLINE bool JSObject::getNonIndexPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - // This method only supports non-index PropertyNames. - ASSERT(!parseIndex(propertyName)); - - VM& vm = exec->vm(); - auto& structureIDTable = vm.heap.structureIDTable(); JSObject* object = this; while (true) { - Structure& structure = *structureIDTable.get(object->structureID()); - if (LIKELY(!TypeInfo::overridesGetOwnPropertySlot(object->inlineTypeFlags()))) { - if (object->getOwnNonIndexPropertySlot(vm, structure, propertyName, slot)) - return true; - } else if (structure.classInfo()->methodTable.getOwnPropertySlot(object, exec, propertyName, slot)) + if (object->methodTable()->getOwnPropertySlotByIndex(object, exec, propertyName, slot)) return true; - JSValue prototype = structure.storedPrototype(); + JSValue prototype = object->prototype(); if (!prototype.isObject()) return false; object = asObject(prototype); @@ -1224,7 +1255,7 @@ ALWAYS_INLINE bool JSObject::getNonIndexPropertySlot(ExecState* exec, PropertyNa inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot(this, PropertySlot::InternalMethodType::Get); + PropertySlot slot(this); if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); @@ -1233,7 +1264,7 @@ inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const { - PropertySlot slot(this, PropertySlot::InternalMethodType::Get); + PropertySlot slot(this); if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); @@ -1241,31 +1272,34 @@ inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const } template<JSObject::PutMode mode> -ALWAYS_INLINE bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot) +inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot, JSCell* specificFunction) { ASSERT(value); ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - ASSERT(!parseIndex(propertyName)); + ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex); - Structure* structure = this->structure(vm); - if (structure->isDictionary()) { - ASSERT(!structure->hasInferredTypes()); - + if (structure()->isDictionary()) { unsigned currentAttributes; - PropertyOffset offset = structure->get(vm, propertyName, 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); - structure->didReplaceProperty(offset); - slot.setExistingProperty(this, offset); - - if ((attributes & Accessor) != (currentAttributes & Accessor) || (attributes & CustomAccessor) != (currentAttributes & CustomAccessor)) { - ASSERT(!(attributes & ReadOnly)); - setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); - } + // 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; } @@ -1274,87 +1308,91 @@ ALWAYS_INLINE bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName 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); + if (structure()->putWillGrowOutOfLineStorage()) + newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); + offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes, specificFunction); + setStructureAndButterfly(vm, structure(), newButterfly); validateOffset(offset); - ASSERT(this->structure()->isValidOffset(offset)); + ASSERT(structure()->isValidOffset(offset)); putDirect(vm, offset, value); - slot.setNewProperty(this, offset); + // See comment on setNewProperty call below. + if (!specificFunction) + slot.setNewProperty(this, offset); if (attributes & ReadOnly) - this->structure()->setContainsReadOnlyProperties(); + structure()->setContainsReadOnlyProperties(); return true; } PropertyOffset offset; - size_t currentCapacity = this->structure()->outOfLineCapacity(); - Structure* newStructure = Structure::addPropertyTransitionToExistingStructure( - structure, propertyName, attributes, offset); - if (newStructure) { - newStructure->willStoreValueForExistingTransition( - vm, propertyName, value, slot.context() == PutPropertySlot::PutById); - + 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 != newStructure->outOfLineCapacity()) { - ASSERT(newStructure != this->structure()); - newButterfly = growOutOfLineStorage(vm, currentCapacity, newStructure->outOfLineCapacity()); + if (currentCapacity != structure->outOfLineCapacity()) { + ASSERT(structure != this->structure()); + newButterfly = growOutOfLineStorage(vm, currentCapacity, structure->outOfLineCapacity()); } validateOffset(offset); - ASSERT(newStructure->isValidOffset(offset)); - setStructureAndButterfly(vm, newStructure, newButterfly); + ASSERT(structure->isValidOffset(offset)); + setStructureAndButterfly(vm, structure, newButterfly); putDirect(vm, offset, value); - slot.setNewProperty(this, offset); + // 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; - bool hasInferredType; - offset = structure->get(vm, propertyName, currentAttributes, hasInferredType); + JSCell* currentSpecificFunction; + offset = structure()->get(vm, propertyName, currentAttributes, currentSpecificFunction); if (offset != invalidOffset) { if ((mode == PutModePut) && currentAttributes & ReadOnly) return false; - structure->didReplaceProperty(offset); - if (UNLIKELY(hasInferredType)) { - structure->willStoreValueForReplace( - vm, propertyName, value, slot.context() == PutPropertySlot::PutById); + // 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); - - if ((attributes & Accessor) != (currentAttributes & Accessor)) { - ASSERT(!(attributes & ReadOnly)); - setStructure(vm, Structure::attributeChangeTransition(vm, structure, propertyName, attributes)); - } return true; } if ((mode == PutModePut) && !isExtensible()) return false; - // We want the structure transition watchpoint to fire after this object has switched - // structure. This allows adaptive watchpoints to observe if the new structure is the one - // we want. - DeferredStructureTransitionWatchpointFire deferredWatchpointFire; - - newStructure = Structure::addPropertyTransition( - vm, structure, propertyName, attributes, offset, slot.context(), &deferredWatchpointFire); - newStructure->willStoreValueForNewTransition( - vm, propertyName, value, slot.context() == PutPropertySlot::PutById); + Structure* structure = Structure::addPropertyTransition(vm, this->structure(), propertyName, attributes, specificFunction, offset, slot.context()); validateOffset(offset); - ASSERT(newStructure->isValidOffset(offset)); - setStructureAndReallocateStorageIfNecessary(vm, newStructure); + ASSERT(structure->isValidOffset(offset)); + setStructureAndReallocateStorageIfNecessary(vm, structure); putDirect(vm, offset, value); - slot.setNewProperty(this, offset); + // 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) - newStructure->setContainsReadOnlyProperties(); + structure->setContainsReadOnlyProperties(); return true; } @@ -1376,7 +1414,7 @@ inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, unsign inline void JSObject::setStructureAndReallocateStorageIfNecessary(VM& vm, Structure* newStructure) { setStructureAndReallocateStorageIfNecessary( - vm, structure(vm)->outOfLineCapacity(), newStructure); + vm, structure()->outOfLineCapacity(), newStructure); } inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) @@ -1384,39 +1422,32 @@ inline bool JSObject::putOwnDataProperty(VM& vm, PropertyName propertyName, JSVa ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); ASSERT(!structure()->hasGetterSetterProperties()); - ASSERT(!structure()->hasCustomGetterSetterProperties()); - return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot); + return putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)); } inline void 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); + putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value)); } inline void JSObject::putDirect(VM& vm, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(!value.isGetterSetter()); - ASSERT(!value.isCustomGetterSetter()); - putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, 0, slot); + 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)); - ASSERT(!value.isCustomGetterSetter()); - Butterfly* newButterfly = m_butterfly.get(this); + Butterfly* newButterfly = m_butterfly.get(); if (structure()->putWillGrowOutOfLineStorage()) newButterfly = growOutOfLineStorage(vm, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity()); - Structure* structure = this->structure(); - PropertyOffset offset = structure->addPropertyWithoutTransition(vm, propertyName, attributes); - bool shouldOptimize = false; - structure->willStoreValueForNewTransition(vm, propertyName, value, shouldOptimize); - setStructureAndButterfly(vm, structure, newButterfly); + PropertyOffset offset = structure()->addPropertyWithoutTransition(vm, propertyName, attributes, getCallableObject(value)); + setStructureAndButterfly(vm, structure(), newButterfly); putDirect(vm, offset, value); } @@ -1425,15 +1456,18 @@ inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType pre return methodTable()->defaultValue(this, exec, preferredType); } -ALWAYS_INLINE JSObject* Register::object() const +ALWAYS_INLINE JSObject* Register::function() const { + if (!jsValue()) + return 0; return asObject(jsValue()); } -ALWAYS_INLINE Register& Register::operator=(JSObject* object) +ALWAYS_INLINE Register Register::withCallee(JSObject* callee) { - u.value = JSValue::encode(JSValue(object)); - return *this; + Register r; + r = JSValue(callee); + return r; } inline size_t offsetInButterfly(PropertyOffset offset) @@ -1441,30 +1475,26 @@ inline size_t offsetInButterfly(PropertyOffset offset) return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage(); } -inline size_t JSObject::butterflyPreCapacity() +// 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(hasIndexingHeader())) - return butterfly()->indexingHeader()->preCapacity(structure()); - return 0; + if (isOutOfLineOffset(offset)) + return sizeof(EncodedJSValue) * offsetInButterfly(offset); + return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset); } -inline size_t JSObject::butterflyTotalSize() +// Returns the maximum offset (away from zero) a load instruction will encode. +inline size_t maxOffsetRelativeToPatchedStorage(PropertyOffset offset) { - 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); + 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); } inline int indexRelativeToBase(PropertyOffset offset) @@ -1482,22 +1512,11 @@ 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); ALWAYS_INLINE Identifier makeIdentifier(VM& vm, const char* name) { - return Identifier::fromString(&vm, name); + return Identifier(&vm, name); } ALWAYS_INLINE Identifier makeIdentifier(VM&, const Identifier& name) @@ -1505,9 +1524,6 @@ 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); - // 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 @@ -1517,38 +1533,32 @@ bool validateAndApplyPropertyDescriptor(ExecState*, JSObject*, PropertyName, boo 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) -#define JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(jsName, cppName, attributes, length) \ - JSC_NATIVE_INTRINSIC_FUNCTION_WITHOUT_TRANSITION(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)) - -#define JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(jsName, generatorName, attributes) \ - putDirectBuiltinFunctionWithoutTransition(\ - vm, globalObject, makeIdentifier(vm, (jsName)), (generatorName)(vm), (attributes)) - -// 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) +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)); +} +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))); +} } // namespace JSC |