From 70ee8fa7f37effdcb53b8831c4be6123ae39a172 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Wed, 30 Mar 2011 07:08:58 -0700 Subject: [PATCH] [INFER] Cache standard class objects earlier to avoid reentrant class construction, bug 646393. --- js/src/jit-test/tests/basic/bug646393.js | 4 ++ js/src/jsobj.cpp | 56 +++++++++++++++++------- js/src/jsobj.h | 4 -- 3 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug646393.js diff --git a/js/src/jit-test/tests/basic/bug646393.js b/js/src/jit-test/tests/basic/bug646393.js new file mode 100644 index 00000000000..5d98e15410f --- /dev/null +++ b/js/src/jit-test/tests/basic/bug646393.js @@ -0,0 +1,4 @@ +try { + x.y; +} catch(ex) {} +x = Number(1); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 05fd4e6515e..a30c50e8b99 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3865,6 +3865,9 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, { jsid id = ATOM_TO_JSID(atom); + if (!cx->addTypePropertyId(obj->getType(), id, v)) + return false; + if (key != JSProto_Null) { /* * Initializing an actual standard class on a global object. If the @@ -3896,6 +3899,28 @@ DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, namespace js { +static bool +SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) +{ + JS_ASSERT(!obj->getParent()); + if (!obj->isGlobal()) + return true; + + return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) && + js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto)); +} + +static void +ClearClassObject(JSContext *cx, JSObject *obj, JSProtoKey key) +{ + JS_ASSERT(!obj->getParent()); + if (!obj->isGlobal()) + return; + + obj->setSlot(key, UndefinedValue()); + obj->setSlot(JSProto_LIMIT + key, UndefinedValue()); +} + JSObject * DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom, JSObject *protoProto, Class *clasp, @@ -3955,6 +3980,7 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt JSObject *ctor; bool named = false; + bool cached = false; if (!constructor) { /* * Lacking a constructor, name the prototype (e.g., Math) unless this @@ -3966,8 +3992,6 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS) ? JSPROP_READONLY | JSPROP_PERMANENT : 0; - if (!cx->addTypePropertyId(obj->getType(), ATOM_TO_JSID(atom), ObjectValue(*proto))) - goto bad; if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named)) goto bad; } @@ -3979,9 +4003,20 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt JSFunction *fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom, ctorHandler, clasp->name); - if (!fun || !cx->addTypePropertyId(obj->getType(), ATOM_TO_JSID(atom), ObjectValue(*fun))) + if (!fun) goto bad; + /* + * Set the class object early for standard class constructors. Type + * inference may need to access these, and js_GetClassPrototype will + * fail if it tries to do a reentrant reconstruction of the class. + */ + if (key != JSProto_Null && !(clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) && + !SetClassObject(cx, obj, key, fun, proto)) { + goto bad; + } + cached = true; + AutoValueRooter tvr2(cx, ObjectValue(*fun)); if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named)) goto bad; @@ -4071,7 +4106,7 @@ DefineConstructorAndPrototype(JSContext *cx, JSObject *obj, JSProtoKey key, JSAt } /* If this is a standard class, cache its prototype. */ - if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto)) + if (!cached && key != JSProto_Null && !SetClassObject(cx, obj, key, ctor, proto)) goto bad; return proto; @@ -4081,6 +4116,8 @@ bad: Value rval; obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false); } + if (cached) + ClearClassObject(cx, obj, key); return NULL; } @@ -4339,17 +4376,6 @@ js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, return true; } -JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto) -{ - JS_ASSERT(!obj->getParent()); - if (!obj->isGlobal()) - return JS_TRUE; - - return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) && - js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto)); -} - JSBool js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey, Value *vp, Class *clasp) diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 83d699a65c0..e852a116678 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1564,10 +1564,6 @@ extern JSBool js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject **objp); -extern JSBool -js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, - JSObject *cobj, JSObject *prototype); - /* * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is * JSProto_Null, clasp must non-null.