/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 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 "JSGlobalObject.h"
#include "NativeErrorConstructor.h"
#include "ObjectPrototype.h"
+#include "PropertyDescriptor.h"
#include "PropertyNameArray.h"
#include "Lookup.h"
#include "Nodes.h"
#include <math.h>
#include <wtf/Assertions.h>
-#define JSOBJECT_MARK_TRACING 0
-
-#if JSOBJECT_MARK_TRACING
-
-#define JSOBJECT_MARK_BEGIN() \
- static int markStackDepth = 0; \
- for (int i = 0; i < markStackDepth; i++) \
- putchar('-'); \
- printf("%s (%p)\n", className().UTF8String().c_str(), this); \
- markStackDepth++; \
-
-#define JSOBJECT_MARK_END() \
- markStackDepth--;
-
-#else // JSOBJECT_MARK_TRACING
-
-#define JSOBJECT_MARK_BEGIN()
-#define JSOBJECT_MARK_END()
-
-#endif // JSOBJECT_MARK_TRACING
-
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(JSObject);
-void JSObject::mark()
-{
- JSOBJECT_MARK_BEGIN();
+static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ // Add properties from the static hashtables of properties
+ for (; classInfo; classInfo = classInfo->parentClass) {
+ const HashTable* table = classInfo->propHashTable(exec);
+ if (!table)
+ continue;
+ table->initializeIfNeeded(exec);
+ ASSERT(table->table);
+
+ int hashSizeMask = table->compactSize - 1;
+ const HashEntry* entry = table->table;
+ for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
+ if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
+ propertyNames.add(entry->key());
+ }
+ }
+}
- JSCell::mark();
- m_structure->mark();
+void JSObject::markChildren(MarkStack& markStack)
+{
+#ifndef NDEBUG
+ bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
+ markStack.m_isCheckingForDefaultMarkViolation = false;
+#endif
- size_t storageSize = m_structure->propertyStorageSize();
- for (size_t i = 0; i < storageSize; ++i) {
- JSValuePtr v = m_propertyStorage[i];
- if (!v.marked())
- v.mark();
- }
+ markChildrenDirect(markStack);
- JSOBJECT_MARK_END();
+#ifndef NDEBUG
+ markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
+#endif
}
UString JSObject::className() const
}
// ECMA 8.6.2.2
-void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
+void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
{
ASSERT(value);
ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
if (!value.isObject() && !value.isNull())
return;
- JSValuePtr nextPrototypeValue = value;
+ JSValue nextPrototypeValue = value;
while (nextPrototypeValue && nextPrototypeValue.isObject()) {
JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
if (nextPrototype == this) {
}
// Check if there are any setters or getters in the prototype chain
- JSValuePtr prototype;
+ JSValue prototype;
for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
prototype = obj->prototype();
if (prototype.isNull()) {
- putDirect(propertyName, value, 0, true, slot);
+ putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
return;
}
}
unsigned attributes;
- if ((m_structure->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
+ JSCell* specificValue;
+ if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
return;
for (JSObject* obj = this; ; obj = asObject(prototype)) {
- if (JSValuePtr gs = obj->getDirect(propertyName)) {
+ if (JSValue gs = obj->getDirect(propertyName)) {
if (gs.isGetterSetter()) {
JSObject* setterFunc = asGetterSetter(gs)->setter();
if (!setterFunc) {
CallData callData;
CallType callType = setterFunc->getCallData(callData);
- ArgList args;
+ MarkedArgumentBuffer args;
args.append(value);
call(exec, setterFunc, callType, callData, this, args);
return;
break;
}
- putDirect(propertyName, value, 0, true, slot);
+ putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
return;
}
-void JSObject::put(ExecState* exec, unsigned propertyName, JSValuePtr value)
+void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
{
PutPropertySlot slot;
put(exec, Identifier::from(exec, propertyName), value, slot);
}
-void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValuePtr value, unsigned attributes)
+void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
{
- putDirect(propertyName, value, attributes);
+ putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
}
-void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValuePtr value, unsigned attributes)
+void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
+{
+ putDirectInternal(exec->globalData(), propertyName, value, attributes);
+}
+
+void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
{
putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
}
bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
{
unsigned attributes;
- if (m_structure->get(propertyName, attributes) != WTF::notFound) {
+ JSCell* specificValue;
+ if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
if ((attributes & DontDelete))
return false;
removeDirect(propertyName);
return deleteProperty(exec, Identifier::from(exec, propertyName));
}
-static ALWAYS_INLINE JSValuePtr callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
+static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
{
- JSValuePtr function = object->get(exec, propertyName);
+ JSValue function = object->get(exec, propertyName);
CallData callData;
CallType callType = function.getCallData(callData);
if (callType == CallTypeNone)
if (exec->hadException())
return exec->exception();
- JSValuePtr result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
+ JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
ASSERT(!result.isGetterSetter());
if (exec->hadException())
return exec->exception();
if (result.isObject())
- return noValue();
+ return JSValue();
return result;
}
-bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValuePtr& result)
+bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
{
result = defaultValue(exec, PreferNumber);
number = result.toNumber(exec);
}
// ECMA 8.6.2.6
-JSValuePtr JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
+JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
{
// Must call toString first for Date objects.
if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
- JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
+ JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
if (value)
return value;
value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
if (value)
return value;
} else {
- JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
+ JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
if (value)
return value;
value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
return 0;
}
-void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
+void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
{
- JSValuePtr object = getDirect(propertyName);
+ JSValue object = getDirect(propertyName);
if (object && object.isGetterSetter()) {
ASSERT(m_structure->hasGetterSetterProperties());
asGetterSetter(object)->setGetter(getterFunction);
}
PutPropertySlot slot;
- GetterSetter* getterSetter = new (exec) GetterSetter;
- putDirect(propertyName, getterSetter, None, true, slot);
+ GetterSetter* getterSetter = new (exec) GetterSetter(exec);
+ putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
// putDirect will change our Structure if we add a new property. For
// getters and setters, though, we also need to change our Structure
getterSetter->setGetter(getterFunction);
}
-void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
+void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
{
- JSValuePtr object = getDirect(propertyName);
+ JSValue object = getDirect(propertyName);
if (object && object.isGetterSetter()) {
ASSERT(m_structure->hasGetterSetterProperties());
asGetterSetter(object)->setSetter(setterFunction);
}
PutPropertySlot slot;
- GetterSetter* getterSetter = new (exec) GetterSetter;
- putDirect(propertyName, getterSetter, None, true, slot);
+ GetterSetter* getterSetter = new (exec) GetterSetter(exec);
+ putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
// putDirect will change our Structure if we add a new property. For
// getters and setters, though, we also need to change our Structure
getterSetter->setSetter(setterFunction);
}
-JSValuePtr JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
+JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
{
JSObject* object = this;
while (true) {
- if (JSValuePtr value = object->getDirect(propertyName)) {
+ if (JSValue value = object->getDirect(propertyName)) {
if (!value.isGetterSetter())
return jsUndefined();
JSObject* functionObject = asGetterSetter(value)->getter();
}
}
-JSValuePtr JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
+JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
{
JSObject* object = this;
while (true) {
- if (JSValuePtr value = object->getDirect(propertyName)) {
+ if (JSValue value = object->getDirect(propertyName)) {
if (!value.isGetterSetter())
return jsUndefined();
JSObject* functionObject = asGetterSetter(value)->setter();
}
}
-bool JSObject::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr proto)
+bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
{
+ if (!value.isObject())
+ return false;
+
if (!proto.isObject()) {
throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
return false;
}
- if (!value.isObject())
- return false;
-
JSObject* object = asObject(value);
while ((object = object->prototype().getObject())) {
if (proto == object)
bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
{
- unsigned attributes;
- if (!getPropertyAttributes(exec, propertyName, attributes))
+ PropertyDescriptor descriptor;
+ if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
return false;
- return !(attributes & DontEnum);
+ return descriptor.enumerable();
}
-bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
+bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
{
- if (m_structure->get(propertyName, attributes) != WTF::notFound)
- return true;
-
- // Look in the static hashtable of properties
- const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
- if (entry) {
- attributes = entry->attributes();
+ unsigned attributes;
+ if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
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(ExecState* exec, PropertyNameArray& propertyNames)
+void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ getOwnPropertyNames(exec, propertyNames, mode);
+
+ if (prototype().isNull())
+ return;
+
+ JSObject* prototype = asObject(this->prototype());
+ while(1) {
+ if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
+ prototype->getPropertyNames(exec, propertyNames, mode);
+ break;
+ }
+ prototype->getOwnPropertyNames(exec, propertyNames, mode);
+ JSValue nextProto = prototype->prototype();
+ if (nextProto.isNull())
+ break;
+ prototype = asObject(nextProto);
+ }
+}
+
+void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
{
- m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
+ m_structure->getPropertyNames(propertyNames, mode);
+ getClassPropertyNames(exec, classInfo(), propertyNames, mode);
}
bool JSObject::toBoolean(ExecState*) const
double JSObject::toNumber(ExecState* exec) const
{
- JSValuePtr primitive = toPrimitive(exec, PreferNumber);
+ JSValue primitive = toPrimitive(exec, PreferNumber);
if (exec->hadException()) // should be picked up soon in Nodes.cpp
return 0.0;
return primitive.toNumber(exec);
UString JSObject::toString(ExecState* exec) const
{
- JSValuePtr primitive = toPrimitive(exec, PreferString);
+ JSValue primitive = toPrimitive(exec, PreferString);
if (exec->hadException())
return "";
return primitive.toString(exec);
void JSObject::removeDirect(const Identifier& propertyName)
{
size_t offset;
- if (m_structure->isDictionary()) {
+ if (m_structure->isUncacheableDictionary()) {
offset = m_structure->removePropertyWithoutTransition(propertyName);
if (offset != WTF::notFound)
- m_propertyStorage[offset] = jsUndefined();
+ putDirectOffset(offset, jsUndefined());
return;
}
RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
- if (offset != WTF::notFound)
- m_propertyStorage[offset] = jsUndefined();
setStructure(structure.release());
+ if (offset != WTF::notFound)
+ putDirectOffset(offset, jsUndefined());
}
void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
{
- putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
+ putDirectFunction(Identifier(exec, function->name(exec)), function, attr);
}
void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
{
- putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
+ putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr);
}
-NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValuePtr* location)
+NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
{
if (JSObject* getterFunction = asGetterSetter(*location)->getter())
slot.setGetterSlot(getterFunction);
allocatePropertyStorageInline(oldSize, newSize);
}
-JSObject* constructEmptyObject(ExecState* exec)
+bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
+{
+ unsigned attributes = 0;
+ JSCell* cell = 0;
+ size_t offset = m_structure->get(propertyName, attributes, cell);
+ if (offset == WTF::notFound)
+ return false;
+ descriptor.setDescriptor(getDirectOffset(offset), attributes);
+ return true;
+}
+
+bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
+{
+ JSObject* object = this;
+ while (true) {
+ if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
+ return true;
+ JSValue prototype = object->prototype();
+ if (!prototype.isObject())
+ return false;
+ object = asObject(prototype);
+ }
+}
+
+static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
{
- return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
+ if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
+ target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
+ return true;
+ }
+ attributes &= ~ReadOnly;
+ if (descriptor.getter() && descriptor.getter().isObject())
+ target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
+ if (exec->hadException())
+ return false;
+ if (descriptor.setter() && descriptor.setter().isObject())
+ target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
+ return !exec->hadException();
+}
+
+bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
+{
+ // If we have a new property we can just put it on normally
+ PropertyDescriptor current;
+ if (!getOwnPropertyDescriptor(exec, propertyName, current))
+ return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
+
+ if (descriptor.isEmpty())
+ return true;
+
+ if (current.equalTo(exec, descriptor))
+ return true;
+
+ // Filter out invalid changes
+ if (!current.configurable()) {
+ if (descriptor.configurable()) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
+ return false;
+ }
+ if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
+ return false;
+ }
+ }
+
+ // A generic descriptor is simply changing the attributes of an existing property
+ if (descriptor.isGenericDescriptor()) {
+ if (!current.attributesEqual(descriptor)) {
+ deleteProperty(exec, propertyName);
+ putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
+ }
+ return true;
+ }
+
+ // Changing between a normal property or an accessor property
+ if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
+ if (!current.configurable()) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
+ return false;
+ }
+ deleteProperty(exec, propertyName);
+ return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
+ }
+
+ // Changing the value and attributes of an existing property
+ if (descriptor.isDataDescriptor()) {
+ if (!current.configurable()) {
+ if (!current.writable() && descriptor.writable()) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
+ return false;
+ }
+ if (!current.writable()) {
+ if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change value of a readonly property.");
+ return false;
+ }
+ }
+ } else if (current.attributesEqual(descriptor)) {
+ if (!descriptor.value())
+ return true;
+ PutPropertySlot slot;
+ put(exec, propertyName, descriptor.value(), slot);
+ if (exec->hadException())
+ return false;
+ return true;
+ }
+ deleteProperty(exec, propertyName);
+ return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
+ }
+
+ // Changing the accessor functions of an existing accessor property
+ ASSERT(descriptor.isAccessorDescriptor());
+ if (!current.configurable()) {
+ if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
+ return false;
+ }
+ if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
+ if (throwException)
+ throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
+ return false;
+ }
+ }
+ JSValue accessor = getDirect(propertyName);
+ if (!accessor)
+ return false;
+ GetterSetter* getterSetter = asGetterSetter(accessor);
+ if (current.attributesEqual(descriptor)) {
+ if (descriptor.setter())
+ getterSetter->setSetter(asObject(descriptor.setter()));
+ if (descriptor.getter())
+ getterSetter->setGetter(asObject(descriptor.getter()));
+ return true;
+ }
+ deleteProperty(exec, propertyName);
+ unsigned attrs = current.attributesWithOverride(descriptor);
+ if (descriptor.setter())
+ attrs |= Setter;
+ if (descriptor.getter())
+ attrs |= Getter;
+ putDirect(propertyName, getterSetter, attrs);
+ return true;
}
} // namespace JSC