From 677be73e767f93741204f07061f0216485086f99 Mon Sep 17 00:00:00 2001 From: caitpotter88 Date: Thu, 21 Jan 2016 19:12:31 -0800 Subject: [PATCH] [es7] implement Object.values() / Object.entries() proposal BUG=v8:4663 LOG=N TBR=hpayer@chromium.org R=ljharb@gmail.com, rossberg@chromium.org, adamk@chromium.org Review URL: https://codereview.chromium.org/1581033002 Cr-Commit-Position: refs/heads/master@{#33450} --- src/bootstrapper.cc | 17 ++++++ src/builtins.cc | 56 ++++++++++++++++++++ src/builtins.h | 2 + src/flag-definitions.h | 3 +- src/heap/heap.h | 2 + test/mjsunit/harmony/object-entries.js | 73 ++++++++++++++++++++++++++ test/mjsunit/harmony/object-values.js | 64 ++++++++++++++++++++++ 7 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 test/mjsunit/harmony/object-entries.js create mode 100644 test/mjsunit/harmony/object-values.js diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 315b47fb7c..1c4574e5e0 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2451,6 +2451,22 @@ void Genesis::InitializeGlobal_harmony_simd() { } +void Genesis::InitializeGlobal_harmony_object_values_entries() { + if (!FLAG_harmony_object_values_entries) return; + + Handle global( + JSGlobalObject::cast(native_context()->global_object())); + Isolate* isolate = global->GetIsolate(); + Factory* factory = isolate->factory(); + + Handle object_function = isolate->object_function(); + SimpleInstallFunction(object_function, factory->entries_string(), + Builtins::kObjectEntries, 1, false); + SimpleInstallFunction(object_function, factory->values_string(), + Builtins::kObjectValues, 1, false); +} + + void Genesis::InstallJSProxyMaps() { // Allocate the different maps for all Proxy types. // Next to the default proxy, we need maps indicating callable and @@ -2940,6 +2956,7 @@ bool Genesis::InstallExperimentalNatives() { static const char* harmony_function_name_natives[] = {nullptr}; static const char* promise_extra_natives[] = {"native promise-extra.js", nullptr}; + static const char* harmony_object_values_entries_natives[] = {nullptr}; for (int i = ExperimentalNatives::GetDebuggerCount(); i < ExperimentalNatives::GetBuiltinsCount(); i++) { diff --git a/src/builtins.cc b/src/builtins.cc index 35671d2c4c..b7f30433d5 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1653,6 +1653,62 @@ BUILTIN(ObjectKeys) { } +BUILTIN(ObjectValues) { + HandleScope scope(isolate); + Handle object = args.atOrUndefined(isolate, 1); + Handle receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver, + Object::ToObject(isolate, object)); + Handle keys; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, keys, + JSReceiver::GetKeys(receiver, JSReceiver::OWN_ONLY, ENUMERABLE_STRINGS, + CONVERT_TO_STRING)); + + for (int i = 0; i < keys->length(); ++i) { + auto key = Handle::cast(FixedArray::get(keys, i)); + Handle value; + + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, value, Object::GetPropertyOrElement(receiver, key, STRICT)); + + keys->set(i, *value); + } + + return *isolate->factory()->NewJSArrayWithElements(keys); +} + + +BUILTIN(ObjectEntries) { + HandleScope scope(isolate); + Handle object = args.atOrUndefined(isolate, 1); + Handle receiver; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver, + Object::ToObject(isolate, object)); + Handle keys; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, keys, + JSReceiver::GetKeys(receiver, JSReceiver::OWN_ONLY, ENUMERABLE_STRINGS, + CONVERT_TO_STRING)); + + for (int i = 0; i < keys->length(); ++i) { + auto key = Handle::cast(FixedArray::get(keys, i)); + Handle value; + + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, value, Object::GetPropertyOrElement(receiver, key, STRICT)); + + auto entry_storage = isolate->factory()->NewUninitializedFixedArray(2); + entry_storage->set(0, *key); + entry_storage->set(1, *value); + auto entry = isolate->factory()->NewJSArrayWithElements(entry_storage); + keys->set(i, *entry); + } + + return *isolate->factory()->NewJSArrayWithElements(keys); +} + + // ES6 section 19.1.2.15 Object.preventExtensions ( O ) BUILTIN(ObjectPreventExtensions) { HandleScope scope(isolate); diff --git a/src/builtins.h b/src/builtins.h index b9629d28d1..8eb89a9a8e 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -116,6 +116,8 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) { V(ObjectIsFrozen, kNone) \ V(ObjectIsSealed, kNone) \ V(ObjectKeys, kNone) \ + V(ObjectValues, kNone) \ + V(ObjectEntries, kNone) \ V(ObjectPreventExtensions, kNone) \ V(ObjectSeal, kNone) \ V(ObjectProtoToString, kNone) \ diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 407301b38f..bd7c1f4581 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -206,7 +206,8 @@ DEFINE_IMPLICATION(es_staging, move_object_start) V(harmony_simd, "harmony simd") \ V(harmony_do_expressions, "harmony do-expressions") \ V(harmony_regexp_subclass, "harmony regexp subclassing") \ - V(harmony_species, "harmony Symbol.species") + V(harmony_species, "harmony Symbol.species") \ + V(harmony_object_values_entries, "harmony Object.values / Object.entries") // Features that are complete (but still behind --harmony/es-staging flag). #define HARMONY_STAGED(V) \ diff --git a/src/heap/heap.h b/src/heap/heap.h index af9d0a6235..4c2aace8e2 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -247,6 +247,7 @@ namespace internal { V(done_string, "done") \ V(dot_result_string, ".result") \ V(dot_string, ".") \ + V(entries_string, "entries") \ V(enumerable_string, "enumerable") \ V(enumerate_string, "enumerate") \ V(Error_string, "Error") \ @@ -328,6 +329,7 @@ namespace internal { V(undefined_string, "undefined") \ V(undefined_to_string, "[object Undefined]") \ V(valueOf_string, "valueOf") \ + V(values_string, "values") \ V(value_string, "value") \ V(WeakMap_string, "WeakMap") \ V(WeakSet_string, "WeakSet") \ diff --git a/test/mjsunit/harmony/object-entries.js b/test/mjsunit/harmony/object-entries.js new file mode 100644 index 0000000000..47fedd1980 --- /dev/null +++ b/test/mjsunit/harmony/object-entries.js @@ -0,0 +1,73 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-object-values-entries --harmony-proxies --harmony-reflect + +function TestMeta() { + assertEquals(1, Object.entries.length); + assertEquals(Function.prototype, Object.getPrototypeOf(Object.entries)); +} +TestMeta(); + + +function TestBasic() { + var x = 16; + var O = { + d: 1, + c: 3, + [Symbol.iterator]: void 0, + 0: 123, + 1000: 456, + [x * x]: "ducks", + [`0x${(x * x).toString(16)}`]: "quack" + }; + O.a = 2; + O.b = 4; + Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); + assertEquals([ + ["0", 123], + ["256", "ducks"], + ["1000", 456], + ["d", 1], + ["c", 3], + ["0x100", "quack"], + ["a", 2], + ["b", 4] + ], Object.entries(O)); + assertEquals(Object.entries(O), Object.keys(O).map(key => [key, O[key]])); +} +TestBasic(); + + +function TestOrder() { + var O = { + a: 1, + [Symbol.iterator]: null + }; + O[456] = 123; + Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); + + var log = []; + var P = new Proxy(O, { + ownKeys(target) { + log.push("[[OwnPropertyKeys]]"); + return Reflect.ownKeys(target); + }, + get(target, name) { + log.push(`[[Get]](${JSON.stringify(name)})`); + return Reflect.get(target, name); + }, + set(target, name, value) { + assertUnreachable(); + } + }); + + assertEquals([["456", 123], ["a", 1]], Object.entries(P)); + assertEquals([ + "[[OwnPropertyKeys]]", + "[[Get]](\"456\")", + "[[Get]](\"a\")" + ], log); +} +TestOrder(); diff --git a/test/mjsunit/harmony/object-values.js b/test/mjsunit/harmony/object-values.js new file mode 100644 index 0000000000..e312889f85 --- /dev/null +++ b/test/mjsunit/harmony/object-values.js @@ -0,0 +1,64 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-object-values-entries --harmony-proxies --harmony-reflect + +function TestMeta() { + assertEquals(1, Object.values.length); + assertEquals(Function.prototype, Object.getPrototypeOf(Object.values)); +} +TestMeta(); + + +function TestBasic() { + var x = 16; + var O = { + d: 1, + c: 3, + [Symbol.iterator]: void 0, + 0: 123, + 1000: 456, + [x * x]: "ducks", + [`0x${(x * x).toString(16)}`]: "quack" + }; + O.a = 2; + O.b = 4; + Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); + assertEquals([123, "ducks", 456, 1, 3, "quack", 2, 4], Object.values(O)); + assertEquals(Object.values(O), Object.keys(O).map(key => O[key])); +} +TestBasic(); + + +function TestOrder() { + var O = { + a: 1, + [Symbol.iterator]: null + }; + O[456] = 123; + Object.defineProperty(O, "HIDDEN", { enumerable: false, value: NaN }); + + var log = []; + var P = new Proxy(O, { + ownKeys(target) { + log.push("[[OwnPropertyKeys]]"); + return Reflect.ownKeys(target); + }, + get(target, name) { + log.push(`[[Get]](${JSON.stringify(name)})`); + return Reflect.get(target, name); + }, + set(target, name, value) { + assertUnreachable(); + } + }); + + assertEquals([123, 1], Object.values(P)); + assertEquals([ + "[[OwnPropertyKeys]]", + "[[Get]](\"456\")", + "[[Get]](\"a\")" + ], log); +} +TestOrder();