Implemented Object.keys.
authorchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 Sep 2009 11:51:40 +0000 (11:51 +0000)
committerchristian.plesner.hansen@gmail.com <christian.plesner.hansen@gmail.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 15 Sep 2009 11:51:40 +0000 (11:51 +0000)
Review URL: http://codereview.chromium.org/201114

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2890 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

12 files changed:
LICENSE
src/api.cc
src/handles.cc
src/handles.h
src/messages.js
src/runtime.cc
src/runtime.h
src/v8natives.js
test/mjsunit/testcfg.py
test/mjsunit/third_party/array-splice-webkit.js [moved from test/mjsunit/array-splice-webkit.js with 52% similarity]
test/mjsunit/third_party/object-keys.js [new file with mode: 0644]
test/mjsunit/third_party/regexp-pcre.js [moved from test/mjsunit/regexp-pcre.js with 100% similarity]

diff --git a/LICENSE b/LICENSE
index 553cf47..d2862b4 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -2,10 +2,15 @@ This license applies to all parts of V8 that are not externally
 maintained libraries.  The externally maintained libraries used by V8
 are:
 
-  - PCRE test suite, located in test/mjsunit/regexp-pcre.js.  This is
-    based on the test suite from PCRE-7.3, which is copyrighted by the
-    University of Cambridge and Google, Inc.  The copyright notice and
-    license are embedded in regexp-pcre.js.
+  - PCRE test suite, located in
+    test/mjsunit/third_party/regexp-pcre.js.  This is based on the
+    test suite from PCRE-7.3, which is copyrighted by the University
+    of Cambridge and Google, Inc.  The copyright notice and license
+    are embedded in regexp-pcre.js.
+
+  - Layout tests, located in test/mjsunit/third_party.  These are
+    based on layout tests from webkit.org which are copyrighted by
+    Apple Computer, Inc. and released under a 3-clause BSD license.
 
   - Dtoa, located under third_party/dtoa.  This code is copyrighted by
     David M. Gay and released under an MIT license.
index 052e875..1b59bc7 100644 (file)
@@ -1988,7 +1988,8 @@ Local<Array> v8::Object::GetPropertyNames() {
   ENTER_V8;
   v8::HandleScope scope;
   i::Handle<i::JSObject> self = Utils::OpenHandle(this);
-  i::Handle<i::FixedArray> value = i::GetKeysInFixedArrayFor(self);
+  i::Handle<i::FixedArray> value =
+      i::GetKeysInFixedArrayFor(self, i::INCLUDE_PROTOS);
   // Because we use caching to speed up enumeration it is important
   // to never change the result of the basic enumeration function so
   // we clone the result.
index fae006a..7a473ae 100644 (file)
@@ -527,7 +527,8 @@ v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
 }
 
 
-Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object) {
+Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
+                                          KeyCollectionType type) {
   Handle<FixedArray> content = Factory::empty_fixed_array();
 
   JSObject* arguments_boilerplate =
@@ -575,6 +576,11 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object) {
         if (!result.IsEmpty())
           content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
       }
+
+      // If we only want local properties we bail out after the first
+      // iteration.
+      if (type == LOCAL_ONLY)
+        break;
     }
   }
   return content;
@@ -583,7 +589,8 @@ Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object) {
 
 Handle<JSArray> GetKeysFor(Handle<JSObject> object) {
   Counters::for_in.Increment();
-  Handle<FixedArray> elements = GetKeysInFixedArrayFor(object);
+  Handle<FixedArray> elements = GetKeysInFixedArrayFor(object,
+                                                       INCLUDE_PROTOS);
   return Factory::NewJSArrayWithElements(elements);
 }
 
index 847aebb..5d57465 100644 (file)
@@ -265,9 +265,13 @@ v8::Handle<v8::Array> GetKeysForNamedInterceptor(Handle<JSObject> receiver,
                                                  Handle<JSObject> object);
 v8::Handle<v8::Array> GetKeysForIndexedInterceptor(Handle<JSObject> receiver,
                                                    Handle<JSObject> object);
+
+enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS };
+
 // Computes the enumerable keys for a JSObject. Used for implementing
 // "for (n in object) { }".
-Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object);
+Handle<FixedArray> GetKeysInFixedArrayFor(Handle<JSObject> object,
+                                          KeyCollectionType type);
 Handle<JSArray> GetKeysFor(Handle<JSObject> object);
 Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object);
 
index 255e544..6513067 100644 (file)
@@ -167,7 +167,8 @@ function FormatMessage(message) {
       no_input_to_regexp:           "No input to %0",
       result_not_primitive:         "Result of %0 must be a primitive, was %1",
       invalid_json:                 "String '%0' is not valid JSON",
-      circular_structure:           "Converting circular structure to JSON"
+      circular_structure:           "Converting circular structure to JSON",
+      object_keys_non_object:       "Object.keys called on non-object"
     };
   }
   var format = kMessages[message.type];
index 6272827..09ca290 100644 (file)
@@ -2992,7 +2992,8 @@ static Object* Runtime_GetPropertyNamesFast(Arguments args) {
 
   HandleScope scope;
   Handle<JSObject> object(raw_object);
-  Handle<FixedArray> content = GetKeysInFixedArrayFor(object);
+  Handle<FixedArray> content = GetKeysInFixedArrayFor(object,
+                                                      INCLUDE_PROTOS);
 
   // Test again, since cache may have been built by preceding call.
   if (object->IsSimpleEnum()) return object->map();
@@ -3001,6 +3002,22 @@ static Object* Runtime_GetPropertyNamesFast(Arguments args) {
 }
 
 
+static Object* Runtime_LocalKeys(Arguments args) {
+  ASSERT_EQ(args.length(), 1);
+  CONVERT_CHECKED(JSObject, raw_object, args[0]);
+  HandleScope scope;
+  Handle<JSObject> object(raw_object);
+  Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
+                                                       LOCAL_ONLY);
+  // Some fast paths through GetKeysInFixedArrayFor reuse a cached
+  // property array and since the result is mutable we have to create
+  // a fresh clone on each invocation.
+  Handle<FixedArray> copy = Factory::NewFixedArray(contents->length());
+  contents->CopyTo(0, *copy, 0, contents->length());
+  return *Factory::NewJSArrayWithElements(copy);
+}
+
+
 static Object* Runtime_GetArgumentsProperty(Arguments args) {
   NoHandleAllocation ha;
   ASSERT(args.length() == 1);
@@ -5516,7 +5533,7 @@ static Object* Runtime_GetArrayKeys(Arguments args) {
   if (array->elements()->IsDictionary()) {
     // Create an array and get all the keys into it, then remove all the
     // keys that are not integers in the range 0 to length-1.
-    Handle<FixedArray> keys = GetKeysInFixedArrayFor(array);
+    Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS);
     int keys_length = keys->length();
     for (int i = 0; i < keys_length; i++) {
       Object* key = keys->get(i);
@@ -6271,7 +6288,7 @@ static Handle<JSObject> MaterializeLocalScope(JavaScriptFrame* frame) {
     if (function_context->has_extension() &&
         !function_context->IsGlobalContext()) {
       Handle<JSObject> ext(JSObject::cast(function_context->extension()));
-      Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
+      Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
       for (int i = 0; i < keys->length(); i++) {
         // Names of variables introduced by eval are strings.
         ASSERT(keys->get(i)->IsString());
@@ -6320,7 +6337,7 @@ static Handle<JSObject> MaterializeClosure(Handle<Context> context) {
   // be variables introduced by eval.
   if (context->has_extension()) {
     Handle<JSObject> ext(JSObject::cast(context->extension()));
-    Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
+    Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS);
     for (int i = 0; i < keys->length(); i++) {
       // Names of variables introduced by eval are strings.
       ASSERT(keys->get(i)->IsString());
index ca38baf..27fa741 100644 (file)
@@ -258,6 +258,8 @@ namespace internal {
   F(Abort, 2, 1) \
   /* Logging */ \
   F(Log, 2, 1) \
+  /* ES5 */ \
+  F(LocalKeys, 1, 1) \
   \
   /* Pseudo functions - handled as macros by parser */ \
   F(IS_VAR, 1, 1)
index be92347..b63ce5f 100644 (file)
@@ -276,6 +276,13 @@ function ObjectLookupSetter(name) {
 }
 
 
+function ObjectKeys(obj) {
+  if (!IS_OBJECT(obj) || IS_NULL_OR_UNDEFINED(obj))
+    throw MakeTypeError('object_keys_non_object', [obj]);
+  return %LocalKeys(obj);
+}
+
+
 %SetCode($Object, function(x) {
   if (%_IsConstructCall()) {
     if (x == null) return this;
@@ -304,6 +311,9 @@ function SetupObject() {
     "__defineSetter__", ObjectDefineSetter,
     "__lookupSetter__", ObjectLookupSetter
   ));
+  InstallFunctions($Object, DONT_ENUM, $Array(
+    "keys", ObjectKeys
+  ));
 }
 
 SetupObject();
index 97924c8..e3f3fcd 100644 (file)
@@ -112,8 +112,9 @@ class MjsunitTestConfiguration(test.TestConfiguration):
     mjsunit = [current_path + [t] for t in self.Ls(self.root)]
     regress = [current_path + ['regress', t] for t in self.Ls(join(self.root, 'regress'))]
     bugs = [current_path + ['bugs', t] for t in self.Ls(join(self.root, 'bugs'))]
+    third_party = [current_path + ['third_party', t] for t in self.Ls(join(self.root, 'third_party'))]
     tools = [current_path + ['tools', t] for t in self.Ls(join(self.root, 'tools'))]
-    all_tests = mjsunit + regress + bugs + tools
+    all_tests = mjsunit + regress + bugs + third_party + tools
     result = []
     for test in all_tests:
       if self.Contains(path, test):
similarity index 52%
rename from test/mjsunit/array-splice-webkit.js
rename to test/mjsunit/third_party/array-splice-webkit.js
index 113a56a..b676a7c 100644 (file)
@@ -1,29 +1,33 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+//
 // Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
 //
-//     * Redistributions of source code must retain the above copyright
-//       notice, this list of conditions and the following disclaimer.
-//     * Redistributions in binary form must reproduce the above
-//       copyright notice, this list of conditions and the following
-//       disclaimer in the documentation and/or other materials provided
-//       with the distribution.
-//     * Neither the name of Google Inc. nor the names of its
-//       contributors may be used to endorse or promote products derived
-//       from this software without specific prior written permission.
+// 3. Neither the name of the copyright holder(s) nor the names of any
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
 //
 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Simple splice tests based on webkit layout tests.
 var arr = ['a','b','c','d'];
@@ -56,5 +60,3 @@ assertArrayEquals([], arr.splice(2, 0))
 assertArrayEquals(['a','b','c'], arr);
 assertArrayEquals(['c'], arr.splice(2, 100))
 assertArrayEquals(['a','b'], arr);
-
-
diff --git a/test/mjsunit/third_party/object-keys.js b/test/mjsunit/third_party/object-keys.js
new file mode 100644 (file)
index 0000000..883937d
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// 3. Neither the name of the copyright holder(s) nor the names of any
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Based on LayoutTests/fast/js/Object-keys.html
+
+assertThrows(function () { Object.keys(2) }, TypeError);
+assertThrows(function () { Object.keys("foo") }, TypeError);
+assertThrows(function () { Object.keys(null) }, TypeError);
+assertThrows(function () { Object.keys(undefined) }, TypeError);
+
+assertEquals(Object.keys({}), []);
+assertEquals(Object.keys({a:null}), ['a']);
+assertEquals(Object.keys({a:null, b:null}), ['a', 'b']);
+assertEquals(Object.keys({b:null, a:null}), ['b', 'a']);
+assertEquals(Object.keys([]), []);
+assertEquals(Object.keys([null]), ['0']);
+assertEquals(Object.keys([null,null]), ['0', '1']);
+assertEquals(Object.keys([null,null,,,,null]), ['0', '1', '5']);
+assertEquals(Object.keys({__proto__:{a:null}}), []);
+assertEquals(Object.keys({__proto__:[1,2,3]}), []);
+var x = [];
+x.__proto__ = [1, 2, 3];
+assertEquals(Object.keys(x), []);
+
+function argsTest(a, b, c) {
+  assertEquals([], Object.keys(arguments));
+}
+
+argsTest(1, 2, 3);
+
+var literal = {a: 1, b: 2, c: 3};
+var keysBefore = Object.keys(literal);
+assertEquals(['a', 'b', 'c'], keysBefore);
+keysBefore[0] = 'x';
+var keysAfter = Object.keys(literal);
+assertEquals(['a', 'b', 'c'], keysAfter);
+assertEquals(['x', 'b', 'c'], keysBefore);