#428: optimized Object.values and Object.entries from M1232369

This commit is contained in:
Cameron Kaiser 2017-08-26 16:52:15 -07:00
parent 127ef91c91
commit af74f27ed9
6 changed files with 141 additions and 62 deletions

View File

@ -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<NativeObject>())
nobj = &obj->as<NativeObject>();
RootedShape shape(cx);
Rooted<PropertyDescriptor> 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),

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -74,15 +74,7 @@ typedef HashSet<jsid, JsidHasher> 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

View File

@ -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);