Bug 1185653 - Fix enumerate hook on unboxed objects to skip non-enumerable properties. r=jorendorff

--HG--
extra : rebase_source : 4f359cb0b34ee5a92afcb983ec765c92eaa1d162
This commit is contained in:
Jan de Mooij 2015-08-11 17:42:56 +02:00
parent 2c05aff66d
commit eed2b5bfff
9 changed files with 70 additions and 31 deletions

View File

@ -176,7 +176,8 @@ static bool
NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
static bool
NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties);
NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
bool enumerableOnly);
static bool
NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
@ -1615,7 +1616,7 @@ CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
static bool
NPObjWrapper_Enumerate(JSContext *cx, JS::Handle<JSObject*> obj,
JS::AutoIdVector &properties)
JS::AutoIdVector &properties, bool enumerableOnly)
{
NPObject *npobj = GetNPObject(cx, obj);
if (!npobj || !npobj->_class) {

View File

@ -255,19 +255,20 @@ typedef bool
(* JSDeletePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
JS::ObjectOpResult& result);
// The type of ObjectOps::enumerate. This callback overrides a portion of SpiderMonkey's default
// [[Enumerate]] internal method. When an ordinary object is enumerated, that object and each object
// on its prototype chain is tested for an enumerate op, and those ops are called in order.
// The properties each op adds to the 'properties' vector are added to the set of values the
// for-in loop will iterate over. All of this is nonstandard.
// The type of ObjectOps::enumerate. This callback overrides a portion of
// SpiderMonkey's default [[Enumerate]] internal method. When an ordinary object
// is enumerated, that object and each object on its prototype chain is tested
// for an enumerate op, and those ops are called in order. The properties each
// op adds to the 'properties' vector are added to the set of values the for-in
// loop will iterate over. All of this is nonstandard.
//
// An object is "enumerated" when it's the target of a for-in loop or JS_Enumerate().
// All other property inspection, including Object.keys(obj), goes through [[OwnKeys]].
//
// The callback's job is to populate 'properties' with all property keys that the for-in loop
// should visit.
// An object is "enumerated" when it's the target of a for-in loop or
// JS_Enumerate(). The callback's job is to populate 'properties' with the
// object's property keys. If `enumerableOnly` is true, the callback should only
// add enumerable properties.
typedef bool
(* JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties);
(* JSNewEnumerateOp)(JSContext* cx, JS::HandleObject obj, JS::AutoIdVector& properties,
bool enumerableOnly);
// The old-style JSClass.enumerate op should define all lazy properties not
// yet reflected in obj.

View File

@ -2059,7 +2059,8 @@ TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id, Ob
}
bool
TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties)
TypedObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly)
{
MOZ_ASSERT(obj->is<TypedObject>());
Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());

View File

@ -544,7 +544,8 @@ class TypedObject : public JSObject
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
public:
TypedProto& typedProto() const {

View File

@ -0,0 +1,28 @@
function f() {
var arr = [];
for (var i=0; i<80; i++) {
var o3 = {foo: i};
var o2 = {owner: o3};
arr.push(o2);
}
for (var i=0; i<80; i++) {
var o2 = arr[i];
var o3 = o2.owner;
Object.defineProperty(o3, "bar", {value: arr, enumerable: false});
}
assertEq(JSON.stringify(arr).length, 1671);
}
f();
function g() {
var arr = [];
for (var i=0; i<100; i++) {
arr.push([1, i]);
}
for (var i=0; i<100; i++) {
for (var p in arr[i]) {
assertEq(p === "0" || p === "1", true);
}
}
}
g();

View File

@ -286,24 +286,20 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
do {
if (JSNewEnumerateOp enumerate = pobj->getOps()->enumerate) {
// This hook has the full control over what gets enumerated.
AutoIdVector properties(cx);
if (!enumerate(cx, pobj, properties))
bool enumerableOnly = !(flags & JSITER_HIDDEN);
if (!enumerate(cx, pobj, properties, enumerableOnly))
return false;
RootedId id(cx);
for (size_t n = 0; n < properties.length(); n++) {
id = properties[n];
bool enumerable = true;
// The enumerate hook does not indicate whether the properties
// it returns are enumerable or not. There is no non-effectful
// way to determine this from the object, so carve out
// exceptions here for places where the property is not
// enumerable.
if (pobj->is<UnboxedArrayObject>() && id == NameToId(cx->names().length))
enumerable = false;
// it returns are enumerable or not. Since we already passed
// `enumerableOnly` to the hook to filter out non-enumerable
// properties, it doesn't really matter what we pass here.
bool enumerable = true;
if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props))
return false;
}

View File

@ -884,7 +884,8 @@ UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, Hand
}
/* static */ bool
UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties)
UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly)
{
UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
@ -907,6 +908,8 @@ UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector&
if (expando) {
Vector<jsid> ids(cx);
for (Shape::Range<NoGC> r(expando->lastProperty()); !r.empty(); r.popFront()) {
if (enumerableOnly && !r.front().enumerable())
continue;
if (!ids.append(r.front().propid()))
return false;
}
@ -1536,13 +1539,18 @@ UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, Hand
}
/* static */ bool
UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties)
UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly)
{
for (size_t i = 0; i < obj->as<UnboxedArrayObject>().initializedLength(); i++) {
if (!properties.append(INT_TO_JSID(i)))
return false;
}
return properties.append(NameToId(cx->names().length));
if (!enumerableOnly && !properties.append(NameToId(cx->names().length)))
return false;
return true;
}
const Class UnboxedArrayObject::class_ = {

View File

@ -262,7 +262,8 @@ class UnboxedPlainObject : public JSObject
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
inline const UnboxedLayout& layout() const;
@ -397,7 +398,8 @@ class UnboxedArrayObject : public JSObject
static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
ObjectOpResult& result);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties);
static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly);
static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
inline const UnboxedLayout& layout() const;

View File

@ -924,7 +924,8 @@ XPC_WN_Helper_Enumerate(JSContext* cx, HandleObject obj)
/***************************************************************************/
static bool
XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties)
XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
bool enumerableOnly)
{
XPCCallContext ccx(JS_CALLER, cx, obj);
XPCWrappedNative* wrapper = ccx.GetWrapper();