diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index 7ec0130674df..2be81665ac20 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -678,7 +678,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) { @@ -686,6 +788,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) @@ -1007,10 +1127,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 bebd2751e620..1694206b6c38 100644 --- a/js/src/builtin/Object.js +++ b/js/src/builtin/Object.js @@ -139,49 +139,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 269ebfb820a2..f62fdd93464d 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3679,6 +3679,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 7d666fd670ce..275f9c619c36 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -119,6 +119,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 bc261868b7e7..662e9f0d611f 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -84,15 +84,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