Skip to content

Commit

Permalink
#428: optimized Object.values and Object.entries from M1232369
Browse files Browse the repository at this point in the history
  • Loading branch information
classilla committed Aug 26, 2017
1 parent 127ef91 commit af74f27
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 62 deletions.
128 changes: 123 additions & 5 deletions js/src/builtin/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -680,14 +680,134 @@ 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)
{
CallArgs args = CallArgsFromVp(argc, 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)
Expand Down Expand Up @@ -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),
Expand Down
46 changes: 0 additions & 46 deletions js/src/builtin/Object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
14 changes: 14 additions & 0 deletions js/src/jsarray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions js/src/jsarray.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 1 addition & 9 deletions js/src/jsiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions js/src/tests/ecma_7/Object/values.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* http://creativecommons.org/licenses/publicdomain/
*/

if ("values" in Object) {
assertEq(Object.values.length, 1);

var o, values;
Expand Down Expand Up @@ -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);

0 comments on commit af74f27

Please sign in to comment.