[ES5] Implement Object.keys
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 13 Sep 2009 02:44:32 +0000 (02:44 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 13 Sep 2009 02:44:32 +0000 (02:44 +0000)
https://bugs.webkit.org/show_bug.cgi?id=29170

Reviewed by Maciej Stachowiak.

This patch basically requires two separate steps, the first is to split getPropertyNames
into two functions -- getOwnPropertyNames and getPropertyNames, basically making them behave
in the same way as getOwnPropertySlot and getPropertySlot.  In essence getOwnPropertyNames
produces the list of properties on an object excluding its prototype chain and getPropertyNames
just iterates the the object and its prototype chain calling getOwnPropertyNames at each level.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@48336 268f45cc-cd09-0410-ab3c-d52691b4dbfc

44 files changed:
JavaScriptCore/API/JSCallbackObject.h
JavaScriptCore/API/JSCallbackObjectFunctions.h
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/debugger/DebuggerActivation.cpp
JavaScriptCore/debugger/DebuggerActivation.h
JavaScriptCore/runtime/CommonIdentifiers.h
JavaScriptCore/runtime/JSArray.cpp
JavaScriptCore/runtime/JSArray.h
JavaScriptCore/runtime/JSByteArray.cpp
JavaScriptCore/runtime/JSByteArray.h
JavaScriptCore/runtime/JSNotAnObject.cpp
JavaScriptCore/runtime/JSNotAnObject.h
JavaScriptCore/runtime/JSObject.cpp
JavaScriptCore/runtime/JSObject.h
JavaScriptCore/runtime/JSVariableObject.cpp
JavaScriptCore/runtime/JSVariableObject.h
JavaScriptCore/runtime/ObjectConstructor.cpp
JavaScriptCore/runtime/RegExpMatchesArray.h
JavaScriptCore/runtime/StringObject.cpp
JavaScriptCore/runtime/StringObject.h
JavaScriptCore/runtime/Structure.cpp
JavaScriptCore/runtime/Structure.h
JavaScriptGlue/ChangeLog
JavaScriptGlue/UserObjectImp.cpp
JavaScriptGlue/UserObjectImp.h
LayoutTests/ChangeLog
LayoutTests/fast/js/Object-keys-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/Object-keys.html [new file with mode: 0644]
LayoutTests/fast/js/resources/Object-keys.js [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-enumeration-expected.txt
LayoutTests/http/tests/security/cross-frame-access-enumeration.html
WebCore/ChangeLog
WebCore/bindings/js/JSDOMWindowCustom.cpp
WebCore/bindings/js/JSDOMWindowShell.cpp
WebCore/bindings/js/JSDOMWindowShell.h
WebCore/bindings/js/JSHistoryCustom.cpp
WebCore/bindings/js/JSLocationCustom.cpp
WebCore/bindings/js/JSQuarantinedObjectWrapper.cpp
WebCore/bindings/js/JSQuarantinedObjectWrapper.h
WebCore/bindings/js/JSStorageCustom.cpp
WebCore/bindings/scripts/CodeGeneratorJS.pm
WebCore/bridge/runtime_object.cpp
WebCore/bridge/runtime_object.h

index 9d22ad9..47fd6c3 100644 (file)
@@ -66,7 +66,7 @@ private:
 
     virtual bool hasInstance(ExecState* exec, JSValue value, JSValue proto);
 
-    virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+    virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
     virtual double toNumber(ExecState*) const;
     virtual UString toString(ExecState*) const;
index 1abed3f..4d113fe 100644 (file)
@@ -373,7 +373,7 @@ JSValue JSCallbackObject<Base>::call(ExecState* exec, JSObject* functionObject,
 }
 
 template <class Base>
-void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSCallbackObject<Base>::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     JSContextRef execRef = toRef(exec);
     JSObjectRef thisRef = toRef(this);
@@ -407,7 +407,7 @@ void JSCallbackObject<Base>::getPropertyNames(ExecState* exec, PropertyNameArray
         }
     }
     
-    Base::getPropertyNames(exec, propertyNames);
+    Base::getOwnPropertyNames(exec, propertyNames);
 }
 
 template <class Base>
index e2e148f..c1cfd07 100644 (file)
@@ -1,3 +1,52 @@
+2009-09-12  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Maciej Stachowiak.
+
+        [ES5] Implement Object.keys
+        https://bugs.webkit.org/show_bug.cgi?id=29170
+
+        This patch basically requires two separate steps, the first is to split getPropertyNames
+        into two functions -- getOwnPropertyNames and getPropertyNames, basically making them behave
+        in the same way as getOwnPropertySlot and getPropertySlot.  In essence getOwnPropertyNames
+        produces the list of properties on an object excluding its prototype chain and getPropertyNames
+        just iterates the the object and its prototype chain calling getOwnPropertyNames at each level.
+
+        * API/JSCallbackObject.h:
+        * API/JSCallbackObjectFunctions.h:
+        (JSC::::getOwnPropertyNames):
+        * JavaScriptCore.exp:
+        * debugger/DebuggerActivation.cpp:
+        (JSC::DebuggerActivation::getOwnPropertyNames):
+        * debugger/DebuggerActivation.h:
+        * runtime/CommonIdentifiers.h:
+        * runtime/JSArray.cpp:
+        (JSC::JSArray::getOwnPropertyNames):
+        * runtime/JSArray.h:
+        * runtime/JSByteArray.cpp:
+        (JSC::JSByteArray::getOwnPropertyNames):
+        * runtime/JSByteArray.h:
+        * runtime/JSNotAnObject.cpp:
+        (JSC::JSNotAnObject::getOwnPropertyNames):
+        * runtime/JSNotAnObject.h:
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::getOwnPropertyNames):
+        * runtime/JSObject.h:
+        * runtime/JSVariableObject.cpp:
+        (JSC::JSVariableObject::getOwnPropertyNames):
+        * runtime/JSVariableObject.h:
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ObjectConstructor::ObjectConstructor):
+        (JSC::objectConstructorKeys):
+        * runtime/RegExpMatchesArray.h:
+        (JSC::RegExpMatchesArray::getOwnPropertyNames):
+        * runtime/StringObject.cpp:
+        (JSC::StringObject::getOwnPropertyNames):
+        * runtime/StringObject.h:
+        * runtime/Structure.cpp:
+        (JSC::Structure::getOwnEnumerablePropertyNames):
+        (JSC::Structure::getEnumerablePropertyNames):
+        * runtime/Structure.h:
+
 2009-09-11  Oliver Hunt  <oliver@apple.com>
 
         Reviewed by Sam Weinig.
index cd80d74..0751ccb 100644 (file)
@@ -113,9 +113,9 @@ __ZN3JSC12SamplingTool4dumpEPNS_9ExecStateE
 __ZN3JSC12SamplingTool5setupEv
 __ZN3JSC12SmallStrings17createEmptyStringEPNS_12JSGlobalDataE
 __ZN3JSC12StringObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE
-__ZN3JSC12StringObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
 __ZN3JSC12StringObject18getOwnPropertySlotEPNS_9ExecStateERKNS_10IdentifierERNS_12PropertySlotE
 __ZN3JSC12StringObject18getOwnPropertySlotEPNS_9ExecStateEjRNS_12PropertySlotE
+__ZN3JSC12StringObject19getOwnPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
 __ZN3JSC12StringObject24getOwnPropertyDescriptorEPNS_9ExecStateERKNS_10IdentifierERNS_18PropertyDescriptorE
 __ZN3JSC12StringObject3putEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueERNS_15PutPropertySlotE
 __ZN3JSC12StringObject4infoE
@@ -147,7 +147,7 @@ __ZN3JSC16InternalFunction4nameEPNS_12JSGlobalDataE
 __ZN3JSC16InternalFunctionC2EPNS_12JSGlobalDataEN3WTF10PassRefPtrINS_9StructureEEERKNS_10IdentifierE
 __ZN3JSC16JSVariableObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE
 __ZN3JSC16JSVariableObject14symbolTableGetERKNS_10IdentifierERNS_18PropertyDescriptorE
-__ZN3JSC16JSVariableObject16getPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
+__ZN3JSC16JSVariableObject19getOwnPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
 __ZN3JSC16toUInt32SlowCaseEdRb
 __ZN3JSC17BytecodeGenerator21setDumpsGeneratedCodeEb
 __ZN3JSC17PropertyNameArray3addEPNS_7UString3RepE
@@ -250,6 +250,7 @@ __ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierENS_7JSValu
 __ZN3JSC8JSObject17putWithAttributesEPNS_9ExecStateEjNS_7JSValueEj
 __ZN3JSC8JSObject18getOwnPropertySlotEPNS_9ExecStateEjRNS_12PropertySlotE
 __ZN3JSC8JSObject18getPrimitiveNumberEPNS_9ExecStateERdRNS_7JSValueE
+__ZN3JSC8JSObject19getOwnPropertyNamesEPNS_9ExecStateERNS_17PropertyNameArrayE
 __ZN3JSC8JSObject21getPropertyDescriptorEPNS_9ExecStateERKNS_10IdentifierERNS_18PropertyDescriptorE
 __ZN3JSC8JSObject22fillGetterPropertySlotERNS_12PropertySlotEPNS_7JSValueE  
 __ZN3JSC8JSObject23allocatePropertyStorageEmm
index 34d0447..7a68d7d 100644 (file)
@@ -71,7 +71,7 @@ bool DebuggerActivation::deleteProperty(ExecState* exec, const Identifier& prope
     return m_activation->deleteProperty(exec, propertyName);
 }
 
-void DebuggerActivation::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void DebuggerActivation::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     m_activation->getPropertyNames(exec, propertyNames);
 }
index 7525a96..06aea5a 100644 (file)
@@ -42,7 +42,7 @@ namespace JSC {
         virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
         virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue, unsigned attributes);
         virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
-        virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
         virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const;
         virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction);
         virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction);
index 8d5d57a..8493d73 100644 (file)
@@ -53,6 +53,7 @@
     macro(input) \
     macro(isArray) \
     macro(isPrototypeOf) \
+    macro(keys) \
     macro(length) \
     macro(message) \
     macro(multiline) \
index 4fb8870..1fcca81 100644 (file)
@@ -464,7 +464,7 @@ bool JSArray::deleteProperty(ExecState* exec, unsigned i)
     return false;
 }
 
-void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     // FIXME: Filling PropertyNameArray with an identifier for every integer
     // is incredibly inefficient for large arrays. We need a different approach,
@@ -484,7 +484,7 @@ void JSArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames
             propertyNames.add(Identifier::from(exec, it->first));
     }
 
-    JSObject::getPropertyNames(exec, propertyNames);
+    JSObject::getOwnPropertyNames(exec, propertyNames);
 }
 
 bool JSArray::increaseVectorLength(unsigned newLength)
index ed302d7..4f2f86a 100644 (file)
@@ -91,7 +91,7 @@ namespace JSC {
         virtual void put(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
         virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
         virtual bool deleteProperty(ExecState*, unsigned propertyName);
-        virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
         virtual void markChildren(MarkStack&);
 
         void* lazyCreationData();
index a30f238..421c440 100644 (file)
@@ -96,12 +96,12 @@ void JSByteArray::put(ExecState* exec, unsigned propertyName, JSValue value)
     setIndex(exec, propertyName, value);
 }
 
-void JSByteArray::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSByteArray::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     unsigned length = m_storage->length();
     for (unsigned i = 0; i < length; ++i)
         propertyNames.add(Identifier::from(exec, i));
-    JSObject::getPropertyNames(exec, propertyNames);
+    JSObject::getOwnPropertyNames(exec, propertyNames);
 }
 
 }
index 66fb67e..016c677 100644 (file)
@@ -82,7 +82,7 @@ namespace JSC {
         virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, JSC::PutPropertySlot&);
         virtual void put(JSC::ExecState*, unsigned propertyName, JSC::JSValue);
 
-        virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
+        virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
 
         virtual const ClassInfo* classInfo() const { return m_classInfo; }
         static const ClassInfo s_defaultInfo;
index f9982b8..c36dc10 100644 (file)
@@ -121,7 +121,7 @@ bool JSNotAnObject::deleteProperty(ExecState* exec, unsigned)
     return false;
 }
 
-void JSNotAnObject::getPropertyNames(ExecState* exec, PropertyNameArray&)
+void JSNotAnObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray&)
 {
     ASSERT_UNUSED(exec, exec->hadException() && exec->exception() == m_exception);
 }
index c3949d8..0d9aca6 100644 (file)
@@ -88,7 +88,7 @@ namespace JSC {
         virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
         virtual bool deleteProperty(ExecState*, unsigned propertyName);
 
-        virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
         JSNotAnObjectErrorStub* m_exception;
     };
index 6c01a84..f910603 100644 (file)
@@ -427,6 +427,11 @@ void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyName
     m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
 }
 
+void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+{
+    m_structure->getOwnEnumerablePropertyNames(exec, propertyNames, this);
+}
+
 bool JSObject::toBoolean(ExecState*) const
 {
     return true;
index 7686dd3..9f44c24 100644 (file)
@@ -123,6 +123,7 @@ namespace JSC {
         virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty);
 
         virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
         virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
         virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value);
index a92e7c0..6586393 100644 (file)
@@ -42,7 +42,7 @@ bool JSVariableObject::deleteProperty(ExecState* exec, const Identifier& propert
     return JSObject::deleteProperty(exec, propertyName);
 }
 
-void JSVariableObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSVariableObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     SymbolTable::const_iterator end = symbolTable().end();
     for (SymbolTable::const_iterator it = symbolTable().begin(); it != end; ++it) {
@@ -50,7 +50,7 @@ void JSVariableObject::getPropertyNames(ExecState* exec, PropertyNameArray& prop
             propertyNames.add(Identifier(exec, it->first.get()));
     }
     
-    JSObject::getPropertyNames(exec, propertyNames);
+    JSObject::getOwnPropertyNames(exec, propertyNames);
 }
 
 bool JSVariableObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
index 86998ed..7c80d36 100644 (file)
@@ -49,7 +49,7 @@ namespace JSC {
         virtual void putWithAttributes(ExecState*, const Identifier&, JSValue, unsigned attributes) = 0;
 
         virtual bool deleteProperty(ExecState*, const Identifier&);
-        virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
         
         virtual bool isVariableObject() const;
         virtual bool isDynamicScope() const = 0;
index 5a34f20..ab44763 100644 (file)
@@ -34,6 +34,7 @@ ASSERT_CLASS_FITS_IN_CELL(ObjectConstructor);
 
 static JSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState*, JSObject*, JSValue, const ArgList&);
 static JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, JSValue, const ArgList&);
+static JSValue JSC_HOST_CALL objectConstructorKeys(ExecState*, JSObject*, JSValue, const ArgList&);
 
 ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> structure, ObjectPrototype* objectPrototype, Structure* prototypeFunctionStructure)
 : InternalFunction(&exec->globalData(), structure, Identifier(exec, "Object"))
@@ -46,6 +47,7 @@ ObjectConstructor::ObjectConstructor(ExecState* exec, PassRefPtr<Structure> stru
     
     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().getPrototypeOf, objectConstructorGetPrototypeOf), DontEnum);
     putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 2, exec->propertyNames().getOwnPropertyDescriptor, objectConstructorGetOwnPropertyDescriptor), DontEnum);
+    putDirectFunctionWithoutTransition(exec, new (exec) NativeFunctionWrapper(exec, prototypeFunctionStructure, 1, exec->propertyNames().keys, objectConstructorKeys), DontEnum);
 }
 
 // ECMA 15.2.2
@@ -116,4 +118,17 @@ JSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec,
     return description;
 }
 
+JSValue JSC_HOST_CALL objectConstructorKeys(ExecState* exec, JSObject*, JSValue, const ArgList& args)
+{
+    if (!args.at(0).isObject())
+        return throwError(exec, TypeError, "Requested keys of a value that is not an object.");
+    PropertyNameArray properties(exec);
+    asObject(args.at(0))->getOwnPropertyNames(exec, properties);
+    JSArray* keys = constructEmptyArray(exec);
+    size_t numProperties = properties.size();
+    for (size_t i = 0; i < numProperties; i++)
+        keys->push(exec, jsOwnedString(exec, properties[i].ustring()));
+    return keys;
+}
+
 } // namespace JSC
index 4448f57..829f7cf 100644 (file)
@@ -79,11 +79,11 @@ namespace JSC {
             return JSArray::deleteProperty(exec, propertyName);
         }
 
-        virtual void getPropertyNames(ExecState* exec, PropertyNameArray& arr)
+        virtual void getOwnPropertyNames(ExecState* exec, PropertyNameArray& arr)
         {
             if (lazyCreationData())
                 fillArrayInstance(exec);
-            JSArray::getPropertyNames(exec, arr);
+            JSArray::getOwnPropertyNames(exec, arr);
         }
 
         void fillArrayInstance(ExecState*);
index 6b7dd67..465435c 100644 (file)
@@ -82,12 +82,12 @@ bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyNam
     return JSObject::deleteProperty(exec, propertyName);
 }
 
-void StringObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     int size = internalValue()->value().size();
     for (int i = 0; i < size; ++i)
         propertyNames.add(Identifier(exec, UString::from(i)));
-    return JSObject::getPropertyNames(exec, propertyNames);
+    return JSObject::getOwnPropertyNames(exec, propertyNames);
 }
 
 } // namespace JSC
index ceb307a..5a889da 100644 (file)
@@ -39,7 +39,7 @@ namespace JSC {
 
         virtual void put(ExecState* exec, const Identifier& propertyName, JSValue, PutPropertySlot&);
         virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
-        virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+        virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
         virtual const ClassInfo* classInfo() const { return &info; }
         static const JS_EXPORTDATA ClassInfo info;
index caad993..f409989 100644 (file)
@@ -273,6 +273,12 @@ void Structure::materializePropertyMap()
     }
 }
 
+void Structure::getOwnEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
+{
+    getEnumerableNamesFromPropertyTable(propertyNames);
+    getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
+}
+
 void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& propertyNames, JSObject* baseObject)
 {
     bool shouldCache = propertyNames.shouldCache() && !(propertyNames.size() || m_isDictionary);
@@ -285,12 +291,22 @@ void Structure::getEnumerablePropertyNames(ExecState* exec, PropertyNameArray& p
         clearEnumerationCache();
     }
 
-    getEnumerableNamesFromPropertyTable(propertyNames);
-    getEnumerableNamesFromClassInfoTable(exec, baseObject->classInfo(), propertyNames);
+    baseObject->getOwnPropertyNames(exec, propertyNames);
 
     if (m_prototype.isObject()) {
         propertyNames.setShouldCache(false); // No need for our prototypes to waste memory on caching, since they're not being enumerated directly.
-        asObject(m_prototype)->getPropertyNames(exec, propertyNames);
+        JSObject* prototype = asObject(m_prototype);
+        while(1) {
+            if (!prototype->structure()->typeInfo().hasDefaultGetPropertyNames()) {
+                prototype->getPropertyNames(exec, propertyNames);
+                break;
+            }
+            prototype->getOwnPropertyNames(exec, propertyNames);
+            JSValue nextProto = prototype->prototype();
+            if (!nextProto.isObject())
+                break;
+            prototype = asObject(nextProto);
+        }
     }
 
     if (shouldCache) {
index 920e56b..a1821c2 100644 (file)
@@ -114,6 +114,7 @@ namespace JSC {
         }
 
         void getEnumerablePropertyNames(ExecState*, PropertyNameArray&, JSObject*);
+        void getOwnEnumerablePropertyNames(ExecState*, PropertyNameArray&, JSObject*);
 
         bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
         void setHasGetterSetterProperties(bool hasGetterSetterProperties) { m_hasGetterSetterProperties = hasGetterSetterProperties; }
index 1400138..f552c61 100644 (file)
@@ -1,3 +1,16 @@
+2009-09-12  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Maciej Stachowiak.
+
+        [ES5] Implement Object.keys
+        https://bugs.webkit.org/show_bug.cgi?id=29170
+
+        Switch over to getOwnPropertyNames instead of getPropertyNames.
+
+        * UserObjectImp.cpp:
+        (UserObjectImp::getOwnPropertyNames):
+        * UserObjectImp.h:
+
 2009-09-02  Darin Adler  <darin@apple.com>
 
         Reviewed by Geoff Garen.
index 4e64ab1..125e349 100644 (file)
@@ -94,7 +94,7 @@ JSValue UserObjectImp::callAsFunction(ExecState *exec, JSObject *thisObj, const
 }
 
 
-void UserObjectImp::getPropertyNames(ExecState *exec, PropertyNameArray& propertyNames)
+void UserObjectImp::getOwnPropertyNames(ExecState *exec, PropertyNameArray& propertyNames)
 {
     JSUserObject* ptr = GetJSUserObject();
     if (ptr) {
@@ -109,7 +109,7 @@ void UserObjectImp::getPropertyNames(ExecState *exec, PropertyNameArray& propert
             CFRelease(cfPropertyNames);
         }
     }
-    JSObject::getPropertyNames(exec, propertyNames);
+    JSObject::getOwnPropertyNames(exec, propertyNames);
 }
 
 JSValue UserObjectImp::userObjectGetter(ExecState*, const Identifier& propertyName, const PropertySlot& slot)
index 9791658..98c9276 100644 (file)
@@ -44,7 +44,7 @@ public:
 
     virtual CallType getCallData(CallData&);
 
-    virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+    virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
     virtual JSValue callAsFunction(ExecState *exec, JSObject *thisObj, const ArgList &args);
     virtual bool getOwnPropertySlot(ExecState *, const Identifier&, PropertySlot&);
index eea36e7..e198c80 100644 (file)
@@ -1,3 +1,18 @@
+2009-09-12  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Maciej Stachowiak.
+
+        [ES5] Implement Object.keys
+        https://bugs.webkit.org/show_bug.cgi?id=29170
+
+        Add tests for Object.keys(o), both standard usage and cross origin.
+
+        * fast/js/Object-keys-expected.txt: Added.
+        * fast/js/Object-keys.html: Added.
+        * fast/js/resources/Object-keys.js: Added.
+        * http/tests/security/cross-frame-access-enumeration-expected.txt:
+        * http/tests/security/cross-frame-access-enumeration.html:
+
 2009-09-12  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Anders Carlsson.
diff --git a/LayoutTests/fast/js/Object-keys-expected.txt b/LayoutTests/fast/js/Object-keys-expected.txt
new file mode 100644 (file)
index 0000000..32963c6
--- /dev/null
@@ -0,0 +1,20 @@
+Test to ensure correct behaviour of Object.keys
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Object.keys({}) is []
+PASS Object.keys({a:null}) is ['a']
+PASS Object.keys({a:null, b:null}) is ['a', 'b']
+PASS Object.keys({b:null, a:null}) is ['b', 'a']
+PASS Object.keys([]) is []
+PASS Object.keys([null]) is ['0']
+PASS Object.keys([null,null]) is ['0','1']
+PASS Object.keys([null,null,,,,null]) is ['0','1','5']
+PASS Object.keys({__proto__:{a:null}}) is []
+PASS Object.keys({__proto__:[1,2,3]}) is []
+PASS x=[];x.__proto__=[1,2,3];Object.keys(x) is []
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/js/Object-keys.html b/LayoutTests/fast/js/Object-keys.html
new file mode 100644 (file)
index 0000000..7d10e4f
--- /dev/null
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<link rel="stylesheet" href="resources/js-test-style.css">
+<script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script src="resources/Object-keys.js"></script>
+<script src="resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/js/resources/Object-keys.js b/LayoutTests/fast/js/resources/Object-keys.js
new file mode 100644 (file)
index 0000000..a2e8e18
--- /dev/null
@@ -0,0 +1,15 @@
+description("Test to ensure correct behaviour of Object.keys");
+
+shouldBe("Object.keys({})", "[]");
+shouldBe("Object.keys({a:null})", "['a']");
+shouldBe("Object.keys({a:null, b:null})", "['a', 'b']");
+shouldBe("Object.keys({b:null, a:null})", "['b', 'a']");
+shouldBe("Object.keys([])", "[]");
+shouldBe("Object.keys([null])", "['0']");
+shouldBe("Object.keys([null,null])", "['0','1']");
+shouldBe("Object.keys([null,null,,,,null])", "['0','1','5']");
+shouldBe("Object.keys({__proto__:{a:null}})", "[]");
+shouldBe("Object.keys({__proto__:[1,2,3]})", "[]");
+shouldBe("x=[];x.__proto__=[1,2,3];Object.keys(x)", "[]");
+
+successfullyParsed = true;
index cf987ae..25a71de 100644 (file)
@@ -4,10 +4,19 @@ CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http
 
 CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-enumeration-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-enumeration.html. Domains, protocols and ports must match.
 
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-enumeration-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-enumeration.html. Domains, protocols and ports must match.
+
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-enumeration-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-enumeration.html. Domains, protocols and ports must match.
+
+CONSOLE MESSAGE: line 1: Unsafe JavaScript attempt to access frame with URL http://localhost:8000/security/resources/cross-frame-iframe-for-enumeration-test.html from frame with URL http://127.0.0.1:8000/security/cross-frame-access-enumeration.html. Domains, protocols and ports must match.
+
 This tests that variable names can't be enumerated cross domain (see http://bugs.webkit.org/show_bug.cgi?id=16387)
 
 
 PASS: Cross frame access by enumerating the window object was denied.
+PASS: Cross frame access by getting the keys of the window object was denied.
 PASS: Cross frame access by enumerating the History object was denied.
+PASS: Cross frame access by getting the keys of the History object was denied.
 PASS: Cross frame access by enumerating the Location object was denied.
+PASS: Cross frame access by getting the keys of the Location object was denied.
 
index 0401c7b..707fff5 100644 (file)
             }
             log("PASS: Cross frame access by enumerating the window object was denied.");
 
+            var b_winKeys = Object.keys(b_win);
+            if (b_winKeys.indexOf("customWindowProperty") != -1) { 
+                log("FAIL: Cross frame access by getting the keys of the window object was allowed.");
+                return;
+            }
+            log("PASS: Cross frame access by getting the keys of the window object was denied.");
+
             // Test enumerating the History object
             var b_win_history = b_win.history;
             try {
             }
             log("PASS: Cross frame access by enumerating the History object was denied.");
 
+            var b_winHistoryKeys = Object.keys(b_win_history);
+            if (b_winHistoryKeys.indexOf("customHistoryProperty") != -1) { 
+                log("FAIL: Cross frame access by getting the keys of the History object was allowed.");
+                return;
+            }
+            log("PASS: Cross frame access by getting the keys of the History object was denied.");
+
             // Test enumerating the Location object
             var b_win_location = b_win.location;
             try {
             } catch (e) {
             }
             log("PASS: Cross frame access by enumerating the Location object was denied.");
+
+            var b_winLocationKeys = Object.keys(b_win_location);
+            if (b_winLocationKeys.indexOf("customLocationProperty") != -1) { 
+                log("FAIL: Cross frame access by getting the keys of the Location object was allowed.");
+                return;
+            }
+            log("PASS: Cross frame access by getting the keys of the Location object was denied.");
         }
     </script>
 </head>
index 0068155..9ad787d 100644 (file)
@@ -1,3 +1,37 @@
+2009-09-12  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Maciej Stachowiak.
+
+        [ES5] Implement Object.keys
+        https://bugs.webkit.org/show_bug.cgi?id=29170
+
+        Switch from getPropertyNames to getOwnPropertyNames, and provide custom
+        getPropertyNames to DOMWindow, DOMWindowShell and QuarantinedObjectWrapper
+        in order to ensure correct semantics are maintained.
+
+        Test: fast/js/Object-keys.html
+
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::getOwnPropertyNames):
+        * bindings/js/JSDOMWindowShell.cpp:
+        (WebCore::JSDOMWindowShell::getOwnPropertyNames):
+        * bindings/js/JSDOMWindowShell.h:
+        * bindings/js/JSHistoryCustom.cpp:
+        (WebCore::JSHistory::getOwnPropertyNames):
+        * bindings/js/JSLocationCustom.cpp:
+        (WebCore::JSLocation::getOwnPropertyNames):
+        * bindings/js/JSQuarantinedObjectWrapper.cpp:
+        (WebCore::JSQuarantinedObjectWrapper::getPropertyNames):
+        (WebCore::JSQuarantinedObjectWrapper::getOwnPropertyNames):
+        * bindings/js/JSQuarantinedObjectWrapper.h:
+        * bindings/js/JSStorageCustom.cpp:
+        (WebCore::JSStorage::getOwnPropertyNames):
+        * bindings/scripts/CodeGeneratorJS.pm:
+        * bridge/runtime_object.cpp:
+        (JSC::RuntimeObjectImp::getPropertyNames):
+        (JSC::RuntimeObjectImp::getOwnPropertyNames):
+        * bridge/runtime_object.h:
+
 2009-09-12  Sam Weinig  <sam@webkit.org>
 
         Reviewed by Anders Carlsson.
index 677e62c..f25f767 100644 (file)
@@ -472,6 +472,14 @@ void JSDOMWindow::getPropertyNames(ExecState* exec, PropertyNameArray& propertyN
     Base::getPropertyNames(exec, propertyNames);
 }
 
+void JSDOMWindow::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+{
+    // Only allow the window to enumerated by frames in the same origin.
+    if (!allowsAccessFrom(exec))
+        return;
+    Base::getOwnPropertyNames(exec, propertyNames);
+}
+
 bool JSDOMWindow::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
 {
     // Only allow getting property attributes properties by frames in the same origin.
index 4b2a7dc..f969dc1 100644 (file)
@@ -113,6 +113,11 @@ void JSDOMWindowShell::getPropertyNames(ExecState* exec, PropertyNameArray& prop
     m_window->getPropertyNames(exec, propertyNames);
 }
 
+void JSDOMWindowShell::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+{
+    m_window->getOwnPropertyNames(exec, propertyNames);
+}
+
 bool JSDOMWindowShell::getPropertyAttributes(JSC::ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
 {
     return m_window->getPropertyAttributes(exec, propertyName, attributes);
index 716fc14..a7c2c56 100644 (file)
@@ -72,6 +72,7 @@ namespace WebCore {
         virtual void putWithAttributes(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue, unsigned attributes);
         virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier& propertyName);
         virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
+        virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
         virtual bool getPropertyAttributes(JSC::ExecState*, const JSC::Identifier& propertyName, unsigned& attributes) const;
         virtual void defineGetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction);
         virtual void defineSetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction);
index 50ff5c3..b24b1ff 100644 (file)
@@ -155,12 +155,12 @@ bool JSHistory::deleteProperty(ExecState* exec, const Identifier& propertyName)
     return Base::deleteProperty(exec, propertyName);
 }
 
-void JSHistory::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSHistory::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     // Only allow the history object to enumerated by frames in the same origin.
     if (!allowsAccessFromFrame(exec, impl()->frame()))
         return;
-    Base::getPropertyNames(exec, propertyNames);
+    Base::getOwnPropertyNames(exec, propertyNames);
 }
 
 } // namespace WebCore
index 76c9ea7..ef58349 100644 (file)
@@ -174,12 +174,12 @@ bool JSLocation::deleteProperty(ExecState* exec, const Identifier& propertyName)
     return Base::deleteProperty(exec, propertyName);
 }
 
-void JSLocation::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSLocation::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     // Only allow the location object to enumerated by frames in the same origin.
     if (!allowsAccessFromFrame(exec, impl()->frame()))
         return;
-    Base::getPropertyNames(exec, propertyNames);
+    Base::getOwnPropertyNames(exec, propertyNames);
 }
 
 void JSLocation::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
index d4ad383..a071a4e 100644 (file)
@@ -290,8 +290,16 @@ void JSQuarantinedObjectWrapper::getPropertyNames(ExecState*, PropertyNameArray&
 {
     if (!allowsGetPropertyNames())
         return;
-
+    
     m_unwrappedObject->getPropertyNames(unwrappedExecState(), array);
 }
 
+void JSQuarantinedObjectWrapper::getOwnPropertyNames(ExecState*, PropertyNameArray& array)
+{
+    if (!allowsGetPropertyNames())
+        return;
+
+    m_unwrappedObject->getOwnPropertyNames(unwrappedExecState(), array);
+}
+
 } // namespace WebCore
index ef4b657..37f2518 100644 (file)
@@ -70,8 +70,9 @@ namespace WebCore {
         virtual JSC::ConstructType getConstructData(JSC::ConstructData&);
 
         virtual bool hasInstance(JSC::ExecState*, JSC::JSValue, JSC::JSValue proto);
-
+        
         virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
+        virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);
 
         virtual JSC::UString className() const { return m_unwrappedObject->className(); }
 
index 07cf2f8..e416d35 100644 (file)
@@ -64,13 +64,13 @@ bool JSStorage::deleteProperty(ExecState* exec, const Identifier& propertyName)
     return true;
 }
 
-void JSStorage::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+void JSStorage::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
 {
     unsigned length = m_impl->length();
     for (unsigned i = 0; i < length; ++i)
         propertyNames.add(Identifier(exec, m_impl->key(i)));
         
-    Base::getPropertyNames(exec, propertyNames);
+    Base::getOwnPropertyNames(exec, propertyNames);
 }
 
 bool JSStorage::putDelegate(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot&)
index ba2325f..88f67f0 100644 (file)
@@ -625,8 +625,10 @@ sub GenerateHeader
     # Custom deleteProperty function
     push(@headerContent, "    virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier&);\n") if $dataNode->extendedAttributes->{"CustomDeleteProperty"};
 
-    # Custom getPropertyNames function
-    push(@headerContent, "    virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);\n") if ($dataNode->extendedAttributes->{"CustomGetPropertyNames"} || $dataNode->extendedAttributes->{"HasIndexGetter"} || $dataNode->extendedAttributes->{"HasCustomIndexGetter"} || $dataNode->extendedAttributes->{"HasNumericIndexGetter"});
+    # Custom getPropertyNames function exists on DOMWindow
+    push(@headerContent, "    virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);\n") if $interfaceName eq "DOMWindow";
+    # Custom getOwnPropertyNames function
+    push(@headerContent, "    virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&);\n") if ($dataNode->extendedAttributes->{"CustomGetPropertyNames"} || $dataNode->extendedAttributes->{"HasIndexGetter"} || $dataNode->extendedAttributes->{"HasCustomIndexGetter"} || $dataNode->extendedAttributes->{"HasNumericIndexGetter"});    
 
     # Custom getPropertyAttributes function
     push(@headerContent, "    virtual bool getPropertyAttributes(JSC::ExecState*, const JSC::Identifier&, unsigned& attributes) const;\n") if $dataNode->extendedAttributes->{"CustomGetPropertyAttributes"};
@@ -1486,13 +1488,13 @@ sub GenerateImplementation
     }
 
     if (($dataNode->extendedAttributes->{"HasIndexGetter"} || $dataNode->extendedAttributes->{"HasCustomIndexGetter"} || $dataNode->extendedAttributes->{"HasNumericIndexGetter"}) && !$dataNode->extendedAttributes->{"CustomGetPropertyNames"}) {
-        push(@implContent, "void ${className}::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)\n");
+        push(@implContent, "void ${className}::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)\n");
         push(@implContent, "{\n");
         if ($dataNode->extendedAttributes->{"HasIndexGetter"} || $dataNode->extendedAttributes->{"HasCustomIndexGetter"} || $dataNode->extendedAttributes->{"HasNumericIndexGetter"}) {
             push(@implContent, "    for (unsigned i = 0; i < static_cast<${implClassName}*>(impl())->length(); ++i)\n");
             push(@implContent, "        propertyNames.add(Identifier::from(exec, i));\n");
         }
-        push(@implContent, "     Base::getPropertyNames(exec, propertyNames);\n");
+        push(@implContent, "     Base::getOwnPropertyNames(exec, propertyNames);\n");
         push(@implContent, "}\n\n");
     }
 
index 91be1a0..0282411 100644 (file)
@@ -295,12 +295,17 @@ void RuntimeObjectImp::getPropertyNames(ExecState* exec, PropertyNameArray& prop
         throwInvalidAccessError(exec);
         return;
     }
-    
+
     instance->begin();
     instance->getPropertyNames(exec, propertyNames);
     instance->end();
 }
 
+void RuntimeObjectImp::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
+{
+    getOwnPropertyNames(exec, propertyNames);
+}
+
 JSObject* RuntimeObjectImp::throwInvalidAccessError(ExecState* exec)
 {
     return throwError(exec, ReferenceError, "Trying to access object from destroyed plug-in.");
index 4a9c396..5e8f57e 100644 (file)
@@ -44,8 +44,9 @@ public:
     virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const;
     virtual CallType getCallData(CallData&);
     virtual ConstructType getConstructData(ConstructData&);
-    
+
     virtual void getPropertyNames(ExecState*, PropertyNameArray&);
+    virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&);
 
     virtual void invalidate();
     Bindings::Instance* getInternalInstance() const { return instance.get(); }