From 32761a6cee1d0dee366b885b7b9c777e67885688 Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Sun, 10 Apr 2016 09:28:39 +0000 Subject: webkitgtk-2.4.11 --- Source/JavaScriptCore/runtime/JSObject.cpp | 1362 +++++++++++----------------- 1 file changed, 513 insertions(+), 849 deletions(-) (limited to 'Source/JavaScriptCore/runtime/JSObject.cpp') diff --git a/Source/JavaScriptCore/runtime/JSObject.cpp b/Source/JavaScriptCore/runtime/JSObject.cpp index 730194f3a..6910b7e04 100644 --- a/Source/JavaScriptCore/runtime/JSObject.cpp +++ b/Source/JavaScriptCore/runtime/JSObject.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003-2006, 2008, 2009, 2012-2016 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel (eric@webkit.org) * * This library is free software; you can redistribute it and/or @@ -25,28 +25,23 @@ #include "JSObject.h" #include "ButterflyInlines.h" -#include "CopiedBlockInlines.h" #include "CopiedSpaceInlines.h" #include "CopyVisitor.h" #include "CopyVisitorInlines.h" -#include "CustomGetterSetter.h" #include "DatePrototype.h" #include "ErrorConstructor.h" -#include "Exception.h" #include "Executable.h" #include "GetterSetter.h" #include "IndexingHeaderInlines.h" -#include "JSBoundSlotBaseFunction.h" #include "JSFunction.h" #include "JSGlobalObject.h" #include "Lookup.h" #include "NativeErrorConstructor.h" #include "Nodes.h" #include "ObjectPrototype.h" -#include "JSCInlines.h" +#include "Operations.h" #include "PropertyDescriptor.h" #include "PropertyNameArray.h" -#include "ProxyObject.h" #include "Reject.h" #include "SlotVisitorInlines.h" #include @@ -60,28 +55,40 @@ namespace JSC { // ArrayConventions.h. static unsigned lastArraySize = 0; +JSCell* getCallableObjectSlow(JSCell* cell) +{ + Structure* structure = cell->structure(); + if (structure->typeInfo().type() == JSFunctionType) + return cell; + if (structure->classInfo()->isSubClassOf(InternalFunction::info())) + return cell; + return 0; +} + STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject); STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject); const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; -const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) }; +const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) }; -const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) }; +const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) }; -static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) +static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify) { - VM& vm = exec->vm(); - // Add properties from the static hashtables of properties for (; classInfo; classInfo = classInfo->parentClass) { - const HashTable* table = classInfo->staticPropHashTable; + const HashTable* table = classInfo->propHashTable(exec); if (!table) continue; - - for (auto iter = table->begin(); iter != table->end(); ++iter) { - if (!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties()) - propertyNames.add(Identifier::fromString(&vm, iter.key())); + table->initializeIfNeeded(exec); + ASSERT(table->table); + + int hashSizeMask = table->compactSize - 1; + const HashEntry* entry = table->table; + for (int i = 0; i <= hashSizeMask; ++i, ++entry) { + if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)) && !((entry->attributes() & Function) && didReify)) + propertyNames.add(entry->key()); } } } @@ -106,7 +113,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt size_t capacityInBytes = Butterfly::totalSize(preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); if (visitor.checkIfShouldCopy(butterfly->base(preCapacity, propertyCapacity))) { Butterfly* newButterfly = Butterfly::createUninitializedDuringCollection(visitor, preCapacity, propertyCapacity, hasIndexingHeader, indexingPayloadSizeInBytes); - + // Copy the properties. PropertyStorage currentTarget = newButterfly->propertyStorage(); PropertyStorage currentSource = butterfly->propertyStorage(); @@ -122,7 +129,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt WriteBarrier* currentSource; size_t count; - switch (this->indexingType()) { + switch (structure->indexingType()) { case ALL_UNDECIDED_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -152,7 +159,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue)); } - m_butterfly.setWithoutBarrier(newButterfly); + m_butterfly.setWithoutWriteBarrier(newButterfly); visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); } } @@ -161,7 +168,7 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but { ASSERT(butterfly); - Structure* structure = this->structure(visitor.vm()); + Structure* structure = this->structure(); size_t propertyCapacity = structure->outOfLineCapacity(); size_t preCapacity; @@ -183,7 +190,7 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but butterfly->base(preCapacity, propertyCapacity), capacityInBytes); // Mark the array if appropriate. - switch (this->indexingType()) { + switch (structure->indexingType()) { case ALL_CONTIGUOUS_INDEXING_TYPES: visitor.appendValues(butterfly->contiguous().data(), butterfly->publicLength()); break; @@ -197,13 +204,6 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but } } -size_t JSObject::estimatedSize(JSCell* cell) -{ - JSObject* thisObject = jsCast(cell); - size_t butterflyOutOfLineSize = thisObject->m_butterfly ? thisObject->structure()->outOfLineSize() : 0; - return Base::estimatedSize(cell) + butterflyOutOfLineSize; -} - void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSObject* thisObject = jsCast(cell); @@ -215,9 +215,9 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) JSCell::visitChildren(thisObject, visitor); - Butterfly* butterfly = thisObject->m_butterfly.getWithoutBarrier(); + Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm())->outOfLineSize()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); #if !ASSERT_DISABLED visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; @@ -228,11 +228,11 @@ void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken to { JSObject* thisObject = jsCast(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - + if (token != ButterflyCopyToken) return; - Butterfly* butterfly = thisObject->m_butterfly.getWithoutBarrier(); + Butterfly* butterfly = thisObject->butterfly(); if (butterfly) thisObject->copyButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); } @@ -248,12 +248,11 @@ void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) JSCell::visitChildren(thisObject, visitor); - Structure* structure = thisObject->structure(); Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, structure->outOfLineSize()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); - size_t storageSize = structure->inlineSize(); + size_t storageSize = thisObject->structure()->inlineSize(); visitor.appendValues(thisObject->inlineStorage(), storageSize); #if !ASSERT_DISABLED @@ -268,44 +267,6 @@ String JSObject::className(const JSObject* object) return info->className; } -String JSObject::calculatedClassName(JSObject* object) -{ - String prototypeFunctionName; - ExecState* exec = object->globalObject()->globalExec(); - PropertySlot slot(object->structure()->storedPrototype(), PropertySlot::InternalMethodType::VMInquiry); - PropertyName constructor(exec->propertyNames().constructor); - if (object->getPropertySlot(exec, constructor, slot)) { - if (slot.isValue()) { - JSValue constructorValue = slot.getValue(exec, constructor); - if (constructorValue.isCell()) { - if (JSCell* constructorCell = constructorValue.asCell()) { - if (JSObject* ctorObject = constructorCell->getObject()) { - if (JSFunction* constructorFunction = jsDynamicCast(ctorObject)) - prototypeFunctionName = constructorFunction->calculatedDisplayName(exec); - else if (InternalFunction* constructorFunction = jsDynamicCast(ctorObject)) - prototypeFunctionName = constructorFunction->calculatedDisplayName(exec); - } - } - } - } - } - - if (prototypeFunctionName.isNull() || prototypeFunctionName == "Object") { - String tableClassName = object->methodTable()->className(object); - if (!tableClassName.isNull() && tableClassName != "Object") - return tableClassName; - - String classInfoName = object->classInfo()->className; - if (!classInfoName.isNull()) - return classInfoName; - - if (prototypeFunctionName.isNull()) - return ASCIILiteral("Object"); - } - - return prototypeFunctionName; -} - bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, unsigned i, PropertySlot& slot) { // NB. The fact that we're directly consulting our indexed storage implies that it is not @@ -313,9 +274,9 @@ bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, // getOwnPropertySlotByIndex(). if (i > MAX_ARRAY_INDEX) - return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); + return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); - switch (thisObject->indexingType()) { + switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: break; @@ -350,7 +311,7 @@ bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = thisObject->m_butterfly.get(thisObject)->arrayStorage(); + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i >= storage->length()) return false; @@ -381,20 +342,42 @@ bool JSObject::getOwnPropertySlotByIndex(JSObject* thisObject, ExecState* exec, // ECMA 8.6.2.2 void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - putInline(cell, exec, propertyName, value, slot); -} - -void JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) -{ + JSObject* thisObject = jsCast(cell); + ASSERT(value); + ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject)); VM& vm = exec->vm(); + + // Try indexed put first. This is required for correctness, since loads on property names that appear like + // valid indices will never look in the named property storage. + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) { + putByIndex(thisObject, exec, i, value, slot.isStrictMode()); + return; + } + + // Check if there are any setters or getters in the prototype chain + JSValue prototype; + if (propertyName != exec->propertyNames().underscoreProto) { + for (JSObject* obj = thisObject; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { + prototype = obj->prototype(); + if (prototype.isNull()) { + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName)); + if (!thisObject->putDirectInternal(vm, propertyName, value, 0, slot, getCallableObject(value)) + && slot.isStrictMode()) + throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); + return; + } + } + } - JSObject* obj = this; - for (;;) { + JSObject* obj; + for (obj = thisObject; ; obj = asObject(prototype)) { unsigned attributes; - PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes); + JSCell* specificValue; + PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes, specificValue); if (isValidOffset(offset)) { if (attributes & ReadOnly) { - ASSERT(structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this); + ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); if (slot.isStrictMode()) exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); return; @@ -402,42 +385,31 @@ void JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue JSValue gs = obj->getDirect(offset); if (gs.isGetterSetter()) { - callSetter(exec, this, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); - if (!structure()->isDictionary()) - slot.setCacheableSetter(obj, offset); + callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); return; - } - if (gs.isCustomGetterSetter()) { - callCustomSetter(exec, gs, attributes & CustomAccessor, obj, slot.thisValue(), value); - if (attributes & CustomAccessor) - slot.setCustomAccessor(obj, jsCast(gs.asCell())->setter()); - else - slot.setCustomValue(obj, jsCast(gs.asCell())->setter()); - return; - } - ASSERT(!(attributes & Accessor)); + } else + ASSERT(!(attributes & Accessor)); // If there's an existing property on the object or one of its // prototypes it should be replaced, so break here. break; } - if (!obj->staticFunctionsReified()) { - if (obj->classInfo()->hasStaticSetterOrReadonlyProperties()) { - if (auto* entry = obj->findPropertyHashEntry(propertyName)) { - putEntry(exec, entry, obj, this, propertyName, value, slot); - return; - } + const ClassInfo* info = obj->classInfo(); + if (info->hasStaticSetterOrReadonlyProperties(vm)) { + if (const HashEntry* entry = obj->findPropertyHashEntry(exec, propertyName)) { + putEntry(exec, entry, obj, propertyName, value, slot); + return; } } - JSValue prototype = obj->prototype(); + prototype = obj->prototype(); if (prototype.isNull()) break; - obj = asObject(prototype); } - ASSERT(!structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == this); - if (!putDirectInternal(vm, propertyName, value, 0, slot) && slot.isStrictMode()) + ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); + if (!thisObject->putDirectInternal(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); + return; } void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) @@ -450,7 +422,7 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, return; } - switch (thisObject->indexingType()) { + switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: break; @@ -505,7 +477,7 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { - ArrayStorage* storage = thisObject->m_butterfly.get(thisObject)->arrayStorage(); + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (propertyName >= storage->vectorLength()) break; @@ -527,7 +499,7 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, case NonArrayWithSlowPutArrayStorage: case ArrayWithSlowPutArrayStorage: { - ArrayStorage* storage = thisObject->m_butterfly.get(thisObject)->arrayStorage(); + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (propertyName >= storage->vectorLength()) break; @@ -577,11 +549,11 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists // This will always be a new entry in the map, so no need to check we can write, // and attributes are default so no need to set them. if (value) - map->add(this, i).iterator->value.set(vm, map, value); + map->add(this, i).iterator->value.set(vm, this, value); } DeferGC deferGC(vm.heap); - Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0)); + Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(0)); RELEASE_ASSERT(newButterfly); newButterfly->arrayStorage()->m_indexBias = 0; newButterfly->arrayStorage()->setVectorLength(0); @@ -593,21 +565,18 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists void JSObject::enterDictionaryIndexingMode(VM& vm) { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: // NOTE: this is horribly inefficient, as it will perform two conversions. We could optimize - // this case if we ever cared. Note that ensureArrayStorage() can return null if the object - // doesn't support traditional indexed properties. At the time of writing, this just affects - // typed arrays. - if (ArrayStorage* storage = ensureArrayStorageSlow(vm)) - enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, storage); + // this case if we ever cared. + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, ensureArrayStorageSlow(vm)); break; case ALL_ARRAY_STORAGE_INDEXING_TYPES: - enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get(this)->arrayStorage()); + enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage()); break; default: @@ -620,7 +589,7 @@ void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) if (mayInterceptIndexedAccesses()) return; - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors)); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AddIndexedAccessors)); if (!vm.prototypeMap.isPrototype(this)) return; @@ -631,13 +600,13 @@ void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize) { ASSERT(length < MAX_ARRAY_INDEX); - IndexingType oldType = indexingType(); + IndexingType oldType = structure()->indexingType(); ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); ASSERT(!structure()->needsSlowPutIndexing()); ASSERT(!indexingShouldBeSparse()); unsigned vectorLength = std::max(length, BASE_VECTOR_LEN); Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( - m_butterfly.get(this), vm, this, structure(), structure()->outOfLineCapacity(), false, 0, + m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0, elementSize * vectorLength); newButterfly->setPublicLength(length); newButterfly->setVectorLength(vectorLength); @@ -648,7 +617,7 @@ Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length) { DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateUndecided); setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly; } @@ -657,7 +626,7 @@ ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length) { DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateInt32); setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguousInt32(); } @@ -667,8 +636,8 @@ ContiguousDoubles JSObject::createInitialDouble(VM& vm, unsigned length) DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(double)); for (unsigned i = newButterfly->vectorLength(); i--;) - newButterfly->contiguousDouble()[i] = PNaN; - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble); + newButterfly->contiguousDouble()[i] = QNaN; + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateDouble); setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguousDouble(); } @@ -677,7 +646,7 @@ ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length) { DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateContiguous); setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguous(); } @@ -685,11 +654,10 @@ ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length) ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength) { DeferGC deferGC(vm.heap); - Structure* structure = this->structure(vm); - IndexingType oldType = indexingType(); + IndexingType oldType = structure()->indexingType(); ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( - m_butterfly.get(this), vm, this, structure, structure->outOfLineCapacity(), false, 0, + m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0, ArrayStorage::sizeFor(vectorLength)); RELEASE_ASSERT(newButterfly); @@ -699,7 +667,7 @@ ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vec result->m_sparseMap.clear(); result->m_numValuesInVector = 0; result->m_indexBias = 0; - Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition()); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), structure()->suggestedArrayStorageTransition()); setStructureAndButterfly(vm, newStructure, newButterfly); return result; } @@ -711,43 +679,41 @@ ArrayStorage* JSObject::createInitialArrayStorage(VM& vm) ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm) { - ASSERT(hasUndecided(indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32)); - return m_butterfly.get(this)->contiguousInt32(); + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32)); + return m_butterfly->contiguousInt32(); } ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm) { - ASSERT(hasUndecided(indexingType())); - - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = butterfly->vectorLength(); i--;) - butterfly->contiguousDouble()[i] = PNaN; + ASSERT(hasUndecided(structure()->indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); - return m_butterfly.get(this)->contiguousDouble(); + for (unsigned i = m_butterfly->vectorLength(); i--;) + m_butterfly->contiguousDouble()[i] = QNaN; + + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); } ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm) { - ASSERT(hasUndecided(indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); - return m_butterfly.get(this)->contiguous(); + ASSERT(hasUndecided(structure()->indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); } ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength) { - Structure* structure = this->structure(vm); - unsigned publicLength = m_butterfly.get(this)->publicLength(); - unsigned propertyCapacity = structure->outOfLineCapacity(); - unsigned propertySize = structure->outOfLineSize(); + unsigned publicLength = m_butterfly->publicLength(); + unsigned propertyCapacity = structure()->outOfLineCapacity(); + unsigned propertySize = structure()->outOfLineSize(); Butterfly* newButterfly = Butterfly::createUninitialized( vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); memcpy( newButterfly->propertyStorage() - propertySize, - m_butterfly.get(this)->propertyStorage() - propertySize, + m_butterfly->propertyStorage() - propertySize, propertySize * sizeof(EncodedJSValue)); ArrayStorage* newStorage = newButterfly->arrayStorage(); @@ -760,154 +726,182 @@ ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& return newStorage; } -ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition) +ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { DeferGC deferGC(vm.heap); - ASSERT(hasUndecided(indexingType())); - - unsigned vectorLength = m_butterfly.get(this)->vectorLength(); - ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); + ASSERT(hasUndecided(structure()->indexingType())); + + ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); // No need to copy elements. - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); setStructureAndButterfly(vm, newStructure, storage->butterfly()); return storage; } +ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + return convertUndecidedToArrayStorage(vm, transition, m_butterfly->vectorLength()); +} + ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm) { - return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); + return convertUndecidedToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); } ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm) { - ASSERT(hasInt32(indexingType())); - - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = butterfly->vectorLength(); i--;) { - WriteBarrier* current = &butterfly->contiguousInt32()[i]; + ASSERT(hasInt32(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + WriteBarrier* current = &m_butterfly->contiguousInt32()[i]; double* currentAsDouble = bitwise_cast(current); JSValue v = current->get(); if (!v) { - *currentAsDouble = PNaN; + *currentAsDouble = QNaN; continue; } ASSERT(v.isInt32()); *currentAsDouble = v.asInt32(); } - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); - return m_butterfly.get(this)->contiguousDouble(); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble)); + return m_butterfly->contiguousDouble(); } ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm) { - ASSERT(hasInt32(indexingType())); + ASSERT(hasInt32(structure()->indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); - return m_butterfly.get(this)->contiguous(); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); } -ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition) +ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { + ASSERT(hasInt32(structure()->indexingType())); + DeferGC deferGC(vm.heap); - ASSERT(hasInt32(indexingType())); - - unsigned vectorLength = m_butterfly.get(this)->vectorLength(); - ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = 0; i < butterfly->publicLength(); i++) { - JSValue v = butterfly->contiguous()[i].get(); - if (v) { - newStorage->m_vector[i].setWithoutWriteBarrier(v); - newStorage->m_numValuesInVector++; - } else - ASSERT(newStorage->m_vector[i].get().isEmpty()); + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } -ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm) +ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition) { - return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); + return convertInt32ToArrayStorage(vm, transition, m_butterfly->vectorLength()); } -ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm) +ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm) { - ASSERT(hasDouble(indexingType())); + return convertInt32ToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); +} - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = butterfly->vectorLength(); i--;) { - double* current = &butterfly->contiguousDouble()[i]; +template +ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm) +{ + ASSERT(hasDouble(structure()->indexingType())); + + for (unsigned i = m_butterfly->vectorLength(); i--;) { + double* current = &m_butterfly->contiguousDouble()[i]; WriteBarrier* currentAsValue = bitwise_cast*>(current); double value = *current; if (value != value) { currentAsValue->clear(); continue; } - JSValue v = JSValue(JSValue::EncodeAsDouble, value); + JSValue v; + switch (mode) { + case EncodeValueAsDouble: + v = JSValue(JSValue::EncodeAsDouble, value); + break; + case RageConvertDoubleToValue: + v = jsNumber(value); + break; + } + ASSERT(v.isNumber()); currentAsValue->setWithoutWriteBarrier(v); } - setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); - return m_butterfly.get(this)->contiguous(); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous)); + return m_butterfly->contiguous(); } -ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition) +ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm) { - DeferGC deferGC(vm.heap); - ASSERT(hasDouble(indexingType())); + return genericConvertDoubleToContiguous(vm); +} - unsigned vectorLength = m_butterfly.get(this)->vectorLength(); - ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = 0; i < butterfly->publicLength(); i++) { - double value = butterfly->contiguousDouble()[i]; - if (value == value) { - newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); - newStorage->m_numValuesInVector++; - } else - ASSERT(newStorage->m_vector[i].get().isEmpty()); +ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm) +{ + return genericConvertDoubleToContiguous(vm); +} + +ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) +{ + DeferGC deferGC(vm.heap); + ASSERT(hasDouble(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + double value = m_butterfly->contiguousDouble()[i]; + if (value != value) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value)); + newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } +ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + return convertDoubleToArrayStorage(vm, transition, m_butterfly->vectorLength()); +} + ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm) { - return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); + return convertDoubleToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); } -ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition) +ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { DeferGC deferGC(vm.heap); - ASSERT(hasContiguous(indexingType())); - - unsigned vectorLength = m_butterfly.get(this)->vectorLength(); - ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength); - Butterfly* butterfly = m_butterfly.get(this); - for (unsigned i = 0; i < butterfly->publicLength(); i++) { - JSValue v = butterfly->contiguous()[i].get(); - if (v) { - newStorage->m_vector[i].setWithoutWriteBarrier(v); - newStorage->m_numValuesInVector++; - } else - ASSERT(newStorage->m_vector[i].get().isEmpty()); + ASSERT(hasContiguous(structure()->indexingType())); + + ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); + for (unsigned i = m_butterfly->publicLength(); i--;) { + JSValue v = m_butterfly->contiguous()[i].get(); + if (!v) + continue; + newStorage->m_vector[i].setWithoutWriteBarrier(v); + newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } +ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition) +{ + return convertContiguousToArrayStorage(vm, transition, m_butterfly->vectorLength()); +} + ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm) { - return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); + return convertContiguousToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); } void JSObject::convertUndecidedForValue(VM& vm, JSValue value) @@ -947,7 +941,7 @@ void JSObject::convertInt32ForValue(VM& vm, JSValue value) { ASSERT(!value.isInt32()); - if (value.isDouble() && !std::isnan(value.asDouble())) { + if (value.isDouble()) { convertInt32ToDouble(vm); return; } @@ -957,8 +951,8 @@ void JSObject::convertInt32ForValue(VM& vm, JSValue value) void JSObject::setIndexQuicklyToUndecided(VM& vm, unsigned index, JSValue value) { - ASSERT(index < m_butterfly.get(this)->publicLength()); - ASSERT(index < m_butterfly.get(this)->vectorLength()); + ASSERT(index < m_butterfly->publicLength()); + ASSERT(index < m_butterfly->vectorLength()); convertUndecidedForValue(vm, value); setIndexQuickly(vm, index, value); } @@ -981,12 +975,9 @@ ContiguousJSValues JSObject::ensureInt32Slow(VM& vm) { ASSERT(inherits(info())); - if (structure(vm)->hijacksIndexingHeader()) - return ContiguousJSValues(); - - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) return ContiguousJSValues(); return createInitialInt32(vm, 0); @@ -1008,12 +999,9 @@ ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm) { ASSERT(inherits(info())); - if (structure(vm)->hijacksIndexingHeader()) - return ContiguousDoubles(); - - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) return ContiguousDoubles(); return createInitialDouble(vm, 0); @@ -1033,16 +1021,13 @@ ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm) } } -ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm) +ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm, DoubleToContiguousMode mode) { ASSERT(inherits(info())); - if (structure(vm)->hijacksIndexingHeader()) - return ContiguousJSValues(); - - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) return ContiguousJSValues(); return createInitialContiguous(vm, 0); @@ -1053,6 +1038,8 @@ ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm) return convertInt32ToContiguous(vm); case ALL_DOUBLE_INDEXING_TYPES: + if (mode == RageConvertDoubleToValue) + return rageConvertDoubleToContiguous(vm); return convertDoubleToContiguous(vm); case ALL_ARRAY_STORAGE_INDEXING_TYPES: @@ -1064,14 +1051,21 @@ ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm) } } +ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm) +{ + return ensureContiguousSlow(vm, EncodeValueAsDouble); +} + +ContiguousJSValues JSObject::rageEnsureContiguousSlow(VM& vm) +{ + return ensureContiguousSlow(vm, RageConvertDoubleToValue); +} + ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) { ASSERT(inherits(info())); - - if (structure(vm)->hijacksIndexingHeader()) - return nullptr; - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: if (UNLIKELY(indexingShouldBeSparse())) return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm); @@ -1079,22 +1073,22 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) case ALL_UNDECIDED_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure(vm)->needsSlowPutIndexing()); + ASSERT(!structure()->needsSlowPutIndexing()); return convertUndecidedToArrayStorage(vm); case ALL_INT32_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure(vm)->needsSlowPutIndexing()); + ASSERT(!structure()->needsSlowPutIndexing()); return convertInt32ToArrayStorage(vm); case ALL_DOUBLE_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure(vm)->needsSlowPutIndexing()); + ASSERT(!structure()->needsSlowPutIndexing()); return convertDoubleToArrayStorage(vm); case ALL_CONTIGUOUS_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure(vm)->needsSlowPutIndexing()); + ASSERT(!structure()->needsSlowPutIndexing()); return convertContiguousToArrayStorage(vm); default: @@ -1105,7 +1099,7 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm) { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { createArrayStorage(vm, 0, 0); SparseArrayValueMap* map = allocateSparseIndexMap(vm); @@ -1126,7 +1120,7 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(V return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, convertContiguousToArrayStorage(vm)); case ALL_ARRAY_STORAGE_INDEXING_TYPES: - return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly.get(this)->arrayStorage()); + return enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(vm, m_butterfly->arrayStorage()); default: CRASH(); @@ -1136,7 +1130,7 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(V void JSObject::switchToSlowPutArrayStorage(VM& vm) { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_UNDECIDED_INDEXING_TYPES: convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage); break; @@ -1155,7 +1149,7 @@ void JSObject::switchToSlowPutArrayStorage(VM& vm) case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), SwitchToSlowPutArrayStorage); setStructure(vm, newStructure); break; } @@ -1172,7 +1166,7 @@ void JSObject::setPrototype(VM& vm, JSValue prototype) if (prototype.isObject()) vm.prototypeMap.addPrototype(asObject(prototype)); - Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype); + Structure* newStructure = Structure::changePrototypeTransition(vm, structure(), prototype); setStructure(vm, newStructure); if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses()) @@ -1183,10 +1177,10 @@ void JSObject::setPrototype(VM& vm, JSValue prototype) return; } - if (!hasIndexedProperties(indexingType())) + if (!hasIndexedProperties(structure()->indexingType())) return; - if (shouldUseSlowPut(indexingType())) + if (shouldUseSlowPut(structure()->indexingType())) return; switchToSlowPutArrayStorage(vm); @@ -1194,7 +1188,7 @@ void JSObject::setPrototype(VM& vm, JSValue prototype) bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype) { - ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this); + ASSERT(methodTable()->toThis(this, exec, NotStrictMode) == this); JSValue nextPrototype = prototype; while (nextPrototype && nextPrototype.isObject()) { if (nextPrototype == this) @@ -1211,84 +1205,45 @@ bool JSObject::allowsAccessFrom(ExecState* exec) return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec); } -void JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter, unsigned attributes) -{ - PropertyDescriptor descriptor; - descriptor.setGetter(getter); - - ASSERT(attributes & Accessor); - if (!(attributes & ReadOnly)) - descriptor.setConfigurable(true); - if (!(attributes & DontEnum)) - descriptor.setEnumerable(true); - - defineOwnProperty(this, exec, propertyName, descriptor, false); -} - -void JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter, unsigned attributes) -{ - PropertyDescriptor descriptor; - descriptor.setSetter(setter); - - ASSERT(attributes & Accessor); - if (!(attributes & ReadOnly)) - descriptor.setConfigurable(true); - if (!(attributes & DontEnum)) - descriptor.setEnumerable(true); - - defineOwnProperty(this, exec, propertyName, descriptor, false); -} - void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) { ASSERT(value.isGetterSetter() && (attributes & Accessor)); - if (Optional index = parseIndex(propertyName)) { - putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect); + unsigned index = propertyName.asIndex(); + if (index != PropertyName::NotAnIndex) { + putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect); return; } putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes); } -void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) -{ - ASSERT(!parseIndex(propertyName)); - - PutPropertySlot slot(this); - putDirectInternal(vm, propertyName, value, attributes, slot); - - ASSERT(slot.type() == PutPropertySlot::NewProperty); - - Structure* structure = this->structure(vm); - if (attributes & ReadOnly) - structure->setContainsReadOnlyProperties(); - structure->setHasCustomGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto); -} - void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) { PutPropertySlot slot(this); - putDirectInternal(vm, propertyName, value, attributes, slot); + putDirectInternal(vm, propertyName, value, attributes, slot, getCallableObject(value)); + + // putDirect will change our Structure if we add a new property. For + // getters and setters, though, we also need to change our Structure + // if we override an existing non-getter or non-setter. + if (slot.type() != PutPropertySlot::NewProperty) + setStructure(vm, Structure::attributeChangeTransition(vm, structure(), propertyName, attributes)); - Structure* structure = this->structure(vm); if (attributes & ReadOnly) - structure->setContainsReadOnlyProperties(); + structure()->setContainsReadOnlyProperties(); - structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto); + structure()->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto); } -// HasProperty(O, P) from Section 7.3.10 of the spec. -// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasproperty bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty); + PropertySlot slot(this); return const_cast(this)->getPropertySlot(exec, propertyName, slot); } bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const { - PropertySlot slot(this, PropertySlot::InternalMethodType::HasProperty); + PropertySlot slot(this); return const_cast(this)->getPropertySlot(exec, propertyName, slot); } @@ -1296,36 +1251,40 @@ bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) { JSObject* thisObject = jsCast(cell); - VM& vm = exec->vm(); - if (Optional index = parseIndex(propertyName)) - return thisObject->methodTable(vm)->deletePropertyByIndex(thisObject, exec, index.value()); + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) + return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i); if (!thisObject->staticFunctionsReified()) - thisObject->reifyAllStaticProperties(exec); + thisObject->reifyStaticFunctionsForDelete(exec); unsigned attributes; - if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes))) { - if (attributes & DontDelete && !vm.isInDefineOwnProperty()) + JSCell* specificValue; + if (isValidOffset(thisObject->structure()->get(exec->vm(), propertyName, attributes, specificValue))) { + if (attributes & DontDelete && !exec->vm().isInDefineOwnProperty()) return false; - thisObject->removeDirect(vm, propertyName); + thisObject->removeDirect(exec->vm(), propertyName); + return true; + } + + // Look in the static hashtable of properties + const HashEntry* entry = thisObject->findPropertyHashEntry(exec, propertyName); + if (entry) { + if (entry->attributes() & DontDelete && !exec->vm().isInDefineOwnProperty()) + return false; // this builtin property can't be deleted + + PutPropertySlot slot(thisObject); + putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot); } return true; } -// HasOwnProperty(O, P) from section 7.3.11 in the spec. -// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-hasownproperty bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty); - return const_cast(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast(this), exec, propertyName, slot); -} - -bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const -{ - PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty); - return const_cast(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast(this), exec, propertyName, slot); + PropertySlot slot(this); + return const_cast(this)->methodTable()->getOwnPropertySlot(const_cast(this), exec, propertyName, slot); } bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) @@ -1333,9 +1292,9 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) JSObject* thisObject = jsCast(cell); if (i > MAX_ARRAY_INDEX) - return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i)); + return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); - switch (thisObject->indexingType()) { + switch (thisObject->structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return true; @@ -1353,12 +1312,12 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) Butterfly* butterfly = thisObject->butterfly(); if (i >= butterfly->vectorLength()) return true; - butterfly->contiguousDouble()[i] = PNaN; + butterfly->contiguousDouble()[i] = QNaN; return true; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = thisObject->m_butterfly.get(thisObject)->arrayStorage(); + ArrayStorage* storage = thisObject->m_butterfly->arrayStorage(); if (i < storage->vectorLength()) { WriteBarrier& valueSlot = storage->m_vector[i]; @@ -1408,7 +1367,7 @@ static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSO bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const { - result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber); + result = methodTable()->defaultValue(this, exec, PreferNumber); number = result.toNumber(exec); return !result.isString(); } @@ -1416,10 +1375,6 @@ bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& resu // ECMA 8.6.2.6 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint) { - // Make sure that whatever default value methods there are on object's prototype chain are - // being watched. - object->structure()->startWatchingInternalPropertiesIfNecessaryForEntireChain(exec->vm()); - // Must call toString first for Date objects. if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) { JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString); @@ -1442,51 +1397,28 @@ JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, Preferre return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value"))); } -const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const +const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, PropertyName propertyName) const { for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { - if (const HashTable* propHashTable = info->staticPropHashTable) { - if (const HashTableValue* entry = propHashTable->entry(propertyName)) + if (const HashTable* propHashTable = info->propHashTable(exec)) { + if (const HashEntry* entry = propHashTable->entry(exec, propertyName)) return entry; } } return 0; } -bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue) +bool JSObject::hasInstance(ExecState* exec, JSValue value) { - VM& vm = exec->vm(); - - if (!hasInstanceValue.isUndefinedOrNull() && hasInstanceValue != exec->lexicalGlobalObject()->functionProtoHasInstanceSymbolFunction()) { - CallData callData; - CallType callType = JSC::getCallData(hasInstanceValue, callData); - if (callType == CallTypeNone) { - vm.throwException(exec, createInvalidInstanceofParameterErrorhasInstanceValueNotFunction(exec, this)); - return false; - } - - MarkedArgumentBuffer args; - args.append(value); - JSValue result = call(exec, hasInstanceValue, callType, callData, this, args); - return result.toBoolean(exec); - } - - TypeInfo info = structure(vm)->typeInfo(); + TypeInfo info = structure()->typeInfo(); if (info.implementsDefaultHasInstance()) return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype)); if (info.implementsHasInstance()) - return methodTable(vm)->customHasInstance(this, exec, value); - vm.throwException(exec, createInvalidInstanceofParameterErrorNotFunction(exec, this)); + return methodTable()->customHasInstance(this, exec, value); + exec->vm().throwException(exec, createInvalidParameterError(exec, "instanceof" , this)); return false; } -bool JSObject::hasInstance(ExecState* exec, JSValue value) -{ - JSValue hasInstanceValue = get(exec, exec->propertyNames().hasInstanceSymbol); - - return hasInstance(exec, value, hasInstanceValue); -} - bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto) { if (!value.isObject()) @@ -1505,29 +1437,34 @@ bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto) return false; } -EncodedJSValue JSC_HOST_CALL objectPrivateFuncInstanceOf(ExecState* exec) +bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const { - JSValue value = exec->uncheckedArgument(0); - JSValue proto = exec->uncheckedArgument(1); + unsigned attributes; + if (isValidOffset(structure()->get(exec->vm(), propertyName, attributes, specificValue))) + return true; - return JSValue::encode(jsBoolean(JSObject::defaultHasInstance(exec, value, proto))); + // This could be a function within the static table? - should probably + // also look in the hash? This currently should not be a problem, since + // we've currently always call 'get' first, which should have populated + // the normal storage. + return false; } void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode); + propertyNames.setBaseObject(object); + object->methodTable()->getOwnPropertyNames(object, exec, propertyNames, mode); if (object->prototype().isNull()) return; - VM& vm = exec->vm(); JSObject* prototype = asObject(object->prototype()); while(1) { - if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) { - prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode); + if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { + prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode); break; } - prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode); + prototype->methodTable()->getOwnPropertyNames(prototype, exec, propertyNames, mode); JSValue nextProto = prototype->prototype(); if (nextProto.isNull()) break; @@ -1537,90 +1474,81 @@ void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameA void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - if (!mode.includeJSObjectProperties()) { - // We still have to get non-indexed properties from any subclasses of JSObject that have them. - object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); - return; + // Add numeric properties first. That appears to be the accepted convention. + // FIXME: Filling PropertyNameArray with an identifier for every integer + // is incredibly inefficient for large arrays. We need a different approach, + // which almost certainly means a different structure for PropertyNameArray. + switch (object->structure()->indexingType()) { + case ALL_BLANK_INDEXING_TYPES: + case ALL_UNDECIDED_INDEXING_TYPES: + break; + + case ALL_INT32_INDEXING_TYPES: + case ALL_CONTIGUOUS_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + if (!butterfly->contiguous()[i]) + continue; + propertyNames.add(Identifier::from(exec, i)); + } + break; } - - if (propertyNames.includeStringProperties()) { - // Add numeric properties first. That appears to be the accepted convention. - // FIXME: Filling PropertyNameArray with an identifier for every integer - // is incredibly inefficient for large arrays. We need a different approach, - // which almost certainly means a different structure for PropertyNameArray. - switch (object->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: - case ALL_UNDECIDED_INDEXING_TYPES: - break; - - case ALL_INT32_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = object->butterfly(); - unsigned usedLength = butterfly->publicLength(); - for (unsigned i = 0; i < usedLength; ++i) { - if (!butterfly->contiguous()[i]) - continue; - propertyNames.add(i); - } - break; + + case ALL_DOUBLE_INDEXING_TYPES: { + Butterfly* butterfly = object->butterfly(); + unsigned usedLength = butterfly->publicLength(); + for (unsigned i = 0; i < usedLength; ++i) { + double value = butterfly->contiguousDouble()[i]; + if (value != value) + continue; + propertyNames.add(Identifier::from(exec, i)); } - - case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = object->butterfly(); - unsigned usedLength = butterfly->publicLength(); - for (unsigned i = 0; i < usedLength; ++i) { - double value = butterfly->contiguousDouble()[i]; - if (value != value) - continue; - propertyNames.add(i); - } - break; + break; + } + + case ALL_ARRAY_STORAGE_INDEXING_TYPES: { + ArrayStorage* storage = object->m_butterfly->arrayStorage(); + + unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); + for (unsigned i = 0; i < usedVectorLength; ++i) { + if (storage->m_vector[i]) + propertyNames.add(Identifier::from(exec, i)); } + + if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { + Vector keys; + keys.reserveInitialCapacity(map->size()); - case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = object->m_butterfly.get(object)->arrayStorage(); - - unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); - for (unsigned i = 0; i < usedVectorLength; ++i) { - if (storage->m_vector[i]) - propertyNames.add(i); - } - - if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { - Vector keys; - keys.reserveInitialCapacity(map->size()); - - SparseArrayValueMap::const_iterator end = map->end(); - for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { - if (mode.includeDontEnumProperties() || !(it->value.attributes & DontEnum)) - keys.uncheckedAppend(static_cast(it->key)); - } - - std::sort(keys.begin(), keys.end()); - for (unsigned i = 0; i < keys.size(); ++i) - propertyNames.add(keys[i]); + SparseArrayValueMap::const_iterator end = map->end(); + for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) { + if (mode == IncludeDontEnumProperties || !(it->value.attributes & DontEnum)) + keys.uncheckedAppend(static_cast(it->key)); } - break; - } - default: - RELEASE_ASSERT_NOT_REACHED(); + std::sort(keys.begin(), keys.end()); + for (unsigned i = 0; i < keys.size(); ++i) + propertyNames.add(Identifier::from(exec, keys[i])); } + break; } - - object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + + object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); } void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - if (!object->staticFunctionsReified()) - getClassPropertyNames(exec, object->classInfo(), propertyNames, mode); + getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified()); - if (!mode.includeJSObjectProperties()) - return; - - VM& vm = exec->vm(); - object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode); + bool canCachePropertiesFromStructure = !propertyNames.size(); + object->structure()->getPropertyNamesFromStructure(exec->vm(), propertyNames, mode); + + if (canCachePropertiesFromStructure) + propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size()); } double JSObject::toNumber(ExecState* exec) const @@ -1648,27 +1576,28 @@ void JSObject::seal(VM& vm) { if (isSealed(vm)) return; - enterDictionaryIndexingMode(vm); - setStructure(vm, Structure::sealTransition(vm, structure(vm))); + preventExtensions(vm); + setStructure(vm, Structure::sealTransition(vm, structure())); } void JSObject::freeze(VM& vm) { if (isFrozen(vm)) return; - enterDictionaryIndexingMode(vm); - setStructure(vm, Structure::freezeTransition(vm, structure(vm))); + preventExtensions(vm); + setStructure(vm, Structure::freezeTransition(vm, structure())); } void JSObject::preventExtensions(VM& vm) { - if (!isExtensible()) - return; enterDictionaryIndexingMode(vm); - setStructure(vm, Structure::preventExtensionsTransition(vm, structure(vm))); + if (isExtensible()) + setStructure(vm, Structure::preventExtensionsTransition(vm, structure())); } -void JSObject::reifyAllStaticProperties(ExecState* exec) +// This presently will flatten to an uncachable dictionary; this is suitable +// for use in delete, we may want to do something different elsewhere. +void JSObject::reifyStaticFunctionsForDelete(ExecState* exec) { ASSERT(!staticFunctionsReified()); VM& vm = exec->vm(); @@ -1676,45 +1605,42 @@ void JSObject::reifyAllStaticProperties(ExecState* exec) // If this object's ClassInfo has no static properties, then nothing to reify! // We can safely set the flag to avoid the expensive check again in the future. if (!classInfo()->hasStaticProperties()) { - structure(vm)->setStaticFunctionsReified(true); + structure()->setStaticFunctionsReified(); return; } - if (!structure(vm)->isUncacheableDictionary()) - setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm))); + if (!structure()->isUncacheableDictionary()) + setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure())); for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { - const HashTable* hashTable = info->staticPropHashTable; + const HashTable* hashTable = info->propHashTable(globalObject()->globalExec()); if (!hashTable) continue; - - for (auto& value : *hashTable) { - unsigned attributes; - PropertyOffset offset = getDirectOffset(vm, Identifier::fromString(&vm, value.m_key), attributes); - if (!isValidOffset(offset)) - reifyStaticProperty(vm, value, *this); + PropertySlot slot(this); + for (HashTable::ConstIterator iter = hashTable->begin(vm); iter != hashTable->end(vm); ++iter) { + if (iter->attributes() & Function) + setUpStaticFunctionSlot(globalObject()->globalExec(), *iter, this, Identifier(&vm, iter->key()), slot); } } - structure(vm)->setStaticFunctionsReified(true); + structure()->setStaticFunctionsReified(); } bool JSObject::removeDirect(VM& vm, PropertyName propertyName) { - Structure* structure = this->structure(vm); - if (!isValidOffset(structure->get(vm, propertyName))) + if (!isValidOffset(structure()->get(vm, propertyName))) return false; PropertyOffset offset; - if (structure->isUncacheableDictionary()) { - offset = structure->removePropertyWithoutTransition(vm, propertyName); + if (structure()->isUncacheableDictionary()) { + offset = structure()->removePropertyWithoutTransition(vm, propertyName); if (offset == invalidOffset) return false; putDirectUndefined(offset); return true; } - setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset)); + setStructure(vm, Structure::removePropertyTransition(vm, structure(), propertyName, offset)); if (offset == invalidOffset) return false; putDirectUndefined(offset); @@ -1727,19 +1653,19 @@ NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue g slot.setGetterSlot(this, attributes, jsCast(getterSetter)); return; } + slot.setCacheableGetterSlot(this, attributes, jsCast(getterSetter), offset); } void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) { VM& vm = exec->vm(); - auto map = m_butterfly.get(this)->arrayStorage()->m_sparseMap.get(); if (descriptor.isDataDescriptor()) { if (descriptor.value()) - entryInMap->set(vm, map, descriptor.value()); + entryInMap->set(vm, this, descriptor.value()); else if (oldDescriptor.isAccessorDescriptor()) - entryInMap->set(vm, map, jsUndefined()); + entryInMap->set(vm, this, jsUndefined()); entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; return; } @@ -1756,13 +1682,13 @@ void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa else if (oldDescriptor.isAccessorDescriptor()) setter = oldDescriptor.setterObject(); - GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + GetterSetter* accessor = GetterSetter::create(vm); if (getter) - accessor->setGetter(vm, exec->lexicalGlobalObject(), getter); + accessor->setGetter(vm, getter); if (setter) - accessor->setSetter(vm, exec->lexicalGlobalObject(), setter); + accessor->setSetter(vm, setter); - entryInMap->set(vm, map, accessor); + entryInMap->set(vm, this, accessor); entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; return; } @@ -1775,13 +1701,13 @@ void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException) { ASSERT(index <= MAX_ARRAY_INDEX); - + if (!inSparseIndexingMode()) { // Fast case: we're putting a regular property to a regular array // FIXME: this will pessimistically assume that if attributes are missing then they'll default to false // however if the property currently exists missing attributes will override from their current 'true' // state (i.e. defineOwnProperty could be used to set a value without needing to entering 'SparseMode'). - if (!descriptor.attributes() && descriptor.value()) { + if (!descriptor.attributes()) { ASSERT(!descriptor.isAccessorDescriptor()); return putDirectIndex(exec, index, descriptor.value(), 0, throwException ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); } @@ -1792,7 +1718,7 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P if (descriptor.attributes() & (ReadOnly | Accessor)) notifyPresenceOfIndexedAccessors(exec->vm()); - SparseArrayValueMap* map = m_butterfly.get(this)->arrayStorage()->m_sparseMap.get(); + SparseArrayValueMap* map = m_butterfly->arrayStorage()->m_sparseMap.get(); RELEASE_ASSERT(map); // 1. Let current be the result of calling the [[GetOwnProperty]] internal method of O with property name P. @@ -1824,9 +1750,8 @@ bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const P entryInMap->get(defaults); putIndexedDescriptor(exec, entryInMap, descriptor, defaults); - Butterfly* butterfly = m_butterfly.get(this); - if (index >= butterfly->arrayStorage()->length()) - butterfly->arrayStorage()->setLength(index + 1); + if (index >= m_butterfly->arrayStorage()->length()) + m_butterfly->arrayStorage()->setLength(index + 1); return true; } @@ -1944,21 +1869,19 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value) { - ASSERT((indexingType() & IndexingShapeMask) == indexingShape); + ASSERT((structure()->indexingType() & IndexingShapeMask) == indexingShape); ASSERT(!indexingShouldBeSparse()); - - Butterfly* butterfly = m_butterfly.get(this); // For us to get here, the index is either greater than the public length, or greater than // or equal to the vector length. - ASSERT(i >= butterfly->vectorLength()); + ASSERT(i >= m_butterfly->vectorLength()); VM& vm = exec->vm(); - if (i > MAX_STORAGE_VECTOR_INDEX + if (i >= MAX_ARRAY_INDEX - 1 || (i >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(i, countElements(butterfly))) - || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) { + && !isDenseEnoughForVector(i, countElements(butterfly()))) + || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) { ASSERT(i <= MAX_ARRAY_INDEX); ensureArrayStorageSlow(vm); SparseArrayValueMap* map = allocateSparseIndexMap(vm); @@ -1969,25 +1892,24 @@ void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un } ensureLength(vm, i + 1); - butterfly = m_butterfly.get(this); - RELEASE_ASSERT(i < butterfly->vectorLength()); + RELEASE_ASSERT(i < m_butterfly->vectorLength()); switch (indexingShape) { case Int32Shape: ASSERT(value.isInt32()); - butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value); + m_butterfly->contiguousInt32()[i].setWithoutWriteBarrier(value); break; case DoubleShape: { ASSERT(value.isNumber()); double valueAsDouble = value.asNumber(); ASSERT(valueAsDouble == valueAsDouble); - butterfly->contiguousDouble()[i] = valueAsDouble; + m_butterfly->contiguousDouble()[i] = valueAsDouble; break; } case ContiguousShape: - butterfly->contiguous()[i].set(vm, this, value); + m_butterfly->contiguous()[i].set(vm, this, value); break; default: @@ -1995,11 +1917,6 @@ void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un } } -// Explicit instantiations needed by JSArray.cpp. -template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned, JSValue); -template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned, JSValue); -template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState*, unsigned, JSValue); - void JSObject::putByIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, unsigned i, JSValue value, bool shouldThrow, ArrayStorage* storage) { VM& vm = exec->vm(); @@ -2081,7 +1998,7 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue // i should be a valid array index that is outside of the current vector. ASSERT(i <= MAX_ARRAY_INDEX); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse()) { putByIndexBeyondVectorLengthWithArrayStorage( @@ -2094,10 +2011,10 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0)); break; } - if (structure(vm)->needsSlowPutIndexing()) { - // Convert the indexing type to the SlowPutArrayStorage and retry. - createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); - putByIndex(this, exec, i, value, shouldThrow); + if (structure()->needsSlowPutIndexing()) { + ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); + storage->m_vector[i].set(vm, this, value); + storage->m_numValuesInVector++; break; } @@ -2149,7 +2066,7 @@ bool JSObject::putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState* exec, VM& vm = exec->vm(); // i should be a valid array index that is outside of the current vector. - ASSERT(hasAnyArrayStorage(indexingType())); + ASSERT(hasArrayStorage(structure()->indexingType())); ASSERT(arrayStorage() == storage); ASSERT(i >= storage->vectorLength() || attributes); ASSERT(i <= MAX_ARRAY_INDEX); @@ -2231,7 +2148,7 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV if (attributes & (ReadOnly | Accessor)) notifyPresenceOfIndexedAccessors(vm); - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse() || attributes) { return putDirectIndexBeyondVectorLengthWithArrayStorage( @@ -2242,7 +2159,7 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV return putDirectIndexBeyondVectorLengthWithArrayStorage( exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0)); } - if (structure(vm)->needsSlowPutIndexing()) { + if (structure()->needsSlowPutIndexing()) { ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); storage->m_vector[i].set(vm, this, value); storage->m_numValuesInVector++; @@ -2260,10 +2177,9 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV } case ALL_INT32_INDEXING_TYPES: { - if (attributes) { - if (i < m_butterfly.get(this)->vectorLength()) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm)); + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertInt32ToArrayStorage(vm)); } if (!value.isInt32()) { convertInt32ForValue(vm, value); @@ -2274,10 +2190,9 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV } case ALL_DOUBLE_INDEXING_TYPES: { - if (attributes) { - if (i < m_butterfly.get(this)->vectorLength()) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm)); + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertDoubleToArrayStorage(vm)); } if (!value.isNumber()) { convertDoubleToContiguous(vm); @@ -2293,20 +2208,15 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV } case ALL_CONTIGUOUS_INDEXING_TYPES: { - if (attributes) { - if (i < m_butterfly.get(this)->vectorLength()) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm)); + if (attributes & (ReadOnly | Accessor)) { + return putDirectIndexBeyondVectorLengthWithArrayStorage( + exec, i, value, attributes, mode, convertContiguousToArrayStorage(vm)); } putByIndexBeyondVectorLengthWithoutAttributes(exec, i, value); return true; } case ALL_ARRAY_STORAGE_INDEXING_TYPES: - if (attributes) { - if (i < m_butterfly.get(this)->vectorLength()) - return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm)); - } return putDirectIndexBeyondVectorLengthWithArrayStorage(exec, i, value, attributes, mode, arrayStorage()); default: @@ -2315,14 +2225,6 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV } } -void JSObject::putDirectNativeIntrinsicGetter(VM& vm, JSGlobalObject* globalObject, Identifier name, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) -{ - GetterSetter* accessor = GetterSetter::create(vm, globalObject); - JSFunction* function = JSFunction::create(vm, globalObject, 0, name.string(), nativeFunction, intrinsic); - accessor->setGetter(vm, globalObject, function); - putDirectNonIndexAccessor(vm, name, accessor, attributes); -} - void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) { StringImpl* name = propertyName.publicName(); @@ -2334,30 +2236,11 @@ void JSObject::putDirectNativeFunction(VM& vm, JSGlobalObject* globalObject, con putDirect(vm, propertyName, function, attributes); } -JSFunction* JSObject::putDirectBuiltinFunction(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes) -{ - StringImpl* name = propertyName.publicName(); - if (!name) - name = vm.propertyNames->anonymous.impl(); - ASSERT(name); - JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast(functionExecutable), globalObject); - putDirect(vm, propertyName, function, attributes); - return function; -} - -JSFunction* JSObject::putDirectBuiltinFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, FunctionExecutable* functionExecutable, unsigned attributes) -{ - JSFunction* function = JSFunction::createBuiltinFunction(vm, static_cast(functionExecutable), globalObject); - putDirectWithoutTransition(vm, propertyName, function, attributes); - return function; -} - void JSObject::putDirectNativeFunctionWithoutTransition(VM& vm, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) { StringImpl* name = propertyName.publicName(); - if (!name) - name = vm.propertyNames->anonymous.impl(); ASSERT(name); + JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic); putDirectWithoutTransition(vm, propertyName, function, attributes); } @@ -2389,9 +2272,9 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) unsigned vectorLength; unsigned length; - if (hasIndexedProperties(indexingType())) { - vectorLength = m_butterfly.get(this)->vectorLength(); - length = m_butterfly.get(this)->publicLength(); + if (hasIndexedProperties(structure()->indexingType())) { + vectorLength = m_butterfly->vectorLength(); + length = m_butterfly->publicLength(); } else { vectorLength = 0; length = 0; @@ -2428,7 +2311,7 @@ unsigned JSObject::countElements(Butterfly* butterfly) unsigned JSObject::countElements() { - switch (indexingType()) { + switch (structure()->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return 0; @@ -2467,11 +2350,10 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength) unsigned newVectorLength = getNewVectorLength(newLength); // Fast case - there is no precapacity. In these cases a realloc makes sense. - Structure* structure = this->structure(vm); if (LIKELY(!indexBias)) { DeferGC deferGC(vm.heap); Butterfly* newButterfly = storage->butterfly()->growArrayRight( - vm, this, structure, structure->outOfLineCapacity(), true, + vm, this, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); if (!newButterfly) return false; @@ -2485,7 +2367,7 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength) unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); Butterfly* newButterfly = storage->butterfly()->resizeArray( vm, this, - structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), + structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), newIndexBias, true, ArrayStorage::sizeFor(newVectorLength)); if (!newButterfly) return false; @@ -2497,108 +2379,49 @@ bool JSObject::increaseVectorLength(VM& vm, unsigned newLength) void JSObject::ensureLengthSlow(VM& vm, unsigned length) { - Butterfly* butterfly = m_butterfly.get(this); - ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); - ASSERT(length > butterfly->vectorLength()); + ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); + ASSERT(length > m_butterfly->vectorLength()); unsigned newVectorLength = std::min( length << 1, MAX_STORAGE_VECTOR_LENGTH); - unsigned oldVectorLength = butterfly->vectorLength(); + unsigned oldVectorLength = m_butterfly->vectorLength(); DeferGC deferGC(vm.heap); - butterfly = butterfly->growArrayRight( + m_butterfly.set(vm, this, m_butterfly->growArrayRight( vm, this, structure(), structure()->outOfLineCapacity(), true, oldVectorLength * sizeof(EncodedJSValue), - newVectorLength * sizeof(EncodedJSValue)); - m_butterfly.set(vm, this, butterfly); + newVectorLength * sizeof(EncodedJSValue))); - butterfly->setVectorLength(newVectorLength); + m_butterfly->setVectorLength(newVectorLength); - if (hasDouble(indexingType())) { + if (hasDouble(structure()->indexingType())) { for (unsigned i = oldVectorLength; i < newVectorLength; ++i) - butterfly->contiguousDouble().data()[i] = PNaN; + m_butterfly->contiguousDouble().data()[i] = QNaN; } } -void JSObject::reallocateAndShrinkButterfly(VM& vm, unsigned length) -{ - ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(length < MAX_STORAGE_VECTOR_LENGTH); - ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); - ASSERT(m_butterfly.get(this)->vectorLength() > length); - ASSERT(!m_butterfly.get(this)->indexingHeader()->preCapacity(structure())); - - DeferGC deferGC(vm.heap); - Butterfly* newButterfly = m_butterfly.get(this)->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length)); - m_butterfly.set(vm, this, newButterfly); - newButterfly->setVectorLength(length); - newButterfly->setPublicLength(length); -} - Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize) { ASSERT(newSize > oldSize); // It's important that this function not rely on structure(), for the property // capacity, since we might have already mutated the structure in-place. - - return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(this), vm, this, structure(vm), oldSize, newSize); -} - -static JSBoundSlotBaseFunction* getBoundSlotBaseFunctionForGetterSetter(ExecState* exec, PropertyName propertyName, JSC::PropertySlot& slot, CustomGetterSetter* getterSetter, JSBoundSlotBaseFunction::Type type) -{ - auto key = std::make_pair(getterSetter, (int)type); - JSBoundSlotBaseFunction* boundSlotBase = exec->vm().customGetterSetterFunctionMap.get(key); - if (!boundSlotBase) { - boundSlotBase = JSBoundSlotBaseFunction::create(exec->vm(), exec->lexicalGlobalObject(), slot.slotBase(), getterSetter, type, propertyName.publicName()); - exec->vm().customGetterSetterFunctionMap.set(key, boundSlotBase); - } - return boundSlotBase; + + return m_butterfly->growPropertyStorage(vm, this, structure(), oldSize, newSize); } bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) { - JSC::PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty); - if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot)) + JSC::PropertySlot slot(this); + if (!methodTable()->getOwnPropertySlot(this, exec, propertyName, slot)) + return false; + /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */ + if (slot.slotBase() != this && slot.slotBase() && slot.slotBase()->methodTable()->toThis(slot.slotBase(), exec, NotStrictMode) != this) return false; - - // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain - // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that - // the property is not an own property. - if (slot.slotBase() != this && slot.slotBase()) { - JSProxy* jsProxy = jsDynamicCast(this); - if (!jsProxy || jsProxy->target() != slot.slotBase()) { - // Try ProxyObject. - ProxyObject* proxyObject = jsDynamicCast(this); - if (!proxyObject || proxyObject->target() != slot.slotBase()) - return false; - } - } - if (slot.isAccessor()) descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes()); - else if (slot.attributes() & CustomAccessor) { - descriptor.setCustomDescriptor(slot.attributes()); - - JSObject* thisObject = this; - if (auto* proxy = jsDynamicCast(this)) - thisObject = proxy->target(); - - JSValue maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName); - if (!maybeGetterSetter) { - thisObject->reifyAllStaticProperties(exec); - maybeGetterSetter = thisObject->getDirect(exec->vm(), propertyName); - } - - ASSERT(maybeGetterSetter); - auto* getterSetter = jsCast(maybeGetterSetter); - if (getterSetter->getter()) - descriptor.setGetter(getBoundSlotBaseFunctionForGetterSetter(exec, propertyName, slot, getterSetter, JSBoundSlotBaseFunction::Type::Getter)); - if (getterSetter->setter()) - descriptor.setSetter(getBoundSlotBaseFunctionForGetterSetter(exec, propertyName, slot, getterSetter, JSBoundSlotBaseFunction::Type::Setter)); - } else + else descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes()); return true; } @@ -2608,11 +2431,11 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper VM& vm = exec->vm(); if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { - GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + GetterSetter* accessor = GetterSetter::create(vm); if (oldDescriptor.getterPresent()) - accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject()); + accessor->setGetter(vm, oldDescriptor.getterObject()); if (oldDescriptor.setterPresent()) - accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject()); + accessor->setSetter(vm, oldDescriptor.setterObject()); target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); return true; } @@ -2623,20 +2446,20 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper newValue = oldDescriptor.value(); target->putDirect(vm, propertyName, newValue, attributes & ~Accessor); if (attributes & ReadOnly) - target->structure(vm)->setContainsReadOnlyProperties(); + target->structure()->setContainsReadOnlyProperties(); return true; } attributes &= ~ReadOnly; - GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject()); + GetterSetter* accessor = GetterSetter::create(vm); if (descriptor.getterPresent()) - accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject()); + accessor->setGetter(vm, descriptor.getterObject()); else if (oldDescriptor.getterPresent()) - accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject()); + accessor->setGetter(vm, oldDescriptor.getterObject()); if (descriptor.setterPresent()) - accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject()); + accessor->setSetter(vm, descriptor.setterObject()); else if (oldDescriptor.setterPresent()) - accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject()); + accessor->setSetter(vm, oldDescriptor.setterObject()); target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); return true; @@ -2644,10 +2467,11 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value) { - if (Optional index = parseIndex(propertyName)) - putDirectIndex(exec, index.value(), value); - else + unsigned asIndex = propertyName.asIndex(); + if (asIndex == PropertyName::NotAnIndex) putDirect(exec->vm(), propertyName, value); + else + putDirectIndex(exec, asIndex, value); } class DefineOwnPropertyScope { @@ -2667,43 +2491,39 @@ private: VM& m_vm; }; - -// 9.1.6.3 of the spec -// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-validateandapplypropertydescriptor -bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, PropertyName propertyName, bool isExtensible, - const PropertyDescriptor& descriptor, bool isCurrentDefined, const PropertyDescriptor& current, bool throwException) +bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) { + // Track on the globaldata that we're in define property. + // Currently DefineOwnProperty uses delete to remove properties when they are being replaced + // (particularly when changing attributes), however delete won't allow non-configurable (i.e. + // DontDelete) properties to be deleted. For now, we can use this flag to make this work. + DefineOwnPropertyScope scope(exec); + // If we have a new property we can just put it on normally - // Step 2. - if (!isCurrentDefined) { + PropertyDescriptor current; + if (!getOwnPropertyDescriptor(exec, propertyName, current)) { // unless extensions are prevented! - // Step 2.a - if (!isExtensible) { + if (!isExtensible()) { if (throwException) exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); return false; } - if (!object) - return true; - // Step 2.c/d PropertyDescriptor oldDescriptor; oldDescriptor.setValue(jsUndefined()); - // FIXME: spec says to always return true here. - return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); } - // Step 3. + if (descriptor.isEmpty()) return true; - // Step 4. + if (current.equalTo(exec, descriptor)) return true; - // Step 5. // Filter out invalid changes if (!current.configurable()) { if (descriptor.configurable()) { if (throwException) - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to configurable attribute of unconfigurable property."))); return false; } if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { @@ -2712,18 +2532,16 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope return false; } } - - // Step 6. + // A generic descriptor is simply changing the attributes of an existing property if (descriptor.isGenericDescriptor()) { - if (!current.attributesEqual(descriptor) && object) { - object->methodTable(exec->vm())->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + if (!current.attributesEqual(descriptor)) { + methodTable()->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } return true; } - - // Step 7. + // Changing between a normal property or an accessor property if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { if (!current.configurable()) { @@ -2731,15 +2549,10 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); return false; } - - if (!object) - return true; - - object->methodTable(exec->vm())->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + methodTable()->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } - // Step 8. // Changing the value and attributes of an existing property if (descriptor.isDataDescriptor()) { if (!current.configurable()) { @@ -2758,13 +2571,10 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope } if (current.attributesEqual(descriptor) && !descriptor.value()) return true; - if (!object) - return true; - object->methodTable(exec->vm())->deleteProperty(object, exec, propertyName); - return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); + methodTable()->deleteProperty(this, exec, propertyName); + return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } - // Step 9. // Changing the accessor functions of an existing accessor property ASSERT(descriptor.isAccessorDescriptor()); if (!current.configurable()) { @@ -2778,197 +2588,51 @@ bool validateAndApplyPropertyDescriptor(ExecState* exec, JSObject* object, Prope exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the getter of an unconfigurable property."))); return false; } - if (current.attributes() & CustomAccessor) { - if (throwException) - exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); - return false; - } } - - // Step 10/11. - if (!object) - return true; - JSValue accessor = object->getDirect(exec->vm(), propertyName); + JSValue accessor = getDirect(exec->vm(), propertyName); if (!accessor) return false; - GetterSetter* getterSetter; - bool getterSetterChanged = false; - if (accessor.isCustomGetterSetter()) - getterSetter = GetterSetter::create(exec->vm(), exec->lexicalGlobalObject()); - else { - ASSERT(accessor.isGetterSetter()); - getterSetter = asGetterSetter(accessor); - } - if (descriptor.setterPresent()) { - getterSetter = getterSetter->withSetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.setterObject()); - getterSetterChanged = true; - } - if (descriptor.getterPresent()) { - getterSetter = getterSetter->withGetter(exec->vm(), exec->lexicalGlobalObject(), descriptor.getterObject()); - getterSetterChanged = true; - } - if (current.attributesEqual(descriptor) && !getterSetterChanged) + GetterSetter* getterSetter = asGetterSetter(accessor); + if (descriptor.setterPresent()) + getterSetter->setSetter(exec->vm(), descriptor.setterObject()); + if (descriptor.getterPresent()) + getterSetter->setGetter(exec->vm(), descriptor.getterObject()); + if (current.attributesEqual(descriptor)) return true; - object->methodTable(exec->vm())->deleteProperty(object, exec, propertyName); + methodTable()->deleteProperty(this, exec, propertyName); unsigned attrs = descriptor.attributesOverridingCurrent(current); - object->putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); + putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); return true; } -bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) -{ - // Track on the globaldata that we're in define property. - // Currently DefineOwnProperty uses delete to remove properties when they are being replaced - // (particularly when changing attributes), however delete won't allow non-configurable (i.e. - // DontDelete) properties to be deleted. For now, we can use this flag to make this work. - DefineOwnPropertyScope scope(exec); - PropertyDescriptor current; - bool isCurrentDefined = getOwnPropertyDescriptor(exec, propertyName, current); - return validateAndApplyPropertyDescriptor(exec, this, propertyName, isExtensible(), descriptor, isCurrentDefined, current, throwException); -} - bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) { // If it's an array index, then use the indexed property storage. - if (Optional index = parseIndex(propertyName)) { + unsigned index = propertyName.asIndex(); + if (index != PropertyName::NotAnIndex) { // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments. // d. Reject if succeeded is false. // e. If index >= oldLen // e.i. Set oldLenDesc.[[Value]] to index + 1. // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. // f. Return true. - return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException); + return object->defineOwnIndexedProperty(exec, index, descriptor, throwException); } return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); } -JSObject* throwTypeError(ExecState* exec, const String& message) -{ - return exec->vm().throwException(exec, createTypeError(exec, message)); -} - -void JSObject::convertToDictionary(VM& vm) -{ - DeferredStructureTransitionWatchpointFire deferredWatchpointFire; - setStructure( - vm, Structure::toCacheableDictionaryTransition(vm, structure(vm), &deferredWatchpointFire)); -} - -void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter) -{ - Butterfly* butterfly = this->butterfly(); - size_t preCapacity = this->butterflyPreCapacity(); - void* currentBase = butterfly->base(preCapacity, outOfLineCapacityAfter); - void* newBase = butterfly->base(preCapacity, outOfLineCapacityBefore); - - memmove(newBase, currentBase, this->butterflyTotalSize()); - setButterflyWithoutChangingStructure(vm, Butterfly::fromBase(newBase, preCapacity, outOfLineCapacityAfter)); -} - -uint32_t JSObject::getEnumerableLength(ExecState* exec, JSObject* object) -{ - VM& vm = exec->vm(); - Structure* structure = object->structure(vm); - if (structure->holesMustForwardToPrototype(vm)) - return 0; - switch (object->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: - case ALL_UNDECIDED_INDEXING_TYPES: - return 0; - - case ALL_INT32_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = object->butterfly(); - unsigned usedLength = butterfly->publicLength(); - for (unsigned i = 0; i < usedLength; ++i) { - if (!butterfly->contiguous()[i]) - return 0; - } - return usedLength; - } - - case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = object->butterfly(); - unsigned usedLength = butterfly->publicLength(); - for (unsigned i = 0; i < usedLength; ++i) { - double value = butterfly->contiguousDouble()[i]; - if (value != value) - return 0; - } - return usedLength; - } - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = object->m_butterfly.get(object)->arrayStorage(); - if (storage->m_sparseMap.get()) - return 0; - - unsigned usedVectorLength = std::min(storage->length(), storage->vectorLength()); - for (unsigned i = 0; i < usedVectorLength; ++i) { - if (!storage->m_vector[i]) - return 0; - } - return usedVectorLength; - } - - default: - RELEASE_ASSERT_NOT_REACHED(); - return 0; - } -} - -void JSObject::getStructurePropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - VM& vm = exec->vm(); - object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode); -} - -void JSObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) -{ - VM& vm = exec->vm(); - object->methodTable(vm)->getOwnPropertyNames(object, exec, propertyNames, EnumerationMode(mode, JSObjectPropertiesMode::Exclude)); - - if (object->prototype().isNull()) - return; - - JSObject* prototype = asObject(object->prototype()); - while (true) { - if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) { - prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode); - break; - } - prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode); - JSValue nextProto = prototype->prototype(); - if (nextProto.isNull()) - break; - prototype = asObject(nextProto); - } + unsigned i = propertyName.asIndex(); + if (i != PropertyName::NotAnIndex) + return getOwnPropertySlotByIndex(this, exec, i, slot); + return false; } -// Implements GetMethod(O, P) in section 7.3.9 of the spec. -// http://www.ecma-international.org/ecma-262/6.0/index.html#sec-getmethod -JSValue JSObject::getMethod(ExecState* exec, CallData& callData, CallType& callType, const Identifier& ident, const String& errorMessage) +JSObject* throwTypeError(ExecState* exec, const String& message) { - JSValue method = get(exec, ident); - if (exec->hadException()) - return jsUndefined(); - - if (!method.isCell()) { - if (method.isUndefinedOrNull()) - return jsUndefined(); - - throwVMTypeError(exec, errorMessage); - return jsUndefined(); - } - - callType = method.asCell()->methodTable()->getCallData(method.asCell(), callData); - if (callType == CallTypeNone) { - throwVMTypeError(exec, errorMessage); - return jsUndefined(); - } - - return method; + return exec->vm().throwException(exec, createTypeError(exec, message)); } } // namespace JSC -- cgit v1.2.1