Lots of MISMATCH_EXITS exits due to protohazardshape on voxel rendering demo (550391, r=mrbkap).
authorBrendan Eich <brendan@mozilla.org>
Wed, 10 Nov 2010 10:59:02 -0800
changeset 57779 e0f43abe672faf3cb9ca6a386fdb59264d91d258
parent 57778 e1c038d2ed9b7060f203da967ce998aac14c8187
child 57780 b60e61a172b819ba5bbecfb3b7e440034b678f9d
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersmrbkap
bugs550391
milestone2.0b8pre
Lots of MISMATCH_EXITS exits due to protohazardshape on voxel rendering demo (550391, r=mrbkap).
js/src/jsfun.cpp
js/src/jsobj.h
js/src/jstracer.cpp
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2921,40 +2921,65 @@ js_DefineFunction(JSContext *cx, JSObjec
          */
         attrs &= ~JSFUN_STUB_GSOPS;
         gsop = PropertyStub;
     } else {
         gsop = NULL;
     }
 
     /*
-     * Historically, all objects have a parent slot and all native functions
-     * defined by the JS_DefineFunction* APIs funnel here and bind parent to
-     * obj. But this prematurely deoptimizes by flagging, e.g. Date.prototype,
-     * as a "delegate" (a proto or parent of some other object), which in turn
-     * causes shadowingShapeChange events and shape regeneration for common
-     * method names that are also (and already) bound on, say, Object.prototype
-     * (e.g., toString).
+     * Historically, all objects have had a parent member as intrinsic scope
+     * chain link. We want to move away from this universal parent, but JS
+     * requires that function objects have something like parent (ES3 and ES5
+     * call it the [[Scope]] internal property), to bake a particular static
+     * scope environment into each function object.
+     *
+     * All function objects thus have parent, including all native functions.
+     * All native functions defined by the JS_DefineFunction* APIs are created
+     * via the call below to js_NewFunction, which passes obj as the parent
+     * parameter, and so binds fun's parent to obj using JSObject::setParent,
+     * under js_NewFunction (in JSObject::init, called from NewObject -- see
+     * jsobjinlines.h).
+     *
+     * But JSObject::setParent sets the DELEGATE object flag on its receiver,
+     * to mark the object as a proto or parent of another object. Such objects
+     * may intervene in property lookups and scope chain searches, so require
+     * special handling when caching lookup and search results (since such
+     * intervening objects can in general grow shadowing properties later).
      *
-     * Until we get rid of parent, avoid flagging standard class prototype
-     * objects as delegates prematurely when defining their methods, instead
-     * parenting each method to the proto's global. We keep API compatibility
-     * for all obj parameters that are not of a standard (cached-proto-key)
-     * class, since some embedding clients count on parent being obj.
+     * Thus using setParent prematurely flags certain objects, notably class
+     * prototypes, so that defining native methods on them, where the method's
+     * name (e.g., toString) is already bound on Object.prototype, triggers
+     * shadowingShapeChange events and gratuitous shape regeneration.
+     *
+     * To fix this longstanding bug, we set check whether obj is already a
+     * delegate, and if not, then if js_NewFunction flagged obj as a delegate,
+     * we clear the flag.
+     *
+     * We thus rely on the fact that native functions (including indirect eval)
+     * do not use the property cache or equivalent JIT techniques that require
+     * this bit to be set on their parent-linked scope chain objects.
+     *
+     * Note: we keep API compatibility by setting parent to obj for all native
+     * function objects, even if obj->getGlobal() would suffice. This should be
+     * revisited when parent is narrowed to exist only for function objects and
+     * possibly a few prehistoric scope objects (e.g. event targets).
      */
-    JSObject *parent = (JSCLASS_CACHED_PROTO_KEY(obj->clasp) != JSProto_Null)
-                       ? obj->getGlobal()
-                       : obj;
+    bool wasDelegate = obj->isDelegate();
 
     fun = js_NewFunction(cx, NULL, native, nargs,
                          attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO),
-                         parent,
+                         obj,
                          JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
     if (!fun)
         return NULL;
+
+    if (!wasDelegate && obj->isDelegate())
+        obj->clearDelegate();
+
     if (!obj->defineProperty(cx, id, ObjectValue(*fun), gsop, gsop, attrs & ~JSFUN_FLAGS_MASK))
         return NULL;
     return fun;
 }
 
 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
 #endif
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -399,16 +399,17 @@ struct JSObject : js::gc::Cell {
 
     uint32 shape() const {
         JS_ASSERT(objShape != JSObjectMap::INVALID_SHAPE);
         return objShape;
     }
 
     bool isDelegate() const     { return !!(flags & DELEGATE); }
     void setDelegate()          { flags |= DELEGATE; }
+    void clearDelegate()        { flags &= ~DELEGATE; }
 
     bool isBoundFunction() const { return !!(flags & BOUND_FUNCTION); }
 
     static void setDelegateNullSafe(JSObject *obj) {
         if (obj)
             obj->setDelegate();
     }
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -9397,17 +9397,17 @@ TraceRecorder::guardPropertyCacheHit(LIn
     }
 
     if (entry->adding()) {
         LIns *vshape_ins =
             w.ldiRuntimeProtoHazardShape(w.ldpConstContextField(runtime));
 
         guard(true,
               w.name(w.eqiN(vshape_ins, vshape), "guard_protoHazardShape"),
-              MISMATCH_EXIT);
+              BRANCH_EXIT);
     }
 
     // For any hit that goes up the scope and/or proto chains, we will need to
     // guard on the shape of the object containing the property.
     if (entry->vcapTag() >= 1) {
         JS_ASSERT(obj2->shape() == vshape);
         if (obj2 == globalObj)
             RETURN_STOP("hitting the global object via a prototype chain");