From 77ff957f582ec92f27187de961c010c525a88960 Mon Sep 17 00:00:00 2001 From: "christian.plesner.hansen@gmail.com" Date: Tue, 15 Sep 2009 11:51:40 +0000 Subject: [PATCH] Implemented Object.keys. Review URL: http://codereview.chromium.org/201114 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2890 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- LICENSE | 13 +++-- src/api.cc | 3 +- src/handles.cc | 11 +++- src/handles.h | 6 +- src/messages.js | 3 +- src/runtime.cc | 25 +++++++-- src/runtime.h | 2 + src/v8natives.js | 10 ++++ test/mjsunit/testcfg.py | 3 +- .../{ => third_party}/array-splice-webkit.js | 48 ++++++++-------- test/mjsunit/third_party/object-keys.js | 65 ++++++++++++++++++++++ test/mjsunit/{ => third_party}/regexp-pcre.js | 0 12 files changed, 152 insertions(+), 37 deletions(-) rename test/mjsunit/{ => third_party}/array-splice-webkit.js (52%) create mode 100644 test/mjsunit/third_party/object-keys.js rename test/mjsunit/{ => third_party}/regexp-pcre.js (100%) diff --git a/LICENSE b/LICENSE index 553cf47..d2862b4 100644 --- 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. diff --git a/src/api.cc b/src/api.cc index 052e875..1b59bc7 100644 --- a/src/api.cc +++ b/src/api.cc @@ -1988,7 +1988,8 @@ Local v8::Object::GetPropertyNames() { ENTER_V8; v8::HandleScope scope; i::Handle self = Utils::OpenHandle(this); - i::Handle value = i::GetKeysInFixedArrayFor(self); + i::Handle 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. diff --git a/src/handles.cc b/src/handles.cc index fae006a..7a473ae 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -527,7 +527,8 @@ v8::Handle GetKeysForIndexedInterceptor(Handle receiver, } -Handle GetKeysInFixedArrayFor(Handle object) { +Handle GetKeysInFixedArrayFor(Handle object, + KeyCollectionType type) { Handle content = Factory::empty_fixed_array(); JSObject* arguments_boilerplate = @@ -575,6 +576,11 @@ Handle GetKeysInFixedArrayFor(Handle 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 GetKeysInFixedArrayFor(Handle object) { Handle GetKeysFor(Handle object) { Counters::for_in.Increment(); - Handle elements = GetKeysInFixedArrayFor(object); + Handle elements = GetKeysInFixedArrayFor(object, + INCLUDE_PROTOS); return Factory::NewJSArrayWithElements(elements); } diff --git a/src/handles.h b/src/handles.h index 847aebb..5d57465 100644 --- a/src/handles.h +++ b/src/handles.h @@ -265,9 +265,13 @@ v8::Handle GetKeysForNamedInterceptor(Handle receiver, Handle object); v8::Handle GetKeysForIndexedInterceptor(Handle receiver, Handle object); + +enum KeyCollectionType { LOCAL_ONLY, INCLUDE_PROTOS }; + // Computes the enumerable keys for a JSObject. Used for implementing // "for (n in object) { }". -Handle GetKeysInFixedArrayFor(Handle object); +Handle GetKeysInFixedArrayFor(Handle object, + KeyCollectionType type); Handle GetKeysFor(Handle object); Handle GetEnumPropertyKeys(Handle object); diff --git a/src/messages.js b/src/messages.js index 255e544..6513067 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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]; diff --git a/src/runtime.cc b/src/runtime.cc index 6272827..09ca290 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -2992,7 +2992,8 @@ static Object* Runtime_GetPropertyNamesFast(Arguments args) { HandleScope scope; Handle object(raw_object); - Handle content = GetKeysInFixedArrayFor(object); + Handle 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 object(raw_object); + Handle 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 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 keys = GetKeysInFixedArrayFor(array); + Handle 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 MaterializeLocalScope(JavaScriptFrame* frame) { if (function_context->has_extension() && !function_context->IsGlobalContext()) { Handle ext(JSObject::cast(function_context->extension())); - Handle keys = GetKeysInFixedArrayFor(ext); + Handle 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 MaterializeClosure(Handle context) { // be variables introduced by eval. if (context->has_extension()) { Handle ext(JSObject::cast(context->extension())); - Handle keys = GetKeysInFixedArrayFor(ext); + Handle 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()); diff --git a/src/runtime.h b/src/runtime.h index ca38baf..27fa741 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -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) diff --git a/src/v8natives.js b/src/v8natives.js index be92347..b63ce5f 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -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(); diff --git a/test/mjsunit/testcfg.py b/test/mjsunit/testcfg.py index 97924c8..e3f3fcd 100644 --- a/test/mjsunit/testcfg.py +++ b/test/mjsunit/testcfg.py @@ -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): diff --git a/test/mjsunit/array-splice-webkit.js b/test/mjsunit/third_party/array-splice-webkit.js 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 --- a/test/mjsunit/array-splice-webkit.js +++ b/test/mjsunit/third_party/array-splice-webkit.js @@ -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 index 0000000..883937d --- /dev/null +++ b/test/mjsunit/third_party/object-keys.js @@ -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); diff --git a/test/mjsunit/regexp-pcre.js b/test/mjsunit/third_party/regexp-pcre.js similarity index 100% rename from test/mjsunit/regexp-pcre.js rename to test/mjsunit/third_party/regexp-pcre.js -- 2.7.4