From af74f27ed90c65aeadfbeb51343279c382ae44dc Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Sat, 26 Aug 2017 16:52:15 -0700 Subject: [PATCH] #428: optimized Object.values and Object.entries from M1232369 --- js/src/builtin/Object.cpp | 128 +++++++++++++++++++++++++-- js/src/builtin/Object.js | 46 ---------- js/src/jsarray.cpp | 14 +++ js/src/jsarray.h | 3 + js/src/jsiter.cpp | 10 +-- js/src/tests/ecma_7/Object/values.js | 2 - 6 files changed, 141 insertions(+), 62 deletions(-) diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index cdbd9f76b..11a0832dc 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -680,7 +680,109 @@ js::obj_getOwnPropertyDescriptor(JSContext* cx, unsigned argc, Value* vp) FromPropertyDescriptor(cx, desc, args.rval()); } -// ES6 draft rev27 (2014/08/24) 19.1.2.14 Object.keys(O) +enum EnumerableOwnPropertiesKind { + Keys, + Values, + KeysAndValues +}; + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#EnumerableOwnProperties +static bool +EnumerableOwnProperties(JSContext* cx, const JS::CallArgs& args, EnumerableOwnPropertiesKind kind) +{ + // Step 1. (Step 1 of Object.{keys,values,entries}, really.) + RootedObject obj(cx, ToObject(cx, args.get(0))); + if (!obj) + return false; + + // Step 2. + AutoIdVector ids(cx); + if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &ids)) + return false; + + // Step 3. + AutoValueVector properties(cx); + size_t len = ids.length(); + if (!properties.resize(len)) + return false; + + RootedId id(cx); + RootedValue key(cx); + RootedValue value(cx); + RootedNativeObject nobj(cx); + if (obj->is()) + nobj = &obj->as(); + RootedShape shape(cx); + Rooted desc(cx); + + // Step 4. + size_t out = 0; + for (size_t i = 0; i < len; i++) { + id = ids[i]; + + // Step 4.a. (Symbols were filtered out in step 2.) + MOZ_ASSERT(!JSID_IS_SYMBOL(id)); + + if (kind != Values) { + if (!IdToStringOrSymbol(cx, id, &key)) + return false; + } + + // Step 4.a.i. + if (nobj) { + if (JSID_IS_INT(id) && nobj->containsDenseElement(JSID_TO_INT(id))) { + value = nobj->getDenseOrTypedArrayElement(JSID_TO_INT(id)); + } else { + shape = nobj->lookup(cx, id); + if (!shape || !(GetShapeAttributes(nobj, shape) & JSPROP_ENUMERATE)) + continue; + if (!shape->isAccessorShape()) { + if (!NativeGetExistingProperty(cx, nobj, nobj, shape, &value)) + return false; + } else if (!GetProperty(cx, obj, obj, id, &value)) { + return false; + } + } + } else { + if (!GetOwnPropertyDescriptor(cx, obj, id, &desc)) + return false; + + // Step 4.a.ii. (inverted.) + if (!desc.object() || !desc.enumerable()) + continue; + + // Step 4.a.ii.1. + // (Omitted because Object.keys doesn't use this implementation.) + + // Step 4.a.ii.2.a. + if (obj->isNative() && desc.hasValue()) + value = desc.value(); + else if (!GetProperty(cx, obj, obj, id, &value)) + return false; + } + + // Steps 4.a.ii.2.b-c. + if (kind == Values) + properties[out++].set(value); + else if (!NewValuePair(cx, key, value, properties[out++])) + return false; + } + + // Step 5. + // (Implemented in step 2.) + + // Step 3 of Object.{keys,values,entries} + JSObject* aobj = NewDenseCopiedArray(cx, out, properties.begin()); + if (!aobj) + return false; + + args.rval().setObject(*aobj); + return true; +} + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.keys static bool obj_keys(JSContext* cx, unsigned argc, Value* vp) { @@ -688,6 +790,24 @@ obj_keys(JSContext* cx, unsigned argc, Value* vp) return GetOwnPropertyKeys(cx, args, JSITER_OWNONLY); } +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.values +static bool +obj_values(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return EnumerableOwnProperties(cx, args, Values); +} + +// ES7 proposal 2015-12-14 +// http://tc39.github.io/proposal-object-values-entries/#Object.entries +static bool +obj_entries(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return EnumerableOwnProperties(cx, args, KeysAndValues); +} + /* ES6 draft 15.2.3.16 */ static bool obj_is(JSContext* cx, unsigned argc, Value* vp) @@ -1009,10 +1129,8 @@ static const JSFunctionSpec object_static_methods[] = { JS_FN("setPrototypeOf", obj_setPrototypeOf, 2, 0), JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2, 0), JS_FN("keys", obj_keys, 1, 0), -#ifndef RELEASE_BUILD - JS_SELF_HOSTED_FN("values", "ObjectValues", 1, JSPROP_DEFINE_LATE), - JS_SELF_HOSTED_FN("entries", "ObjectEntries", 1, JSPROP_DEFINE_LATE), -#endif + JS_FN("values", obj_values, 1, 0), + JS_FN("entries", obj_entries, 1, 0), JS_FN("is", obj_is, 2, 0), JS_FN("defineProperty", obj_defineProperty, 3, 0), JS_FN("defineProperties", obj_defineProperties, 2, 0), diff --git a/js/src/builtin/Object.js b/js/src/builtin/Object.js index db3e4aa67..150f1397a 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -133,49 +133,3 @@ function ObjectLookupGetter(name) { object = std_Reflect_getPrototypeOf(object); } while (object !== null); } - -// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.values -function ObjectValues(O) { - // Steps 1-2. - var object = ToObject(O); - - // Steps 3-4. - // EnumerableOwnProperties is inlined here. - var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN); - var values = []; - var valuesCount = 0; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!callFunction(std_Object_propertyIsEnumerable, object, key)) - continue; - - var value = object[key]; - _DefineDataProperty(values, valuesCount++, value); - } - - // Step 5. - return values; -} - -// Draft proposal http://tc39.github.io/proposal-object-values-entries/#Object.entries -function ObjectEntries(O) { - // Steps 1-2. - var object = ToObject(O); - - // Steps 3-4. - // EnumerableOwnProperties is inlined here. - var keys = OwnPropertyKeys(object, JSITER_OWNONLY | JSITER_HIDDEN); - var entries = []; - var entriesCount = 0; - for (var i = 0; i < keys.length; i++) { - var key = keys[i]; - if (!callFunction(std_Object_propertyIsEnumerable, object, key)) - continue; - - var value = object[key]; - _DefineDataProperty(entries, entriesCount++, [key, value]); - } - - // Step 5. - return entries; -} diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 1131720cb..4b274cfdb 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3671,6 +3671,20 @@ js::NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_ return NewCopiedArrayTryUseGroup(cx, group, vp, length); } +bool +js::NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval) +{ + JS::AutoValueArray<2> vec(cx); + vec[0].set(val1); + vec[1].set(val2); + + JSObject* aobj = js::NewDenseCopiedArray(cx, 2, vec.begin()); + if (!aobj) + return false; + rval.setObject(*aobj); + return true; +} + #ifdef DEBUG bool js::ArrayInfo(JSContext* cx, unsigned argc, Value* vp) diff --git a/js/src/jsarray.h b/js/src/jsarray.h index bcd62d175..9e200c6d6 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -123,6 +123,9 @@ extern JSObject* NewCopiedArrayForCallingAllocationSite(JSContext* cx, const Value* vp, size_t length, HandleObject proto = nullptr); +extern bool +NewValuePair(JSContext* cx, const Value& val1, const Value& val2, MutableHandleValue rval); + /* * Determines whether a write to the given element on |obj| should fail because * |obj| is an Array with a non-writable length, and writing that element would diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 9cbff88bf..1a8c2f0d6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -74,15 +74,7 @@ typedef HashSet IdSet; static inline bool NewKeyValuePair(JSContext* cx, jsid id, const Value& val, MutableHandleValue rval) { - JS::AutoValueArray<2> vec(cx); - vec[0].set(IdToValue(id)); - vec[1].set(val); - - JSObject* aobj = NewDenseCopiedArray(cx, 2, vec.begin()); - if (!aobj) - return false; - rval.setObject(*aobj); - return true; + return NewValuePair(cx, IdToValue(id), val, rval); } static inline bool diff --git a/js/src/tests/ecma_7/Object/values.js b/js/src/tests/ecma_7/Object/values.js index 78543c875..f5aaff4b6 100644 --- a/js/src/tests/ecma_7/Object/values.js +++ b/js/src/tests/ecma_7/Object/values.js @@ -3,7 +3,6 @@ * http://creativecommons.org/licenses/publicdomain/ */ -if ("values" in Object) { assertEq(Object.values.length, 1); var o, values; @@ -88,7 +87,6 @@ if ("values" in Object) { assertEq(ownKeysCallCount, 1); assertDeepEq(values, [3, 1]); assertDeepEq(getOwnPropertyDescriptorCalls, ["c", "a"]); -} if (typeof reportCompare === "function") reportCompare(true, true);