summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/runtime/JSObject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSObject.cpp')
-rw-r--r--Source/JavaScriptCore/runtime/JSObject.cpp1362
1 files changed, 513 insertions, 849 deletions
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 <math.h>
@@ -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<Unknown>* 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<JSObject*>(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<JSObject*>(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<JSObject*>(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<JSFunction*>(ctorObject))
- prototypeFunctionName = constructorFunction->calculatedDisplayName(exec);
- else if (InternalFunction* constructorFunction = jsDynamicCast<InternalFunction*>(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<JSObject*>(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<PutModePut>(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<CustomGetterSetter*>(gs.asCell())->setter());
- else
- slot.setCustomValue(obj, jsCast<CustomGetterSetter*>(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<PutModePut>(vm, propertyName, value, 0, slot) && slot.isStrictMode())
+ ASSERT(!thisObject->structure()->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
+ if (!thisObject->putDirectInternal<PutModePut>(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<Unknown>* current = &butterfly->contiguousInt32()[i];
+ ASSERT(hasInt32(structure()->indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;) {
+ WriteBarrier<Unknown>* current = &m_butterfly->contiguousInt32()[i];
double* currentAsDouble = bitwise_cast<double*>(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<JSObject::DoubleToContiguousMode mode>
+ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
+{
+ ASSERT(hasDouble(structure()->indexingType()));
+
+ for (unsigned i = m_butterfly->vectorLength(); i--;) {
+ double* current = &m_butterfly->contiguousDouble()[i];
WriteBarrier<Unknown>* currentAsValue = bitwise_cast<WriteBarrier<Unknown>*>(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<EncodeValueAsDouble>(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<RageConvertDoubleToValue>(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<uint32_t> 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<PutModeDefineOwnProperty>(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<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
+ putDirectInternal<PutModeDefineOwnProperty>(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<JSObject*>(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<JSObject*>(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<JSObject*>(cell);
- VM& vm = exec->vm();
- if (Optional<uint32_t> 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<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
-}
-
-bool JSObject::hasOwnProperty(ExecState* exec, unsigned propertyName) const
-{
- PropertySlot slot(this, PropertySlot::InternalMethodType::GetOwnProperty);
- return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot);
+ PropertySlot slot(this);
+ return const_cast<JSObject*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject*>(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<JSObject*>(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<Unknown>& 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<unsigned, 0, UnsafeVectorOverflow> 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<unsigned, 0, UnsafeVectorOverflow> 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<unsigned>(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<unsigned>(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*>(getterSetter));
return;
}
+
slot.setCacheableGetterSlot(this, attributes, jsCast<GetterSetter*>(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<IndexingType indexingShape>
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<indexingShape>(butterfly)))
- || indexIsSufficientlyBeyondLengthForSparseMap(i, butterfly->vectorLength())) {
+ && !isDenseEnoughForVector(i, countElements<indexingShape>(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<Int32Shape>(ExecState*, unsigned, JSValue);
-template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes<DoubleShape>(ExecState*, unsigned, JSValue);
-template void JSObject::putByIndexBeyondVectorLengthWithoutAttributes<ContiguousShape>(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<ContiguousShape>(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*>(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*>(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<JSProxy*>(this);
- if (!jsProxy || jsProxy->target() != slot.slotBase()) {
- // Try ProxyObject.
- ProxyObject* proxyObject = jsDynamicCast<ProxyObject*>(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<JSProxy*>(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<CustomGetterSetter*>(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<uint32_t> 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<uint32_t> 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