/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2003-2006, 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
* Copyright (C) 2007 Eric Seidel (eric@webkit.org)
*
* This library is free software; you can redistribute it and/or
#include "CustomGetterSetter.h"
#include "DatePrototype.h"
#include "ErrorConstructor.h"
+#include "Exception.h"
#include "Executable.h"
#include "GetterSetter.h"
#include "IndexingHeaderInlines.h"
+#include "JSCatchScope.h"
#include "JSFunction.h"
+#include "JSFunctionNameScope.h"
#include "JSGlobalObject.h"
#include "Lookup.h"
#include "NativeErrorConstructor.h"
// ArrayConventions.h.
static unsigned lastArraySize = 0;
-JSCell* getCallableObjectSlow(JSCell* cell)
-{
- if (cell->type() == JSFunctionType)
- return cell;
- if (cell->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, 0, CREATE_METHOD_TABLE(JSObject) };
+const ClassInfo JSObject::s_info = { "Object", 0, 0, CREATE_METHOD_TABLE(JSObject) };
-const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) };
+const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, CREATE_METHOD_TABLE(JSFinalObject) };
static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode, bool didReify)
{
// Add properties from the static hashtables of properties
for (; classInfo; classInfo = classInfo->parentClass) {
- const HashTable* table = classInfo->propHashTable(vm);
+ const HashTable* table = classInfo->staticPropHashTable;
if (!table)
continue;
- for (auto iter = table->begin(vm); iter != table->end(vm); ++iter) {
- if ((!(iter->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)) && !((iter->attributes() & BuiltinOrFunction) && didReify))
- propertyNames.add(Identifier(&vm, iter.key()));
+ for (auto iter = table->begin(); iter != table->end(); ++iter) {
+ if ((!(iter->attributes() & DontEnum) || mode.includeDontEnumProperties()) && !((iter->attributes() & BuiltinOrFunctionOrAccessor) && didReify))
+ propertyNames.add(Identifier::fromString(&vm, iter.key()));
}
}
}
return info->className;
}
+String JSObject::calculatedClassName(JSObject* object)
+{
+ String prototypeFunctionName;
+ ExecState* exec = object->globalObject()->globalExec();
+ PropertySlot slot(object->structure()->storedPrototype());
+ 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
// 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());
+ if (Optional<uint32_t> index = parseIndex(propertyName)) {
+ putByIndex(thisObject, exec, index.value(), value, slot.isStrictMode());
return;
}
prototype = obj->prototype();
if (prototype.isNull()) {
ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName));
- if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value))
+ if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot)
&& slot.isStrictMode())
throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
return;
JSObject* obj;
for (obj = thisObject; ; obj = asObject(prototype)) {
unsigned attributes;
- JSCell* specificValue;
- PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes, specificValue);
+ PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
if (isValidOffset(offset)) {
if (attributes & ReadOnly) {
ASSERT(thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
break;
}
const ClassInfo* info = obj->classInfo();
- if (info->hasStaticSetterOrReadonlyProperties(vm)) {
- if (const HashTableValue* entry = obj->findPropertyHashEntry(vm, propertyName)) {
- putEntry(exec, entry, obj, propertyName, value, slot);
- return;
+ if (info->hasStaticSetterOrReadonlyProperties()) {
+ if (const HashTableValue* entry = obj->findPropertyHashEntry(propertyName)) {
+ if (!obj->staticFunctionsReified() || !(entry->attributes() & BuiltinOrFunctionOrAccessor)) {
+ putEntry(exec, entry, obj, propertyName, value, slot);
+ return;
+ }
}
}
prototype = obj->prototype();
}
ASSERT(!thisObject->structure(vm)->prototypeChainMayInterceptStoreTo(exec->vm(), propertyName) || obj == thisObject);
- if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot, getCallableObject(value)) && slot.isStrictMode())
+ if (!thisObject->putDirectInternal<PutModePut>(vm, propertyName, value, 0, slot) && slot.isStrictMode())
throwTypeError(exec, ASCIILiteral(StrictModeReadonlyPropertyWriteError));
return;
}
return newStorage;
}
-ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+ArrayStorage* JSObject::convertUndecidedToArrayStorage(VM& vm, NonPropertyTransition transition)
{
DeferGC deferGC(vm.heap);
ASSERT(hasUndecided(indexingType()));
-
- ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* storage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
// No need to copy elements.
Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
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 m_butterfly->contiguous();
}
-ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
{
- ASSERT(hasInt32(indexingType()));
-
DeferGC deferGC(vm.heap);
- ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
- for (unsigned i = m_butterfly->publicLength(); i--;) {
+ ASSERT(hasInt32(indexingType()));
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
JSValue v = m_butterfly->contiguous()[i].get();
- if (!v)
- continue;
- newStorage->m_vector[i].setWithoutWriteBarrier(v);
- newStorage->m_numValuesInVector++;
+ if (v) {
+ newStorage->m_vector[i].setWithoutWriteBarrier(v);
+ newStorage->m_numValuesInVector++;
+ } else
+ ASSERT(newStorage->m_vector[i].get().isEmpty());
}
Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
return newStorage;
}
-ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm, NonPropertyTransition transition)
-{
- return convertInt32ToArrayStorage(vm, transition, m_butterfly->vectorLength());
-}
-
ArrayStorage* JSObject::convertInt32ToArrayStorage(VM& vm)
{
return convertInt32ToArrayStorage(vm, structure(vm)->suggestedArrayStorageTransition());
}
-template<JSObject::DoubleToContiguousMode mode>
-ContiguousJSValues JSObject::genericConvertDoubleToContiguous(VM& vm)
+ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
{
ASSERT(hasDouble(indexingType()));
currentAsValue->clear();
continue;
}
- JSValue v;
- switch (mode) {
- case EncodeValueAsDouble:
- v = JSValue(JSValue::EncodeAsDouble, value);
- break;
- case RageConvertDoubleToValue:
- v = jsNumber(value);
- break;
- default:
- v = JSValue();
- RELEASE_ASSERT_NOT_REACHED();
- break;
- }
- ASSERT(v.isNumber());
+ JSValue v = JSValue(JSValue::EncodeAsDouble, value);
currentAsValue->setWithoutWriteBarrier(v);
}
return m_butterfly->contiguous();
}
-ContiguousJSValues JSObject::convertDoubleToContiguous(VM& vm)
-{
- return genericConvertDoubleToContiguous<EncodeValueAsDouble>(vm);
-}
-
-ContiguousJSValues JSObject::rageConvertDoubleToContiguous(VM& vm)
-{
- return genericConvertDoubleToContiguous<RageConvertDoubleToValue>(vm);
-}
-
-ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+ArrayStorage* JSObject::convertDoubleToArrayStorage(VM& vm, NonPropertyTransition transition)
{
DeferGC deferGC(vm.heap);
ASSERT(hasDouble(indexingType()));
-
- ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
- for (unsigned i = m_butterfly->publicLength(); i--;) {
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ for (unsigned i = 0; 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++;
+ if (value == value) {
+ newStorage->m_vector[i].setWithoutWriteBarrier(JSValue(JSValue::EncodeAsDouble, value));
+ newStorage->m_numValuesInVector++;
+ } else
+ ASSERT(newStorage->m_vector[i].get().isEmpty());
}
Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
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());
}
-ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition, unsigned neededLength)
+ArrayStorage* JSObject::convertContiguousToArrayStorage(VM& vm, NonPropertyTransition transition)
{
DeferGC deferGC(vm.heap);
ASSERT(hasContiguous(indexingType()));
-
- ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, neededLength);
- for (unsigned i = m_butterfly->publicLength(); i--;) {
+
+ unsigned vectorLength = m_butterfly->vectorLength();
+ ArrayStorage* newStorage = constructConvertedArrayStorageWithoutCopyingElements(vm, vectorLength);
+ for (unsigned i = 0; i < m_butterfly->publicLength(); i++) {
JSValue v = m_butterfly->contiguous()[i].get();
- if (!v)
- continue;
- newStorage->m_vector[i].setWithoutWriteBarrier(v);
- newStorage->m_numValuesInVector++;
+ if (v) {
+ newStorage->m_vector[i].setWithoutWriteBarrier(v);
+ newStorage->m_numValuesInVector++;
+ } else
+ ASSERT(newStorage->m_vector[i].get().isEmpty());
}
Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), transition);
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());
{
ASSERT(!value.isInt32());
- if (value.isDouble()) {
+ if (value.isDouble() && !std::isnan(value.asDouble())) {
convertInt32ToDouble(vm);
return;
}
}
}
-ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm, DoubleToContiguousMode mode)
+ContiguousJSValues JSObject::ensureContiguousSlow(VM& vm)
{
ASSERT(inherits(info()));
return convertInt32ToContiguous(vm);
case ALL_DOUBLE_INDEXING_TYPES:
- if (mode == RageConvertDoubleToValue)
- return rageConvertDoubleToContiguous(vm);
return convertDoubleToContiguous(vm);
case ALL_ARRAY_STORAGE_INDEXING_TYPES:
}
}
-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()));
return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
}
+void JSObject::putGetter(ExecState* exec, PropertyName propertyName, JSValue getter)
+{
+ PropertyDescriptor descriptor;
+ descriptor.setGetter(getter);
+ descriptor.setEnumerable(true);
+ descriptor.setConfigurable(true);
+ defineOwnProperty(this, exec, propertyName, descriptor, false);
+}
+
+void JSObject::putSetter(ExecState* exec, PropertyName propertyName, JSValue setter)
+{
+ PropertyDescriptor descriptor;
+ descriptor.setSetter(setter);
+ descriptor.setEnumerable(true);
+ descriptor.setConfigurable(true);
+ defineOwnProperty(this, exec, propertyName, descriptor, false);
+}
+
void JSObject::putDirectAccessor(ExecState* exec, PropertyName propertyName, JSValue value, unsigned attributes)
{
ASSERT(value.isGetterSetter() && (attributes & Accessor));
- unsigned index = propertyName.asIndex();
- if (index != PropertyName::NotAnIndex) {
- putDirectIndex(exec, index, value, attributes, PutDirectIndexLikePutDirect);
+ if (Optional<uint32_t> index = parseIndex(propertyName)) {
+ putDirectIndex(exec, index.value(), value, attributes, PutDirectIndexLikePutDirect);
return;
}
void JSObject::putDirectCustomAccessor(VM& vm, PropertyName propertyName, JSValue value, unsigned attributes)
{
- ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex);
+ ASSERT(!parseIndex(propertyName));
PutPropertySlot slot(this);
- putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot, getCallableObject(value));
+ putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
ASSERT(slot.type() == PutPropertySlot::NewProperty);
Structure* structure = this->structure(vm);
if (attributes & ReadOnly)
structure->setContainsReadOnlyProperties();
- structure->setHasCustomGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
+ 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, 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(vm), propertyName, attributes));
+ putDirectInternal<PutModeDefineOwnProperty>(vm, propertyName, value, attributes, slot);
Structure* structure = this->structure(vm);
if (attributes & ReadOnly)
structure->setContainsReadOnlyProperties();
- structure->setHasGetterSetterProperties(propertyName == vm.propertyNames->underscoreProto);
+ structure->setHasGetterSetterPropertiesWithProtoCheck(propertyName == vm.propertyNames->underscoreProto);
}
bool JSObject::hasProperty(ExecState* exec, PropertyName propertyName) const
{
JSObject* thisObject = jsCast<JSObject*>(cell);
- unsigned i = propertyName.asIndex();
- if (i != PropertyName::NotAnIndex)
- return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, i);
+ if (Optional<uint32_t> index = parseIndex(propertyName))
+ return thisObject->methodTable(exec->vm())->deletePropertyByIndex(thisObject, exec, index.value());
if (!thisObject->staticFunctionsReified())
thisObject->reifyStaticFunctionsForDelete(exec);
unsigned attributes;
- JSCell* specificValue;
VM& vm = exec->vm();
- if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes, specificValue))) {
+ if (isValidOffset(thisObject->structure(vm)->get(vm, propertyName, attributes))) {
if (attributes & DontDelete && !vm.isInDefineOwnProperty())
return false;
thisObject->removeDirect(vm, propertyName);
}
// Look in the static hashtable of properties
- const HashTableValue* entry = thisObject->findPropertyHashEntry(vm, propertyName);
+ const HashTableValue* entry = thisObject->findPropertyHashEntry(propertyName);
if (entry) {
if (entry->attributes() & DontDelete && !vm.isInDefineOwnProperty())
return false; // this builtin property can't be deleted
PutPropertySlot slot(thisObject);
- putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot);
+ if (!(entry->attributes() & BuiltinOrFunctionOrAccessor)) {
+ ASSERT(thisObject->staticFunctionsReified());
+ putEntry(exec, entry, thisObject, propertyName, jsUndefined(), slot);
+ }
}
return true;
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);
+ return const_cast<JSObject*>(this)->methodTable(exec->vm())->getOwnPropertySlotByIndex(const_cast<JSObject*>(this), exec, propertyName, slot);
+}
+
bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned i)
{
JSObject* thisObject = jsCast<JSObject*>(cell);
// 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);
return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("No default value")));
}
-const HashTableValue* JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const
+const HashTableValue* JSObject::findPropertyHashEntry(PropertyName propertyName) const
{
for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
- if (const HashTable* propHashTable = info->propHashTable(vm)) {
- if (const HashTableValue* entry = propHashTable->entry(vm, propertyName))
+ if (const HashTable* propHashTable = info->staticPropHashTable) {
+ if (const HashTableValue* entry = propHashTable->entry(propertyName))
return entry;
}
}
return defaultHasInstance(exec, value, get(exec, exec->propertyNames().prototype));
if (info.implementsHasInstance())
return methodTable(vm)->customHasInstance(this, exec, value);
- vm.throwException(exec, createInvalidParameterError(exec, "instanceof" , this));
+ vm.throwException(exec, createInvalidInstanceofParameterError(exec, this));
return false;
}
return false;
}
-bool JSObject::getPropertySpecificValue(ExecState* exec, PropertyName propertyName, JSCell*& specificValue) const
-{
- VM& vm = exec->vm();
- unsigned attributes;
- if (isValidOffset(structure(vm)->get(vm, propertyName, attributes, specificValue)))
- return true;
-
- // 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)
{
- propertyNames.setBaseObject(object);
object->methodTable(exec->vm())->getOwnPropertyNames(object, exec, propertyNames, mode);
if (object->prototype().isNull())
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,
for (unsigned i = 0; i < usedLength; ++i) {
if (!butterfly->contiguous()[i])
continue;
- propertyNames.add(Identifier::from(exec, i));
+ propertyNames.add(i);
}
break;
}
double value = butterfly->contiguousDouble()[i];
if (value != value)
continue;
- propertyNames.add(Identifier::from(exec, i));
+ propertyNames.add(i);
}
break;
}
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));
+ propertyNames.add(i);
}
if (SparseArrayValueMap* map = storage->m_sparseMap.get()) {
SparseArrayValueMap::const_iterator end = map->end();
for (SparseArrayValueMap::const_iterator it = map->begin(); it != end; ++it) {
- if (mode == IncludeDontEnumProperties || !(it->value.attributes & DontEnum))
+ 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(Identifier::from(exec, keys[i]));
+ propertyNames.add(keys[i]);
}
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
}
-
+
object->methodTable(exec->vm())->getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
}
{
getClassPropertyNames(exec, object->classInfo(), propertyNames, mode, object->staticFunctionsReified());
+ if (!mode.includeJSObjectProperties())
+ return;
+
VM& vm = exec->vm();
- bool canCachePropertiesFromStructure = !propertyNames.size();
object->structure(vm)->getPropertyNamesFromStructure(vm, propertyNames, mode);
-
- if (canCachePropertiesFromStructure)
- propertyNames.setNumCacheableSlotsForObject(object, propertyNames.size());
}
double JSObject::toNumber(ExecState* exec) const
return jsCast<JSObject*>(cell);
}
+bool JSObject::isCatchScopeObject() const
+{
+ return inherits(JSCatchScope::info());
+}
+
+bool JSObject::isFunctionNameScopeObject() const
+{
+ return inherits(JSFunctionNameScope::info());
+}
+
void JSObject::seal(VM& vm)
{
if (isSealed(vm))
// 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();
+ structure(vm)->setStaticFunctionsReified(true);
return;
}
setStructure(vm, Structure::toUncacheableDictionaryTransition(vm, structure(vm)));
for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
- const HashTable* hashTable = info->propHashTable(vm);
+ const HashTable* hashTable = info->staticPropHashTable;
if (!hashTable)
continue;
PropertySlot slot(this);
- for (auto iter = hashTable->begin(vm); iter != hashTable->end(vm); ++iter) {
- if (iter->attributes() & BuiltinOrFunction)
- setUpStaticFunctionSlot(globalObject()->globalExec(), iter.value(), this, Identifier(&vm, iter.key()), slot);
+ for (auto iter = hashTable->begin(); iter != hashTable->end(); ++iter) {
+ if (iter->attributes() & BuiltinOrFunctionOrAccessor)
+ setUpStaticFunctionSlot(globalObject()->globalExec(), iter.value(), this, Identifier::fromString(&vm, iter.key()), slot);
}
}
- structure(vm)->setStaticFunctionsReified();
+ structure(vm)->setStaticFunctionsReified(true);
}
bool JSObject::removeDirect(VM& vm, PropertyName propertyName)
else if (oldDescriptor.isAccessorDescriptor())
setter = oldDescriptor.setterObject();
- GetterSetter* accessor = GetterSetter::create(vm);
+ GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
if (getter)
- accessor->setGetter(vm, getter);
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), getter);
if (setter)
- accessor->setSetter(vm, setter);
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), setter);
entryInMap->set(vm, map, accessor);
entryInMap->attributes = descriptor.attributesOverridingCurrent(oldDescriptor) & ~ReadOnly;
}
}
+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->vectorLength() > length);
+ ASSERT(!m_butterfly->indexingHeader()->preCapacity(structure()));
+
+ DeferGC deferGC(vm.heap);
+ Butterfly* newButterfly = m_butterfly->resizeArray(vm, this, structure(), 0, ArrayStorage::sizeFor(length));
+ m_butterfly.set(vm, this, newButterfly);
+ m_butterfly->setVectorLength(length);
+ m_butterfly->setPublicLength(length);
+}
+
Butterfly* JSObject::growOutOfLineStorage(VM& vm, size_t oldSize, size_t newSize)
{
ASSERT(newSize > oldSize);
VM& vm = exec->vm();
if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
- GetterSetter* accessor = GetterSetter::create(vm);
+ GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
if (oldDescriptor.getterPresent())
- accessor->setGetter(vm, oldDescriptor.getterObject());
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
if (oldDescriptor.setterPresent())
- accessor->setSetter(vm, oldDescriptor.setterObject());
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
return true;
}
return true;
}
attributes &= ~ReadOnly;
- GetterSetter* accessor = GetterSetter::create(vm);
+ GetterSetter* accessor = GetterSetter::create(vm, exec->lexicalGlobalObject());
if (descriptor.getterPresent())
- accessor->setGetter(vm, descriptor.getterObject());
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), descriptor.getterObject());
else if (oldDescriptor.getterPresent())
- accessor->setGetter(vm, oldDescriptor.getterObject());
+ accessor->setGetter(vm, exec->lexicalGlobalObject(), oldDescriptor.getterObject());
if (descriptor.setterPresent())
- accessor->setSetter(vm, descriptor.setterObject());
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), descriptor.setterObject());
else if (oldDescriptor.setterPresent())
- accessor->setSetter(vm, oldDescriptor.setterObject());
+ accessor->setSetter(vm, exec->lexicalGlobalObject(), oldDescriptor.setterObject());
target->putDirectAccessor(exec, propertyName, accessor, attributes | Accessor);
return true;
void JSObject::putDirectMayBeIndex(ExecState* exec, PropertyName propertyName, JSValue value)
{
- unsigned asIndex = propertyName.asIndex();
- if (asIndex == PropertyName::NotAnIndex)
- putDirect(exec->vm(), propertyName, value);
+ if (Optional<uint32_t> index = parseIndex(propertyName))
+ putDirectIndex(exec, index.value(), value);
else
- putDirectIndex(exec, asIndex, value);
+ putDirect(exec->vm(), propertyName, value);
}
class DefineOwnPropertyScope {
if (!current.configurable()) {
if (descriptor.configurable()) {
if (throwException)
- exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to configurable attribute of unconfigurable property.")));
+ exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Attempting to change configurable attribute of unconfigurable property.")));
return false;
}
if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
if (!accessor)
return false;
GetterSetter* getterSetter;
+ bool getterSetterChanged = false;
if (accessor.isCustomGetterSetter())
- getterSetter = GetterSetter::create(exec->vm());
+ getterSetter = GetterSetter::create(exec->vm(), exec->lexicalGlobalObject());
else {
ASSERT(accessor.isGetterSetter());
getterSetter = asGetterSetter(accessor);
}
- if (descriptor.setterPresent())
- getterSetter->setSetter(exec->vm(), descriptor.setterObject());
- if (descriptor.getterPresent())
- getterSetter->setGetter(exec->vm(), descriptor.getterObject());
- if (current.attributesEqual(descriptor))
+ 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)
return true;
methodTable(exec->vm())->deleteProperty(this, exec, propertyName);
unsigned attrs = descriptor.attributesOverridingCurrent(current);
bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException)
{
// If it's an array index, then use the indexed property storage.
- unsigned index = propertyName.asIndex();
- if (index != PropertyName::NotAnIndex) {
+ if (Optional<uint32_t> index = parseIndex(propertyName)) {
// 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, descriptor, throwException);
+ return object->defineOwnIndexedProperty(exec, index.value(), descriptor, throwException);
}
return object->defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException);
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->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)
+{
+ 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);
+ }
+}
+
} // namespace JSC