[INFER] Ensure type/prototype are preserved when cloning functions at method barriers, bug 683714.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 06 Sep 2011 22:49:47 -0700
changeset 78362 50d4f6fa00ced827a04e199d77814fcfe25faf5d
parent 78361 6a8947bcc821f3d7d0859757878f42f88fc74ba5
child 78363 f3dd7cf2d0b3f8efad9fe733662b0fe65e4488cc
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs683714
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[INFER] Ensure type/prototype are preserved when cloning functions at method barriers, bug 683714.
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/methodjit/StubCalls.cpp
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1312,17 +1312,17 @@ StackFrame::getValidCalleeObject(JSConte
              * barrier, so we must clone fun and store it in fp's callee to
              * avoid re-cloning upon repeated foo.caller access.
              *
              * This must mean the code in js_DeleteProperty could not find this
              * stack frame on the stack when the method was deleted. We've lost
              * track of the method, so we associate it with the first barriered
              * object found starting from thisp on the prototype chain.
              */
-            JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent(), true);
+            JSObject *newfunobj = CloneFunctionObject(cx, fun);
             if (!newfunobj)
                 return false;
             newfunobj->setMethodObj(*first_barriered_thisp);
             overwriteCallee(*newfunobj);
             vp->setObject(*newfunobj);
             return true;
         }
     }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -458,16 +458,34 @@ CloneFunctionObject(JSContext *cx, JSFun
         JS_ASSERT(fun->getProto() == proto);
         fun->setParent(parent);
         return fun;
     }
 
     return js_CloneFunctionObject(cx, fun, parent, proto);
 }
 
+inline JSObject *
+CloneFunctionObject(JSContext *cx, JSFunction *fun)
+{
+    /*
+     * Variant which makes an exact clone of fun, preserving parent and proto.
+     * Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
+     * is not equivalent: API clients, including XPConnect, can reparent
+     * objects so that fun->getGlobal() != fun->getProto()->getGlobal().
+     * See ReparentWrapperIfFound.
+     */
+    JS_ASSERT(fun->getParent() && fun->getProto());
+
+    if (fun->hasSingletonType())
+        return fun;
+
+    return js_CloneFunctionObject(cx, fun, fun->getParent(), fun->getProto());
+}
+
 extern JSObject * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
 
 extern JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, js::Native native,
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4807,16 +4807,17 @@ BEGIN_CASE(JSOP_DEFFUN)
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
     if (obj->getParent() != obj2) {
         obj = CloneFunctionObject(cx, fun, obj2, true);
         if (!obj)
             goto error;
+        JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     uintN attrs = regs.fp()->isEvalFrame()
                   ? JSPROP_ENUMERATE
@@ -4940,16 +4941,18 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
                 AbortRecording(cx, "DEFLOCALFUN for closure");
 #endif
             obj = CloneFunctionObject(cx, fun, parent, true);
             if (!obj)
                 goto error;
         }
     }
 
+    JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
+
     uint32 slot = GET_SLOTNO(regs.pc);
     TRACE_2(DefLocalFunSetSlot, slot, obj);
 
     regs.fp()->slots()[slot].setObject(*obj);
 }
 END_CASE(JSOP_DEFLOCALFUN)
 
 BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
@@ -5058,28 +5061,31 @@ BEGIN_CASE(JSOP_LAMBDA)
         }
 
         obj = CloneFunctionObject(cx, fun, parent, true);
         if (!obj)
             goto error;
     } while (0);
 
     JS_ASSERT(obj->getProto());
+    JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
+
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_LAMBDA)
 
 BEGIN_CASE(JSOP_LAMBDA_FC)
 {
     JSFunction *fun;
     LOAD_FUNCTION(0);
 
     JSObject *obj = js_NewFlatClosure(cx, fun, JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH);
     if (!obj)
         goto error;
+    JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_LAMBDA_FC)
 
 BEGIN_CASE(JSOP_CALLEE)
     JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
     PUSH_COPY(argv[-2]);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5908,17 +5908,17 @@ CloneFunctionForSetMethod(JSContext *cx,
     JSObject *funobj = &vp->toObject();
     JSFunction *fun = funobj->getFunctionPrivate();
 
     /*
      * If fun is already different from the original JSFunction, it does not
      * need to be cloned again.
      */
     if (fun == funobj) {
-        funobj = CloneFunctionObject(cx, fun, fun->parent, true);
+        funobj = CloneFunctionObject(cx, fun);
         if (!funobj)
             return false;
         vp->setObject(*funobj);
     }
     return true;
 }
 
 JSBool
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -261,17 +261,17 @@ JSObject::methodReadBarrier(JSContext *c
     JS_ASSERT(shape.hasDefaultSetter());
     JS_ASSERT(!isGlobal());  /* i.e. we are not changing the global shape */
 
     JSObject *funobj = &vp->toObject();
     JSFunction *fun = funobj->getFunctionPrivate();
     JS_ASSERT(fun == funobj);
     JS_ASSERT(fun->isNullClosure());
 
-    funobj = CloneFunctionObject(cx, fun, funobj->getParent(), true);
+    funobj = CloneFunctionObject(cx, fun);
     if (!funobj)
         return NULL;
     funobj->setMethodObj(*this);
 
     /*
      * Replace the method property with an ordinary data property. This is
      * equivalent to this->setProperty(cx, shape.id, vp) except that any
      * watchpoint on the property is not triggered.
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -787,16 +787,17 @@ stubs::DefFun(VMFrame &f, JSFunction *fu
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
     if (obj->getParent() != obj2) {
         obj = CloneFunctionObject(cx, fun, obj2, true);
         if (!obj)
             THROW();
+        JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
      */
     uintN attrs = fp->isEvalFrame()
                   ? JSPROP_ENUMERATE
@@ -1437,16 +1438,18 @@ stubs::DefLocalFun(VMFrame &f, JSFunctio
 
         if (obj->getParent() != parent) {
             obj = CloneFunctionObject(f.cx, fun, parent, true);
             if (!obj)
                 THROWV(NULL);
         }
     }
 
+    JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
+
     return obj;
 }
 
 JSObject * JS_FASTCALL
 stubs::DefLocalFun_FC(VMFrame &f, JSFunction *fun)
 {
     JSObject *obj = js_NewFlatClosure(f.cx, fun, JSOP_DEFLOCALFUN_FC, JSOP_DEFLOCALFUN_FC_LENGTH);
     if (!obj)
@@ -1551,16 +1554,17 @@ stubs::Lambda(VMFrame &f, JSFunction *fu
         if (!parent)
             THROWV(NULL);
     }
 
     JSObject *obj = CloneFunctionObject(f.cx, fun, parent, true);
     if (!obj)
         THROWV(NULL);
 
+    JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
     return obj;
 }
 
 static bool JS_ALWAYS_INLINE
 InlineGetProp(VMFrame &f)
 {
     JSContext *cx = f.cx;
     FrameRegs &regs = f.regs;