mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 15:52:07 +00:00
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:
parent
2c05aff66d
commit
eed2b5bfff
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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>());
|
||||
|
@ -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 {
|
||||
|
28
js/src/jit-test/tests/basic/bug1185653.js
Normal file
28
js/src/jit-test/tests/basic/bug1185653.js
Normal 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();
|
@ -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;
|
||||
}
|
||||
|
@ -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_ = {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user