X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4be4e30906bcb8ee30b4d189205cb70bad6707ce..81345200c95645a1b0d2635520f96ad55dfde63f:/runtime/JSObject.cpp diff --git a/runtime/JSObject.cpp b/runtime/JSObject.cpp index d18b4e2..4bd6a6e 100644 --- a/runtime/JSObject.cpp +++ b/runtime/JSObject.cpp @@ -28,6 +28,7 @@ #include "CopiedSpaceInlines.h" #include "CopyVisitor.h" #include "CopyVisitorInlines.h" +#include "CustomGetterSetter.h" #include "DatePrototype.h" #include "ErrorConstructor.h" #include "Executable.h" @@ -39,7 +40,7 @@ #include "NativeErrorConstructor.h" #include "Nodes.h" #include "ObjectPrototype.h" -#include "Operations.h" +#include "JSCInlines.h" #include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "Reject.h" @@ -57,16 +58,15 @@ static unsigned lastArraySize = 0; JSCell* getCallableObjectSlow(JSCell* cell) { - Structure* structure = cell->structure(); - if (structure->typeInfo().type() == JSFunctionType) + if (cell->type() == JSFunctionType) return cell; - if (structure->classInfo()->isSubClassOf(&InternalFunction::s_info)) + if (cell->structure()->classInfo()->isSubClassOf(InternalFunction::info())) return cell; return 0; } -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject); -ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSObject); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSFinalObject); const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; @@ -76,19 +76,17 @@ const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_ 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->propHashTable(exec); + const HashTable* table = classInfo->propHashTable(vm); if (!table) continue; - 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()); + + for (auto iter = table->begin(vm); iter != table->end(vm); ++iter) { + if ((!(iter->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)) && !((iter->attributes() & BuiltinOrFunction) && didReify)) + propertyNames.add(Identifier(&vm, iter.key())); } } } @@ -102,7 +100,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt size_t propertyCapacity = structure->outOfLineCapacity(); size_t preCapacity; size_t indexingPayloadSizeInBytes; - bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); + bool hasIndexingHeader = this->hasIndexingHeader(); if (UNLIKELY(hasIndexingHeader)) { preCapacity = butterfly->indexingHeader()->preCapacity(structure); indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); @@ -129,7 +127,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt WriteBarrier* currentSource; size_t count; - switch (structure->indexingType()) { + switch (this->indexingType()) { case ALL_UNDECIDED_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -148,8 +146,8 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt count = newButterfly->arrayStorage()->vectorLength(); break; } + default: - CRASH(); currentTarget = 0; currentSource = 0; count = 0; @@ -159,7 +157,7 @@ ALWAYS_INLINE void JSObject::copyButterfly(CopyVisitor& visitor, Butterfly* butt memcpy(currentTarget, currentSource, count * sizeof(EncodedJSValue)); } - m_butterfly = newButterfly; + m_butterfly.setWithoutWriteBarrier(newButterfly); visitor.didCopy(butterfly->base(preCapacity, propertyCapacity), capacityInBytes); } } @@ -168,12 +166,12 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but { ASSERT(butterfly); - Structure* structure = this->structure(); + Structure* structure = this->structure(visitor.vm()); size_t propertyCapacity = structure->outOfLineCapacity(); size_t preCapacity; size_t indexingPayloadSizeInBytes; - bool hasIndexingHeader = JSC::hasIndexingHeader(structure->indexingType()); + bool hasIndexingHeader = this->hasIndexingHeader(); if (UNLIKELY(hasIndexingHeader)) { preCapacity = butterfly->indexingHeader()->preCapacity(structure); indexingPayloadSizeInBytes = butterfly->indexingHeader()->indexingPayloadSizeInBytes(structure); @@ -185,10 +183,12 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but // Mark the properties. visitor.appendValues(butterfly->propertyStorage() - storageSize, storageSize); - visitor.copyLater(this, butterfly->base(preCapacity, propertyCapacity), capacityInBytes); + visitor.copyLater( + this, ButterflyCopyToken, + butterfly->base(preCapacity, propertyCapacity), capacityInBytes); // Mark the array if appropriate. - switch (structure->indexingType()) { + switch (this->indexingType()) { case ALL_CONTIGUOUS_INDEXING_TYPES: visitor.appendValues(butterfly->contiguous().data(), butterfly->publicLength()); break; @@ -205,7 +205,7 @@ ALWAYS_INLINE void JSObject::visitButterfly(SlotVisitor& visitor, Butterfly* but void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSObject* thisObject = jsCast(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); #if !ASSERT_DISABLED bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; visitor.m_isCheckingForDefaultMarkViolation = false; @@ -215,17 +215,20 @@ void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) Butterfly* butterfly = thisObject->butterfly(); if (butterfly) - thisObject->visitButterfly(visitor, butterfly, thisObject->structure()->outOfLineSize()); + thisObject->visitButterfly(visitor, butterfly, thisObject->structure(visitor.vm())->outOfLineSize()); #if !ASSERT_DISABLED visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; #endif } -void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor) +void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor, CopyToken token) { JSObject* thisObject = jsCast(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + if (token != ButterflyCopyToken) + return; Butterfly* butterfly = thisObject->butterfly(); if (butterfly) @@ -235,7 +238,7 @@ void JSObject::copyBackingStore(JSCell* cell, CopyVisitor& visitor) void JSFinalObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSFinalObject* thisObject = jsCast(cell); - ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); #if !ASSERT_DISABLED bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; visitor.m_isCheckingForDefaultMarkViolation = false; @@ -243,11 +246,12 @@ 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, thisObject->structure()->outOfLineSize()); + thisObject->visitButterfly(visitor, butterfly, structure->outOfLineSize()); - size_t storageSize = thisObject->structure()->inlineSize(); + size_t storageSize = structure->inlineSize(); visitor.appendValues(thisObject->inlineStorage(), storageSize); #if !ASSERT_DISABLED @@ -262,31 +266,29 @@ String JSObject::className(const JSObject* object) return info->className; } -bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned i, PropertySlot& slot) +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 // legal for anyone to override getOwnPropertySlot() without also overriding // getOwnPropertySlotByIndex(). - JSObject* thisObject = jsCast(cell); - if (i > MAX_ARRAY_INDEX) - return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); + return thisObject->methodTable(exec->vm())->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, i), slot); - switch (thisObject->structure()->indexingType()) { + switch (thisObject->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: break; case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (i >= butterfly->vectorLength()) return false; JSValue value = butterfly->contiguous()[i].get(); if (value) { - slot.setValue(value); + slot.setValue(thisObject, None, value); return true; } @@ -294,13 +296,13 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned } case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (i >= butterfly->vectorLength()) return false; double value = butterfly->contiguousDouble()[i]; if (value == value) { - slot.setValue(JSValue(JSValue::EncodeAsDouble, value)); + slot.setValue(thisObject, None, JSValue(JSValue::EncodeAsDouble, value)); return true; } @@ -315,13 +317,13 @@ bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned if (i < storage->vectorLength()) { JSValue value = storage->m_vector[i].get(); if (value) { - slot.setValue(value); + slot.setValue(thisObject, None, value); return true; } } else if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { SparseArrayValueMap::iterator it = map->find(i); if (it != map->notFound()) { - it->value.get(slot); + it->value.get(thisObject, slot); return true; } } @@ -355,10 +357,10 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV // 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)) { + for (JSObject* obj = thisObject; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { - ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName)); + ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName)); if (!thisObject->putDirectInternal(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); @@ -371,48 +373,46 @@ void JSObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSV for (obj = thisObject; ; obj = asObject(prototype)) { unsigned attributes; JSCell* specificValue; - PropertyOffset offset = obj->structure()->get(vm, propertyName, attributes, specificValue); + PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes, specificValue); if (isValidOffset(offset)) { if (attributes & ReadOnly) { - ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); + ASSERT(thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); if (slot.isStrictMode()) - throwError(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError))); return; } JSValue gs = obj->getDirect(offset); if (gs.isGetterSetter()) { - ASSERT(attributes & Accessor); - ASSERT(thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); - JSObject* setterFunc = asGetterSetter(gs)->setter(); - if (!setterFunc) { - if (slot.isStrictMode()) - throwError(exec, createTypeError(exec, ASCIILiteral("setting a property that has only a getter"))); - return; - } - - CallData callData; - CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData); - MarkedArgumentBuffer args; - args.append(value); - - // If this is WebCore's global object then we need to substitute the shell. - call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), args); + callSetter(exec, cell, gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode); + if (!thisObject->structure()->isDictionary()) + slot.setCacheableSetter(obj, offset); return; - } else - ASSERT(!(attributes & Accessor)); + } + if (gs.isCustomGetterSetter()) { + callCustomSetter(exec, gs, obj, slot.thisValue(), value); + slot.setCustomProperty(obj, jsCast(gs.asCell())->setter()); + return; + } + 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; } - + const ClassInfo* info = obj->classInfo(); + if (info->hasStaticSetterOrReadonlyProperties(vm)) { + if (const HashTableValue* entry = obj->findPropertyHashEntry(vm, propertyName)) { + putEntry(exec, entry, obj, propertyName, value, slot); + return; + } + } prototype = obj->prototype(); if (prototype.isNull()) break; } - ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); + ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject); if (!thisObject->putDirectInternal(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode()) throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError)); return; @@ -423,12 +423,12 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSObject* thisObject = jsCast(cell); if (propertyName > MAX_ARRAY_INDEX) { - PutPropertySlot slot(shouldThrow); + PutPropertySlot slot(cell, shouldThrow); thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); return; } - switch (thisObject->structure()->indexingType()) { + switch (thisObject->indexingType()) { case ALL_BLANK_INDEXING_TYPES: break; @@ -445,11 +445,11 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, putByIndex(cell, exec, propertyName, value, shouldThrow); return; } - // Fall through. + FALLTHROUGH; } case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (propertyName >= butterfly->vectorLength()) break; butterfly->contiguous()[propertyName].set(exec->vm(), thisObject, value); @@ -472,7 +472,7 @@ void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, putByIndex(cell, exec, propertyName, value, shouldThrow); return; } - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (propertyName >= butterfly->vectorLength()) break; butterfly->contiguousDouble()[propertyName] = valueAsDouble; @@ -558,20 +558,20 @@ ArrayStorage* JSObject::enterDictionaryIndexingModeWhenArrayStorageAlreadyExists map->add(this, i).iterator->value.set(vm, this, value); } - Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, structure(), 0, ArrayStorage::sizeFor(0)); + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = storage->butterfly()->resizeArray(vm, this, structure(vm), 0, ArrayStorage::sizeFor(0)); RELEASE_ASSERT(newButterfly); - - m_butterfly = newButterfly; newButterfly->arrayStorage()->m_indexBias = 0; newButterfly->arrayStorage()->setVectorLength(0); newButterfly->arrayStorage()->m_sparseMap.set(vm, this, map); + setButterflyWithoutChangingStructure(vm, newButterfly); return newButterfly->arrayStorage(); } void JSObject::enterDictionaryIndexingMode(VM& vm) { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: case ALL_INT32_INDEXING_TYPES: @@ -595,7 +595,7 @@ void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) if (mayInterceptIndexedAccesses()) return; - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AddIndexedAccessors), m_butterfly); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors)); if (!vm.prototypeMap.isPrototype(this)) return; @@ -606,13 +606,13 @@ void JSObject::notifyPresenceOfIndexedAccessors(VM& vm) Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t elementSize) { ASSERT(length < MAX_ARRAY_INDEX); - IndexingType oldType = structure()->indexingType(); + IndexingType oldType = 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, - vm, structure(), structure()->outOfLineCapacity(), false, 0, + Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( + m_butterfly.get(), vm, this, structure(), structure()->outOfLineCapacity(), false, 0, elementSize * vectorLength); newButterfly->setPublicLength(length); newButterfly->setVectorLength(vectorLength); @@ -621,44 +621,50 @@ Butterfly* JSObject::createInitialIndexedStorage(VM& vm, unsigned length, size_t Butterfly* JSObject::createInitialUndecided(VM& vm, unsigned length) { + DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateUndecided); - setButterfly(vm, newButterfly, newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided); + setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly; } ContiguousJSValues JSObject::createInitialInt32(VM& vm, unsigned length) { + DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateInt32); - setButterfly(vm, newButterfly, newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32); + setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguousInt32(); } 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] = QNaN; - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateDouble); - setButterfly(vm, newButterfly, newStructure); + newButterfly->contiguousDouble()[i] = PNaN; + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble); + setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguousDouble(); } ContiguousJSValues JSObject::createInitialContiguous(VM& vm, unsigned length) { + DeferGC deferGC(vm.heap); Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), AllocateContiguous); - setButterfly(vm, newButterfly, newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous); + setStructureAndButterfly(vm, newStructure, newButterfly); return newButterfly->contiguous(); } ArrayStorage* JSObject::createArrayStorage(VM& vm, unsigned length, unsigned vectorLength) { - IndexingType oldType = structure()->indexingType(); + DeferGC deferGC(vm.heap); + Structure* structure = this->structure(vm); + IndexingType oldType = indexingType(); ASSERT_UNUSED(oldType, !hasIndexedProperties(oldType)); - Butterfly* newButterfly = Butterfly::createOrGrowArrayRight(m_butterfly, - vm, structure(), structure()->outOfLineCapacity(), false, 0, + Butterfly* newButterfly = Butterfly::createOrGrowArrayRight( + m_butterfly.get(), vm, this, structure, structure->outOfLineCapacity(), false, 0, ArrayStorage::sizeFor(vectorLength)); RELEASE_ASSERT(newButterfly); @@ -668,8 +674,8 @@ 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()); - setButterfly(vm, newButterfly, newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure, structure->suggestedArrayStorageTransition()); + setStructureAndButterfly(vm, newStructure, newButterfly); return result; } @@ -680,37 +686,38 @@ ArrayStorage* JSObject::createInitialArrayStorage(VM& vm) ContiguousJSValues JSObject::convertUndecidedToInt32(VM& vm) { - ASSERT(hasUndecided(structure()->indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateInt32), m_butterfly); + ASSERT(hasUndecided(indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32)); return m_butterfly->contiguousInt32(); } ContiguousDoubles JSObject::convertUndecidedToDouble(VM& vm) { - ASSERT(hasUndecided(structure()->indexingType())); + ASSERT(hasUndecided(indexingType())); for (unsigned i = m_butterfly->vectorLength(); i--;) - m_butterfly->contiguousDouble()[i] = QNaN; + m_butterfly->contiguousDouble()[i] = PNaN; - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); return m_butterfly->contiguousDouble(); } ContiguousJSValues JSObject::convertUndecidedToContiguous(VM& vm) { - ASSERT(hasUndecided(structure()->indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly); + ASSERT(hasUndecided(indexingType())); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); return m_butterfly->contiguous(); } ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& vm, unsigned neededLength) { + Structure* structure = this->structure(vm); unsigned publicLength = m_butterfly->publicLength(); - unsigned propertyCapacity = structure()->outOfLineCapacity(); - unsigned propertySize = structure()->outOfLineSize(); + unsigned propertyCapacity = structure->outOfLineCapacity(); + unsigned propertySize = structure->outOfLineSize(); Butterfly* newButterfly = Butterfly::createUninitialized( - vm, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); + vm, this, 0, propertyCapacity, true, ArrayStorage::sizeFor(neededLength)); memcpy( newButterfly->propertyStorage() - propertySize, @@ -729,13 +736,14 @@ ArrayStorage* JSObject::constructConvertedArrayStorageWithoutCopyingElements(VM& ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { - ASSERT(hasUndecided(structure()->indexingType())); + DeferGC deferGC(vm.heap); + ASSERT(hasUndecided(indexingType())); ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); // No need to copy elements. - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); - setButterfly(vm, storage->butterfly(), newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, storage->butterfly()); return storage; } @@ -746,41 +754,42 @@ ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransi ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm) { - return convertUndecidedToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); + return convertUndecidedToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); } ContiguousDoubles JSObject::convertInt32ToDouble(VM& vm) { - ASSERT(hasInt32(structure()->indexingType())); + ASSERT(hasInt32(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 = QNaN; + *currentAsDouble = PNaN; continue; } ASSERT(v.isInt32()); *currentAsDouble = v.asInt32(); } - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateDouble), m_butterfly); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble)); return m_butterfly->contiguousDouble(); } ContiguousJSValues JSObject::convertInt32ToContiguous(VM& vm) { - ASSERT(hasInt32(structure()->indexingType())); + ASSERT(hasInt32(indexingType())); - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); return m_butterfly->contiguous(); } ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { - ASSERT(hasInt32(structure()->indexingType())); + ASSERT(hasInt32(indexingType())); + DeferGC deferGC(vm.heap); ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); for (unsigned i = m_butterfly->publicLength(); i--;) { JSValue v = m_butterfly->contiguous()[i].get(); @@ -790,8 +799,8 @@ ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); - setButterfly(vm, newStorage->butterfly(), newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } @@ -802,13 +811,13 @@ ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm) { - return convertInt32ToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); + return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); } template ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm) { - ASSERT(hasDouble(structure()->indexingType())); + ASSERT(hasDouble(indexingType())); for (unsigned i = m_butterfly->vectorLength(); i--;) { double* current = &m_butterfly->contiguousDouble()[i]; @@ -826,12 +835,16 @@ ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm) case RageConvertDoubleToValue: v = jsNumber(value); break; + default: + v = JSValue(); + RELEASE_ASSERT_NOT_REACHED(); + break; } ASSERT(v.isNumber()); currentAsValue->setWithoutWriteBarrier(v); } - setStructure(vm, Structure::nonPropertyTransition(vm, structure(), AllocateContiguous), m_butterfly); + setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous)); return m_butterfly->contiguous(); } @@ -847,7 +860,8 @@ ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm) ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { - ASSERT(hasDouble(structure()->indexingType())); + DeferGC deferGC(vm.heap); + ASSERT(hasDouble(indexingType())); ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); for (unsigned i = m_butterfly->publicLength(); i--;) { @@ -858,8 +872,8 @@ ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransitio newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); - setButterfly(vm, newStorage->butterfly(), newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } @@ -870,12 +884,13 @@ ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransitio ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm) { - return convertDoubleToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); + return convertDoubleToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); } ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength) { - ASSERT(hasContiguous(structure()->indexingType())); + DeferGC deferGC(vm.heap); + ASSERT(hasContiguous(indexingType())); ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength); for (unsigned i = m_butterfly->publicLength(); i--;) { @@ -886,8 +901,8 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTrans newStorage->m_numValuesInVector++; } - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), transition); - setButterfly(vm, newStorage->butterfly(), newStructure); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition); + setStructureAndButterfly(vm, newStructure, newStorage->butterfly()); return newStorage; } @@ -898,7 +913,7 @@ ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTrans ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm) { - return convertContiguousToArrayStorage(vm, structure()->suggestedArrayStorageTransition()); + return convertContiguousToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition()); } void JSObject::convertUndecidedForValue(VM& vm, JSValue value) @@ -908,7 +923,7 @@ void JSObject::convertUndecidedForValue(VM& vm, JSValue value) return; } - if (value.isDouble()) { + if (value.isDouble() && value.asNumber() == value.asNumber()) { convertUndecidedToDouble(vm); return; } @@ -916,6 +931,24 @@ void JSObject::convertUndecidedForValue(VM& vm, JSValue value) convertUndecidedToContiguous(vm); } +void JSObject::createInitialForValueAndSet(VM& vm, unsigned index, JSValue value) +{ + if (value.isInt32()) { + createInitialInt32(vm, index + 1)[index].set(vm, this, value); + return; + } + + if (value.isDouble()) { + double doubleValue = value.asNumber(); + if (doubleValue == doubleValue) { + createInitialDouble(vm, index + 1)[index] = doubleValue; + return; + } + } + + createInitialContiguous(vm, index + 1)[index].set(vm, this, value); +} + void JSObject::convertInt32ForValue(VM& vm, JSValue value) { ASSERT(!value.isInt32()); @@ -952,11 +985,11 @@ void JSObject::convertDoubleToContiguousWhilePerformingSetIndex(VM& vm, unsigned ContiguousJSValues JSObject::ensureInt32Slow(VM& vm) { - ASSERT(inherits(&s_info)); + ASSERT(inherits(info())); - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) return ContiguousJSValues(); return createInitialInt32(vm, 0); @@ -976,11 +1009,11 @@ ContiguousJSValues JSObject::ensureInt32Slow(VM& vm) ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm) { - ASSERT(inherits(&s_info)); + ASSERT(inherits(info())); - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) return ContiguousDoubles(); return createInitialDouble(vm, 0); @@ -1002,11 +1035,11 @@ ContiguousDoubles JSObject::ensureDoubleSlow(VM& vm) ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm, DoubleToContiguousMode mode) { - ASSERT(inherits(&s_info)); + ASSERT(inherits(info())); - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: - if (UNLIKELY(indexingShouldBeSparse() || structure()->needsSlowPutIndexing())) + if (UNLIKELY(indexingShouldBeSparse() || structure(vm)->needsSlowPutIndexing())) return ContiguousJSValues(); return createInitialContiguous(vm, 0); @@ -1042,9 +1075,9 @@ ContiguousJSValues JSObject::rageEnsureContiguousSlow(VM& vm) ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) { - ASSERT(inherits(&s_info)); + ASSERT(inherits(info())); - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: if (UNLIKELY(indexingShouldBeSparse())) return ensureArrayStorageExistsAndEnterDictionaryIndexingMode(vm); @@ -1052,22 +1085,22 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) case ALL_UNDECIDED_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); return convertUndecidedToArrayStorage(vm); case ALL_INT32_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); return convertInt32ToArrayStorage(vm); case ALL_DOUBLE_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); return convertDoubleToArrayStorage(vm); case ALL_CONTIGUOUS_INDEXING_TYPES: ASSERT(!indexingShouldBeSparse()); - ASSERT(!structure()->needsSlowPutIndexing()); + ASSERT(!structure(vm)->needsSlowPutIndexing()); return convertContiguousToArrayStorage(vm); default: @@ -1078,7 +1111,7 @@ ArrayStorage* JSObject::ensureArrayStorageSlow(VM& vm) ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(VM& vm) { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: { createArrayStorage(vm, 0, 0); SparseArrayValueMap* map = allocateSparseIndexMap(vm); @@ -1109,7 +1142,7 @@ ArrayStorage* JSObject::ensureArrayStorageExistsAndEnterDictionaryIndexingMode(V void JSObject::switchToSlowPutArrayStorage(VM& vm) { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_UNDECIDED_INDEXING_TYPES: convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage); break; @@ -1128,8 +1161,8 @@ void JSObject::switchToSlowPutArrayStorage(VM& vm) case NonArrayWithArrayStorage: case ArrayWithArrayStorage: { - Structure* newStructure = Structure::nonPropertyTransition(vm, structure(), SwitchToSlowPutArrayStorage); - setStructure(vm, newStructure, m_butterfly); + Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage); + setStructure(vm, newStructure); break; } @@ -1139,21 +1172,14 @@ void JSObject::switchToSlowPutArrayStorage(VM& vm) } } -void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes) -{ - ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); - PutPropertySlot slot; - object->putDirectInternal(exec->vm(), propertyName, value, attributes, slot, getCallableObject(value)); -} - void JSObject::setPrototype(VM& vm, JSValue prototype) { ASSERT(prototype); if (prototype.isObject()) vm.prototypeMap.addPrototype(asObject(prototype)); - Structure* newStructure = Structure::changePrototypeTransition(vm, structure(), prototype); - setStructure(vm, newStructure, m_butterfly); + Structure* newStructure = Structure::changePrototypeTransition(vm, structure(vm), prototype); + setStructure(vm, newStructure); if (!newStructure->anyObjectInChainMayInterceptIndexedAccesses()) return; @@ -1163,28 +1189,25 @@ void JSObject::setPrototype(VM& vm, JSValue prototype) return; } - if (!hasIndexingHeader(structure()->indexingType())) + if (!hasIndexedProperties(indexingType())) return; - if (shouldUseSlowPut(structure()->indexingType())) + if (shouldUseSlowPut(indexingType())) return; switchToSlowPutArrayStorage(vm); } -bool JSObject::setPrototypeWithCycleCheck(VM& vm, JSValue prototype) +bool JSObject::setPrototypeWithCycleCheck(ExecState* exec, JSValue prototype) { - JSValue checkFor = this; - if (this->isGlobalObject()) - checkFor = jsCast(this)->globalExec()->thisValue(); - + ASSERT(methodTable(exec->vm())->toThis(this, exec, NotStrictMode) == this); JSValue nextPrototype = prototype; while (nextPrototype && nextPrototype.isObject()) { - if (nextPrototype == checkFor) + if (nextPrototype == this) return false; nextPrototype = asObject(nextPrototype)->prototype(); } - setPrototype(vm, prototype); + setPrototype(exec->vm(), prototype); return true; } @@ -1204,32 +1227,51 @@ void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSV return; } - VM& vm = exec->vm(); + putDirectNonIndexAccessor(exec->vm(), propertyName, value, attributes); +} - PutPropertySlot slot; +void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex); + + PutPropertySlot slot(this); + putDirectInternal(vm, propertyName, value, attributes, slot, getCallableObject(value)); + + ASSERT(slot.type() == PutPropertySlot::NewProperty); + + Structure* structure = this->structure(vm); + if (attributes & ReadOnly) + structure->setContainsReadOnlyProperties(); + structure->setHasCustomGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto); +} + +void JSObject::putDirectNonIndexAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes) +{ + PutPropertySlot slot(this); 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), m_butterfly); + setStructure(vm, Structure::attributeChangeTransition(vm, structure(vm), propertyName, attributes)); + Structure* structure = this->structure(vm); if (attributes & ReadOnly) - structure()->setContainsReadOnlyProperties(); + structure->setContainsReadOnlyProperties(); - structure()->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto); + structure->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto); } bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot; + PropertySlot slot(this); return const_cast(this)->getPropertySlot(exec, propertyName, slot); } bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const { - PropertySlot slot; + PropertySlot slot(this); return const_cast(this)->getPropertySlot(exec, propertyName, slot); } @@ -1240,27 +1282,29 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper unsigned i = propertyName.asIndex(); if (i != PropertyName::NotAnIndex) - return thisObject->methodTable()->deletePropertyByIndex(thisObject, exec, i); + return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, i); if (!thisObject->staticFunctionsReified()) thisObject->reifyStaticFunctionsForDelete(exec); unsigned attributes; JSCell* specificValue; - if (isValidOffset(thisObject->structure()->get(exec->vm(), propertyName, attributes, specificValue))) { - if (attributes & DontDelete && !exec->vm().isInDefineOwnProperty()) + VM& vm = exec->vm(); + if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes, specificValue))) { + if (attributes & DontDelete && !vm.isInDefineOwnProperty()) return false; - thisObject->removeDirect(exec->vm(), propertyName); + thisObject->removeDirect(vm, propertyName); return true; } // Look in the static hashtable of properties - const HashEntry* entry = thisObject->findPropertyHashEntry(exec, propertyName); + const HashTableValue* entry = thisObject->findPropertyHashEntry(vm, propertyName); if (entry) { - if (entry->attributes() & DontDelete && !exec->vm().isInDefineOwnProperty()) + if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty()) return false; // this builtin property can't be deleted - putEntry(exec, entry, propertyName, jsUndefined(), thisObject); + PutPropertySlot slot(thisObject); + putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot); } return true; @@ -1268,8 +1312,8 @@ bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName proper bool JSObject::hasOwnProperty(ExecState* exec, PropertyName propertyName) const { - PropertySlot slot; - return const_cast(this)->methodTable()->getOwnPropertySlot(const_cast(this), exec, propertyName, slot); + PropertySlot slot(this); + return const_cast(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast(this), exec, propertyName, slot); } bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) @@ -1277,16 +1321,16 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) JSObject* thisObject = jsCast(cell); if (i > MAX_ARRAY_INDEX) - return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, i)); + return thisObject->methodTable(exec->vm())->deleteProperty(thisObject, exec, Identifier::from(exec, i)); - switch (thisObject->structure()->indexingType()) { + switch (thisObject->indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return true; case ALL_INT32_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (i >= butterfly->vectorLength()) return true; butterfly->contiguous()[i].clear(); @@ -1294,10 +1338,10 @@ bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i) } case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = thisObject->m_butterfly; + Butterfly* butterfly = thisObject->butterfly(); if (i >= butterfly->vectorLength()) return true; - butterfly->contiguousDouble()[i] = QNaN; + butterfly->contiguousDouble()[i] = PNaN; return true; } @@ -1352,7 +1396,7 @@ static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSO bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const { - result = methodTable()->defaultValue(this, exec, PreferNumber); + result = methodTable(exec->vm())->defaultValue(this, exec, PreferNumber); number = result.toNumber(exec); return !result.isString(); } @@ -1379,14 +1423,14 @@ JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, Preferre ASSERT(!exec->hadException()); - return throwError(exec, createTypeError(exec, ASCIILiteral("No default value"))); + return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value"))); } -const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, PropertyName propertyName) const +const HashTableValue* JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const { for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { - if (const HashTable* propHashTable = info->propHashTable(exec)) { - if (const HashEntry* entry = propHashTable->entry(exec, propertyName)) + if (const HashTable* propHashTable = info->propHashTable(vm)) { + if (const HashTableValue* entry = propHashTable->entry(vm, propertyName)) return entry; } } @@ -1395,12 +1439,13 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, PropertyName p bool JSObject::hasInstance(ExecState* exec, JSValue value) { - TypeInfo info = structure()->typeInfo(); + VM& vm = exec->vm(); + TypeInfo info = structure(vm)->typeInfo(); if (info.implementsDefaultHasInstance()) return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype)); if (info.implementsHasInstance()) - return methodTable()->customHasInstance(this, exec, value); - throwError(exec, createInvalidParamError(exec, "instanceof" , this)); + return methodTable(vm)->customHasInstance(this, exec, value); + vm.throwException(exec, createInvalidParameterError(exec, "instanceof" , this)); return false; } @@ -1410,7 +1455,7 @@ bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto) return false; if (!proto.isObject()) { - throwError(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("instanceof called on an object with an invalid prototype property."))); return false; } @@ -1422,18 +1467,11 @@ bool JSObject::defaultHasInstance(ExecState* exec, JSValue value, JSValue proto) return false; } -bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const -{ - PropertyDescriptor descriptor; - if (!const_cast(this)->methodTable()->getOwnPropertyDescriptor(const_cast(this), exec, propertyName, descriptor)) - return false; - return descriptor.enumerable(); -} - bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const { + VM& vm = exec->vm(); unsigned attributes; - if (isValidOffset(structure()->get(exec->vm(), propertyName, attributes, specificValue))) + if (isValidOffset(structure(vm)->get(vm, propertyName, attributes, specificValue))) return true; // This could be a function within the static table? - should probably @@ -1446,18 +1484,19 @@ bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyNa void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { propertyNames.setBaseObject(object); - object->methodTable()->getOwnPropertyNames(object, exec, propertyNames, mode); + object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode); if (object->prototype().isNull()) return; + VM& vm = exec->vm(); JSObject* prototype = asObject(object->prototype()); while(1) { - if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { - prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode); + if (prototype->structure(vm)->typeInfo().overridesGetPropertyNames()) { + prototype->methodTable(vm)->getPropertyNames(prototype, exec, propertyNames, mode); break; } - prototype->methodTable()->getOwnPropertyNames(prototype, exec, propertyNames, mode); + prototype->methodTable(vm)->getOwnPropertyNames(prototype, exec, propertyNames, mode); JSValue nextProto = prototype->prototype(); if (nextProto.isNull()) break; @@ -1471,14 +1510,14 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa // 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()) { + 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->m_butterfly; + Butterfly* butterfly = object->butterfly(); unsigned usedLength = butterfly->publicLength(); for (unsigned i = 0; i < usedLength; ++i) { if (!butterfly->contiguous()[i]) @@ -1489,7 +1528,7 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa } case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = object->m_butterfly; + Butterfly* butterfly = object->butterfly(); unsigned usedLength = butterfly->publicLength(); for (unsigned i = 0; i < usedLength; ++i) { double value = butterfly->contiguousDouble()[i]; @@ -1530,15 +1569,16 @@ void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNa RELEASE_ASSERT_NOT_REACHED(); } - object->methodTable()->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); + object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); } void JSObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified()); + VM& vm = exec->vm(); bool canCachePropertiesFromStructure = !propertyNames.size(); - object->structure()->getPropertyNamesFromStructure(exec->vm(), propertyNames, mode); + object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode); if (canCachePropertiesFromStructure) propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size()); @@ -1560,7 +1600,7 @@ JSString* JSObject::toString(ExecState* exec) const return primitive.toString(exec); } -JSObject* JSObject::toThisObject(JSCell* cell, ExecState*) +JSValue JSObject::toThis(JSCell* cell, ExecState*, ECMAMode) { return jsCast(cell); } @@ -1570,7 +1610,7 @@ void JSObject::seal(VM& vm) if (isSealed(vm)) return; preventExtensions(vm); - setStructure(vm, Structure::sealTransition(vm, structure()), m_butterfly); + setStructure(vm, Structure::sealTransition(vm, structure(vm))); } void JSObject::freeze(VM& vm) @@ -1578,14 +1618,14 @@ void JSObject::freeze(VM& vm) if (isFrozen(vm)) return; preventExtensions(vm); - setStructure(vm, Structure::freezeTransition(vm, structure()), m_butterfly); + setStructure(vm, Structure::freezeTransition(vm, structure(vm))); } void JSObject::preventExtensions(VM& vm) { enterDictionaryIndexingMode(vm); if (isExtensible()) - setStructure(vm, Structure::preventExtensionsTransition(vm, structure()), m_butterfly); + setStructure(vm, Structure::preventExtensionsTransition(vm, structure(vm))); } // This presently will flatten to an uncachable dictionary; this is suitable @@ -1598,66 +1638,67 @@ void JSObject::reifyStaticFunctionsForDelete(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()->setStaticFunctionsReified(); + structure(vm)->setStaticFunctionsReified(); return; } - if (!structure()->isUncacheableDictionary()) - setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure()), m_butterfly); + if (!structure(vm)->isUncacheableDictionary()) + setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm))); for (const ClassInfo* info = classInfo(); info; info = info->parentClass) { - const HashTable* hashTable = info->propHashTable(globalObject()->globalExec()); + const HashTable* hashTable = info->propHashTable(vm); if (!hashTable) continue; - PropertySlot slot; - 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); + PropertySlot slot(this); + for (auto iter = hashTable->begin(vm); iter != hashTable->end(vm); ++iter) { + if (iter->attributes() & BuiltinOrFunction) + setUpStaticFunctionSlot(globalObject()->globalExec(), iter.value(), this, Identifier(&vm, iter.key()), slot); } } - structure()->setStaticFunctionsReified(); + structure(vm)->setStaticFunctionsReified(); } bool JSObject::removeDirect(VM& vm, PropertyName propertyName) { - if (!isValidOffset(structure()->get(vm, propertyName))) + Structure* structure = this->structure(vm); + 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), m_butterfly); + setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset)); if (offset == invalidOffset) return false; putDirectUndefined(offset); return true; } -NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, PropertyOffset offset) +NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue getterSetter, unsigned attributes, PropertyOffset offset) { - if (JSObject* getterFunction = asGetterSetter(getDirect(offset))->getter()) { - if (!structure()->isDictionary()) - slot.setCacheableGetterSlot(this, getterFunction, offset); - else - slot.setGetterSlot(getterFunction); - } else - slot.setUndefined(); + if (structure()->isDictionary()) { + slot.setGetterSlot(this, attributes, jsCast(getterSetter)); + return; + } + slot.setCacheableGetterSlot(this, attributes, jsCast(getterSetter), offset); } -void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) +void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMap, const PropertyDescriptor& descriptor, PropertyDescriptor& oldDescriptor) { + VM& vm = exec->vm(); + if (descriptor.isDataDescriptor()) { if (descriptor.value()) - entryInMap->set(exec->vm(), this, descriptor.value()); + entryInMap->set(vm, this, descriptor.value()); else if (oldDescriptor.isAccessorDescriptor()) - entryInMap->set(exec->vm(), this, jsUndefined()); + entryInMap->set(vm, this, jsUndefined()); entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~Accessor; return; } @@ -1674,13 +1715,13 @@ void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa else if (oldDescriptor.isAccessorDescriptor()) setter = oldDescriptor.setterObject(); - GetterSetter* accessor = GetterSetter::create(exec); + GetterSetter* accessor = GetterSetter::create(vm); if (getter) - accessor->setGetter(exec->vm(), getter); + accessor->setGetter(vm, getter); if (setter) - accessor->setSetter(exec->vm(), setter); + accessor->setSetter(vm, setter); - entryInMap->set(exec->vm(), this, accessor); + entryInMap->set(vm, this, accessor); entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly; return; } @@ -1690,7 +1731,7 @@ void JSObject::putIndexedDescriptor(ExecState* exec, SparseArrayEntry* entryInMa } // Defined in ES5.1 8.12.9 -bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, PropertyDescriptor& descriptor, bool throwException) +bool JSObject::defineOwnIndexedProperty(ExecState* exec, unsigned index, const PropertyDescriptor& descriptor, bool throwException) { ASSERT(index <= MAX_ARRAY_INDEX); @@ -1861,7 +1902,7 @@ bool JSObject::attemptToInterceptPutByIndexOnHole(ExecState* exec, unsigned i, J template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, unsigned i, JSValue value) { - ASSERT((structure()->indexingType() & IndexingShapeMask) == indexingShape); + ASSERT((indexingType() & IndexingShapeMask) == indexingShape); ASSERT(!indexingShouldBeSparse()); // For us to get here, the index is either greater than the public length, or greater than @@ -1872,7 +1913,7 @@ void JSObject::putByIndexBeyondVectorLengthWithoutAttributes(ExecState* exec, un if (i >= MAX_ARRAY_INDEX - 1 || (i >= MIN_SPARSE_ARRAY_INDEX - && !isDenseEnoughForVector(i, countElements(m_butterfly))) + && !isDenseEnoughForVector(i, countElements(butterfly()))) || indexIsSufficientlyBeyondLengthForSparseMap(i, m_butterfly->vectorLength())) { ASSERT(i <= MAX_ARRAY_INDEX); ensureArrayStorageSlow(vm); @@ -1990,7 +2031,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 (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse()) { putByIndexBeyondVectorLengthWithArrayStorage( @@ -2003,14 +2044,14 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue exec, i, value, shouldThrow, createArrayStorage(vm, 0, 0)); break; } - if (structure()->needsSlowPutIndexing()) { + if (structure(vm)->needsSlowPutIndexing()) { ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); storage->m_vector[i].set(vm, this, value); storage->m_numValuesInVector++; break; } - - createInitialContiguous(vm, i + 1)[i].set(vm, this, value); + + createInitialForValueAndSet(vm, i, value); break; } @@ -2040,7 +2081,7 @@ void JSObject::putByIndexBeyondVectorLength(ExecState* exec, unsigned i, JSValue SparseArrayValueMap* map = arrayStorage()->m_sparseMap.get(); if (!(map && map->contains(i)) && attemptToInterceptPutByIndexOnHole(exec, i, value, shouldThrow)) return; - // Otherwise, fall though. + FALLTHROUGH; } case NonArrayWithArrayStorage: @@ -2058,7 +2099,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(hasArrayStorage(structure()->indexingType())); + ASSERT(hasAnyArrayStorage(indexingType())); ASSERT(arrayStorage() == storage); ASSERT(i >= storage->vectorLength() || attributes); ASSERT(i <= MAX_ARRAY_INDEX); @@ -2140,7 +2181,7 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV if (attributes & (ReadOnly | Accessor)) notifyPresenceOfIndexedAccessors(vm); - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: { if (indexingShouldBeSparse() || attributes) { return putDirectIndexBeyondVectorLengthWithArrayStorage( @@ -2151,14 +2192,14 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV return putDirectIndexBeyondVectorLengthWithArrayStorage( exec, i, value, attributes, mode, createArrayStorage(vm, 0, 0)); } - if (structure()->needsSlowPutIndexing()) { + if (structure(vm)->needsSlowPutIndexing()) { ArrayStorage* storage = createArrayStorage(vm, i + 1, getNewVectorLength(0, 0, i + 1)); storage->m_vector[i].set(vm, this, value); storage->m_numValuesInVector++; return true; } - createInitialContiguous(vm, i + 1)[i].set(vm, this, value); + createInitialForValueAndSet(vm, i, value); return true; } @@ -2217,14 +2258,46 @@ bool JSObject::putDirectIndexBeyondVectorLength(ExecState* exec, unsigned i, JSV } } -void JSObject::putDirectNativeFunction(ExecState* exec, JSGlobalObject* globalObject, const PropertyName& propertyName, unsigned functionLength, NativeFunction nativeFunction, Intrinsic intrinsic, unsigned attributes) +void JSObject::putDirectNativeFunction(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(exec, globalObject, functionLength, name, nativeFunction, intrinsic); - putDirect(exec->vm(), propertyName, function, attributes); + + JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic); + 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) +{ + StringImpl* name = propertyName.publicName(); + if (!name) + name = vm.propertyNames->anonymous.impl(); + ASSERT(name); + 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(); + ASSERT(name); + + JSFunction* function = JSFunction::create(vm, globalObject, functionLength, name, nativeFunction, intrinsic); + putDirectWithoutTransition(vm, propertyName, function, attributes); } ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength) @@ -2254,7 +2327,7 @@ ALWAYS_INLINE unsigned JSObject::getNewVectorLength(unsigned desiredLength) unsigned vectorLength; unsigned length; - if (hasIndexedProperties(structure()->indexingType())) { + if (hasIndexedProperties(indexingType())) { vectorLength = m_butterfly->vectorLength(); length = m_butterfly->publicLength(); } else { @@ -2293,19 +2366,19 @@ unsigned JSObject::countElements(Butterfly* butterfly) unsigned JSObject::countElements() { - switch (structure()->indexingType()) { + switch (indexingType()) { case ALL_BLANK_INDEXING_TYPES: case ALL_UNDECIDED_INDEXING_TYPES: return 0; case ALL_INT32_INDEXING_TYPES: - return countElements(m_butterfly); + return countElements(butterfly()); case ALL_DOUBLE_INDEXING_TYPES: - return countElements(m_butterfly); + return countElements(butterfly()); case ALL_CONTIGUOUS_INDEXING_TYPES: - return countElements(m_butterfly); + return countElements(butterfly()); default: CRASH(); @@ -2332,49 +2405,56 @@ 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)) { - Butterfly* newButterfly = storage->butterfly()->growArrayRight(vm, structure(), structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); + DeferGC deferGC(vm.heap); + Butterfly* newButterfly = storage->butterfly()->growArrayRight( + vm, this, structure, structure->outOfLineCapacity(), true, + ArrayStorage::sizeFor(vectorLength), ArrayStorage::sizeFor(newVectorLength)); if (!newButterfly) return false; - m_butterfly = newButterfly; newButterfly->arrayStorage()->setVectorLength(newVectorLength); + setButterflyWithoutChangingStructure(vm, newButterfly); return true; } // Remove some, but not all of the precapacity. Atomic decay, & capped to not overflow array length. + DeferGC deferGC(vm.heap); unsigned newIndexBias = std::min(indexBias >> 1, MAX_STORAGE_VECTOR_LENGTH - newVectorLength); Butterfly* newButterfly = storage->butterfly()->resizeArray( - vm, - structure()->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), + vm, this, + structure->outOfLineCapacity(), true, ArrayStorage::sizeFor(vectorLength), newIndexBias, true, ArrayStorage::sizeFor(newVectorLength)); if (!newButterfly) return false; - - m_butterfly = newButterfly; newButterfly->arrayStorage()->setVectorLength(newVectorLength); newButterfly->arrayStorage()->m_indexBias = newIndexBias; + setButterflyWithoutChangingStructure(vm, newButterfly); return true; } void JSObject::ensureLengthSlow(VM& vm, unsigned length) { ASSERT(length < MAX_ARRAY_INDEX); - ASSERT(hasContiguous(structure()->indexingType()) || hasInt32(structure()->indexingType()) || hasDouble(structure()->indexingType()) || hasUndecided(structure()->indexingType())); + ASSERT(hasContiguous(indexingType()) || hasInt32(indexingType()) || hasDouble(indexingType()) || hasUndecided(indexingType())); ASSERT(length > m_butterfly->vectorLength()); unsigned newVectorLength = std::min( length << 1, MAX_STORAGE_VECTOR_LENGTH); unsigned oldVectorLength = m_butterfly->vectorLength(); - m_butterfly = m_butterfly->growArrayRight( - vm, structure(), structure()->outOfLineCapacity(), true, + DeferGC deferGC(vm.heap); + m_butterfly.set(vm, this, m_butterfly->growArrayRight( + vm, this, structure(), structure()->outOfLineCapacity(), true, oldVectorLength * sizeof(EncodedJSValue), - newVectorLength * sizeof(EncodedJSValue)); - if (hasDouble(structure()->indexingType())) { + newVectorLength * sizeof(EncodedJSValue))); + + m_butterfly->setVectorLength(newVectorLength); + + if (hasDouble(indexingType())) { for (unsigned i = oldVectorLength; i < newVectorLength; ++i) - m_butterfly->contiguousDouble().data()[i] = QNaN; + m_butterfly->contiguousDouble().data()[i] = PNaN; } - m_butterfly->setVectorLength(newVectorLength); } Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize) @@ -2384,100 +2464,36 @@ Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize // 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 m_butterfly->growPropertyStorage(vm, structure(), oldSize, newSize); + return Butterfly::createOrGrowPropertyStorage(m_butterfly.get(), vm, this, structure(vm), oldSize, newSize); } -bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) +bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) { - unsigned attributes = 0; - JSCell* cell = 0; - PropertyOffset offset = object->structure()->get(exec->vm(), propertyName, attributes, cell); - if (isValidOffset(offset)) { - descriptor.setDescriptor(object->getDirect(offset), attributes); - return true; - } - - unsigned i = propertyName.asIndex(); - if (i == PropertyName::NotAnIndex) + JSC::PropertySlot slot(this); + if (!methodTable(exec->vm())->getOwnPropertySlot(this, exec, propertyName, slot)) return false; - - switch (object->structure()->indexingType()) { - case ALL_BLANK_INDEXING_TYPES: - case ALL_UNDECIDED_INDEXING_TYPES: + /* Workaround, JSDOMWindow::getOwnPropertySlot searches the prototype chain. :-( */ + if (slot.slotBase() != this && slot.slotBase() && slot.slotBase()->methodTable(exec->vm())->toThis(slot.slotBase(), exec, NotStrictMode) != this) return false; - - case ALL_INT32_INDEXING_TYPES: - case ALL_CONTIGUOUS_INDEXING_TYPES: { - Butterfly* butterfly = object->m_butterfly; - if (i >= butterfly->vectorLength()) - return false; - JSValue value = butterfly->contiguous()[i].get(); - if (!value) - return false; - descriptor.setDescriptor(value, 0); - return true; - } - - case ALL_DOUBLE_INDEXING_TYPES: { - Butterfly* butterfly = object->m_butterfly; - if (i >= butterfly->vectorLength()) - return false; - double value = butterfly->contiguousDouble()[i]; - if (value != value) - return false; - descriptor.setDescriptor(JSValue(JSValue::EncodeAsDouble, value), 0); - return true; - } - - case ALL_ARRAY_STORAGE_INDEXING_TYPES: { - ArrayStorage* storage = object->m_butterfly->arrayStorage(); - if (i >= storage->length()) - return false; - if (i < storage->vectorLength()) { - WriteBarrier& value = storage->m_vector[i]; - if (!value) - return false; - descriptor.setDescriptor(value.get(), 0); - return true; - } - if (SparseArrayValueMap* map = storage->m_sparseMap.get()) { - SparseArrayValueMap::iterator it = map->find(i); - if (it == map->notFound()) - return false; - it->value.get(descriptor); - return true; - } - return false; - } - - default: - RELEASE_ASSERT_NOT_REACHED(); - return false; - } -} - -bool JSObject::getPropertyDescriptor(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) -{ - JSObject* object = this; - while (true) { - if (object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, descriptor)) - return true; - JSValue prototype = object->prototype(); - if (!prototype.isObject()) - return false; - object = asObject(prototype); - } + if (slot.isAccessor()) + descriptor.setAccessorDescriptor(slot.getterSetter(), slot.attributes()); + else if (slot.attributes() & CustomAccessor) + descriptor.setCustomDescriptor(slot.attributes()); + else + descriptor.setDescriptor(slot.getValue(exec, propertyName), slot.attributes()); + return true; } -static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) +static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName propertyName, const PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) { + VM& vm = exec->vm(); if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { - GetterSetter* accessor = GetterSetter::create(exec); + GetterSetter* accessor = GetterSetter::create(vm); if (oldDescriptor.getterPresent()) - accessor->setGetter(exec->vm(), oldDescriptor.getterObject()); + accessor->setGetter(vm, oldDescriptor.getterObject()); if (oldDescriptor.setterPresent()) - accessor->setSetter(exec->vm(), oldDescriptor.setterObject()); + accessor->setSetter(vm, oldDescriptor.setterObject()); target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); return true; } @@ -2486,22 +2502,22 @@ static bool putDescriptor(ExecState* exec, JSObject* target, PropertyName proper newValue = descriptor.value(); else if (oldDescriptor.value()) newValue = oldDescriptor.value(); - target->putDirect(exec->vm(), propertyName, newValue, attributes & ~Accessor); + target->putDirect(vm, propertyName, newValue, attributes & ~Accessor); if (attributes & ReadOnly) - target->structure()->setContainsReadOnlyProperties(); + target->structure(vm)->setContainsReadOnlyProperties(); return true; } attributes &= ~ReadOnly; - GetterSetter* accessor = GetterSetter::create(exec); + GetterSetter* accessor = GetterSetter::create(vm); if (descriptor.getterPresent()) - accessor->setGetter(exec->vm(), descriptor.getterObject()); + accessor->setGetter(vm, descriptor.getterObject()); else if (oldDescriptor.getterPresent()) - accessor->setGetter(exec->vm(), oldDescriptor.getterObject()); + accessor->setGetter(vm, oldDescriptor.getterObject()); if (descriptor.setterPresent()) - accessor->setSetter(exec->vm(), descriptor.setterObject()); + accessor->setSetter(vm, descriptor.setterObject()); else if (oldDescriptor.setterPresent()) - accessor->setSetter(exec->vm(), oldDescriptor.setterObject()); + accessor->setSetter(vm, oldDescriptor.setterObject()); target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor); return true; @@ -2533,7 +2549,7 @@ private: VM& m_vm; }; -bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, 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 @@ -2543,11 +2559,11 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN // If we have a new property we can just put it on normally PropertyDescriptor current; - if (!methodTable()->getOwnPropertyDescriptor(this, exec, propertyName, current)) { + if (!getOwnPropertyDescriptor(exec, propertyName, current)) { // unless extensions are prevented! if (!isExtensible()) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to define property on object that is not extensible."))); return false; } PropertyDescriptor oldDescriptor; @@ -2565,12 +2581,12 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN if (!current.configurable()) { if (descriptor.configurable()) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to 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()) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change enumerable attribute of unconfigurable property."))); return false; } } @@ -2578,7 +2594,7 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN // A generic descriptor is simply changing the attributes of an existing property if (descriptor.isGenericDescriptor()) { if (!current.attributesEqual(descriptor)) { - methodTable()->deleteProperty(this, exec, propertyName); + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } return true; @@ -2588,10 +2604,10 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { if (!current.configurable()) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change access mechanism for an unconfigurable property."))); return false; } - methodTable()->deleteProperty(this, exec, propertyName); + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } @@ -2600,20 +2616,20 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN if (!current.configurable()) { if (!current.writable() && descriptor.writable()) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change writable attribute of unconfigurable property."))); return false; } if (!current.writable()) { if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change value of a readonly property."))); return false; } } } if (current.attributesEqual(descriptor) && !descriptor.value()) return true; - methodTable()->deleteProperty(this, exec, propertyName); + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } @@ -2622,32 +2638,43 @@ bool JSObject::defineOwnNonIndexProperty(ExecState* exec, PropertyName propertyN if (!current.configurable()) { if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change the setter of an unconfigurable property."))); + exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change the setter of an unconfigurable property."))); return false; } if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { if (throwException) - throwError(exec, createTypeError(exec, ASCIILiteral("Attempting to change the getter of an unconfigurable property."))); + 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; } } JSValue accessor = getDirect(exec->vm(), propertyName); if (!accessor) return false; - GetterSetter* getterSetter = asGetterSetter(accessor); + GetterSetter* getterSetter; + if (accessor.isCustomGetterSetter()) + getterSetter = GetterSetter::create(exec->vm()); + else { + ASSERT(accessor.isGetterSetter()); + 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; - methodTable()->deleteProperty(this, exec, propertyName); + methodTable(exec->vm())->deleteProperty(this, exec, propertyName); unsigned attrs = descriptor.attributesOverridingCurrent(current); putDirectAccessor(exec, propertyName, getterSetter, attrs | Accessor); return true; } -bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool 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. unsigned index = propertyName.asIndex(); @@ -2664,17 +2691,20 @@ bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); } -bool JSObject::getOwnPropertySlotSlow(ExecState* exec, PropertyName propertyName, PropertySlot& slot) +JSObject* throwTypeError(ExecState* exec, const String& message) { - unsigned i = propertyName.asIndex(); - if (i != PropertyName::NotAnIndex) - return getOwnPropertySlotByIndex(this, exec, i, slot); - return false; + return exec->vm().throwException(exec, createTypeError(exec, message)); } -JSObject* throwTypeError(ExecState* exec, const String& message) +void JSObject::shiftButterflyAfterFlattening(VM& vm, size_t outOfLineCapacityBefore, size_t outOfLineCapacityAfter) { - return throwError(exec, createTypeError(exec, message)); + 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)); } } // namespace JSC