]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/JSObject.cpp
JavaScriptCore-721.26.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.cpp
index b13f143a6ec0834e47a95968c554c59a8739d51b..61d3bb145767b676a9a0aa280fbcfec663c96b78 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  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
@@ -30,6 +30,7 @@
 #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();
-
-    JSCell::mark();
-    m_structure->mark();
+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());
+        }
+    }
+}
 
-    PropertyStorage storage = propertyStorage();
+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) {
-        JSValue v = JSValue::decode(storage[i]);
-        if (!v.marked())
-            v.mark();
-    }
+    markChildrenDirect(markStack);
 
-    JSOBJECT_MARK_END();
+#ifndef NDEBUG
+    markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
+#endif
 }
 
 UString JSObject::className() const
@@ -300,7 +294,7 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifi
     return 0;
 }
 
-void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
+void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
 {
     JSValue object = getDirect(propertyName);
     if (object && object.isGetterSetter()) {
@@ -310,8 +304,8 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO
     }
 
     PutPropertySlot slot;
-    GetterSetter* getterSetter = new (exec) GetterSetter;
-    putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, 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
@@ -327,7 +321,7 @@ void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSO
     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)
 {
     JSValue object = getDirect(propertyName);
     if (object && object.isGetterSetter()) {
@@ -337,8 +331,8 @@ void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSO
     }
 
     PutPropertySlot slot;
-    GetterSetter* getterSetter = new (exec) GetterSetter;
-    putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, 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
@@ -412,26 +406,10 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
 
 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);
-}
-
-bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
-{
-    JSCell* specificValue;
-    if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
-        return true;
-    
-    // Look in the static hashtable of properties
-    const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
-    if (entry) {
-        attributes = entry->attributes();
-        return true;
-    }
-    
-    return false;
+    return descriptor.enumerable();
 }
 
 bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
@@ -447,9 +425,31 @@ bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyNa
     return false;
 }
 
-void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
-    m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
+    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->getPropertyNames(propertyNames, mode);
+    getClassPropertyNames(exec, classInfo(), propertyNames, mode);
 }
 
 bool JSObject::toBoolean(ExecState*) const
@@ -506,19 +506,22 @@ void JSObject::removeDirect(const Identifier& propertyName)
 
 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
 {
-    putDirectFunction(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)
 {
-    putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
+    putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr);
 }
 
 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
 {
-    if (JSObject* getterFunction = asGetterSetter(*location)->getter())
-        slot.setGetterSlot(getterFunction);
-    else
+    if (JSObject* getterFunction = asGetterSetter(*location)->getter()) {
+        if (!structure()->isDictionary())
+            slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
+        else
+            slot.setGetterSlot(getterFunction);
+    } else
         slot.setUndefined();
 }
 
@@ -533,9 +536,154 @@ void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
     allocatePropertyStorageInline(oldSize, newSize);
 }
 
-JSObject* constructEmptyObject(ExecState* exec)
+bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
 {
-    return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
+    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)
+{
+    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