Bug 586482 - arguments.callee.caller not equal to proto-delegated joined function object method (r=igor).
authorBrendan Eich <brendan@mozilla.org>
Thu, 11 Nov 2010 10:06:56 -0800
changeset 59994 441a46526a95af562ef2ec27691106d3af549b74
parent 59993 cd2898655d25703228fdaa5d08472437da349e23
child 59995 e07d85cea44b618cbe6d0fc9f95a729cf4f432f5
push id17820
push usercleary@mozilla.com
push dateTue, 04 Jan 2011 21:40:57 +0000
treeherdermozilla-central@969691cfe40e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersigor
bugs586482
milestone2.0b9pre
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
Bug 586482 - arguments.callee.caller not equal to proto-delegated joined function object method (r=igor).
js/src/jsfun.cpp
js/src/jsobj.cpp
js/src/tests/js1_8_5/regress/jstests.list
js/src/tests/js1_8_5/regress/regress-586482.js
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1401,70 +1401,77 @@ JSStackFrame::getValidCalleeObject(JSCon
      * atom by which it was uniquely associated with a property.
      */
     const Value &thisv = functionThis();
     if (thisv.isObject()) {
         JS_ASSERT(funobj.getFunctionPrivate() == fun);
 
         if (&fun->compiledFunObj() == &funobj && fun->methodAtom()) {
             JSObject *thisp = &thisv.toObject();
-            JS_ASSERT(thisp->canHaveMethodBarrier());
-
-            if (thisp->hasMethodBarrier()) {
-                const Shape *shape = thisp->nativeLookup(ATOM_TO_JSID(fun->methodAtom()));
-
+
+            do {
                 /*
-                 * The method property might have been deleted while the method
-                 * barrier flag stuck, so we must lookup and test here.
-                 *
-                 * Two cases follow: the method barrier was not crossed yet, so
-                 * we cross it here; the method barrier *was* crossed, in which
-                 * case we must fetch and validate the cloned (unjoined) funobj
-                 * in the method property's slot.
-                 *
-                 * In either case we must allow for the method property to have
-                 * been replaced, or its value to have been overwritten.
+                 * No point worrying about anything above a non-native object
+                 * on the |this| object's prototype chain, as a non-native is
+                 * responsible for handling its entire prototype chain.
                  */
+                if (!thisp->isNative())
+                    break;
+
+                const Shape *shape = thisp->nativeLookup(ATOM_TO_JSID(fun->methodAtom()));
                 if (shape) {
-                    if (shape->isMethod() && &shape->methodObject() == &funobj) {
-                        if (!thisp->methodReadBarrier(cx, *shape, vp))
-                            return false;
-                        calleeValue().setObject(vp->toObject());
-                        return true;
-                    }
-                    if (shape->hasSlot()) {
-                        Value v = thisp->getSlot(shape->slot);
-                        JSObject *clone;
-
-                        if (IsFunctionObject(v, &clone) &&
-                            GET_FUNCTION_PRIVATE(cx, clone) == fun &&
-                            clone->hasMethodObj(*thisp)) {
-                            JS_ASSERT(clone != &funobj);
-                            *vp = v;
-                            calleeValue().setObject(*clone);
+                    if (thisp->hasMethodBarrier()) {
+                        /*
+                         * Two cases follow: the method barrier was not crossed
+                         * yet, so we cross it here; the method barrier *was*
+                         * crossed but after the call, in which case we fetch
+                         * and validate the cloned (unjoined) funobj from the
+                         * method property's slot.
+                         *
+                         * In either case we must allow for the method property
+                         * to have been replaced, or its value overwritten.
+                         */
+                        if (shape->isMethod() && &shape->methodObject() == &funobj) {
+                            if (!thisp->methodReadBarrier(cx, *shape, vp))
+                                return false;
+                            calleeValue().setObject(vp->toObject());
                             return true;
                         }
+
+                        if (shape->hasSlot()) {
+                            Value v = thisp->getSlot(shape->slot);
+                            JSObject *clone;
+
+                            if (IsFunctionObject(v, &clone) &&
+                                GET_FUNCTION_PRIVATE(cx, clone) == fun &&
+                                clone->hasMethodObj(*thisp)) {
+                                JS_ASSERT(clone != &funobj);
+                                *vp = v;
+                                calleeValue().setObject(*clone);
+                                return true;
+                            }
+                        }
+
+                        /*
+                         * If control flow reaches here, we couldn't find an
+                         * already-existing clone (or force to exist a fresh
+                         * clone) created via thisp's method read barrier, so
+                         * we must clone fun and store it in fp's callee to
+                         * avoid re-cloning upon repeated foo.caller access.
+                         * There are not any properties left referring to fun.
+                         */
+                        JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent());
+                        if (!newfunobj)
+                            return false;
+                        newfunobj->setMethodObj(*thisp);
+                        calleeValue().setObject(*newfunobj);
                     }
+                    return true;
                 }
-
-                /*
-                 * If control flows here, we can't find an already-existing
-                 * clone (or force to exist a fresh clone) created via thisp's
-                 * method read barrier, so we must clone fun and store it in
-                 * fp's callee to avoid re-cloning upon repeated foo.caller
-                 * access. It seems that there are no longer any properties
-                 * referring to fun.
-                 */
-                JSObject *newfunobj = CloneFunctionObject(cx, fun, fun->getParent());
-                if (!newfunobj)
-                    return false;
-                newfunobj->setMethodObj(*thisp);
-                calleeValue().setObject(*newfunobj);
-                return true;
-            }
+            } while ((thisp = thisp->getProto()) != NULL);
         }
     }
 
     return true;
 }
 
 /* Generic function tinyids. */
 enum {
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4867,16 +4867,17 @@ js_LookupPropertyWithFlagsInline(JSConte
         }
 
         JSObject *proto = obj->getProto();
         if (!proto)
             break;
         if (!proto->isNative()) {
             if (!proto->lookupProperty(cx, id, objp, propp))
                 return -1;
+            JS_ASSERT_IF(*propp, !(*objp)->isNative());
             return protoIndex + 1;
         }
 
         obj = proto;
     }
 
     *objp = NULL;
     *propp = NULL;
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -26,16 +26,17 @@ script regress-566914.js
 script regress-567152.js
 script regress-569306.js
 script regress-569464.js
 script regress-571014.js
 script regress-577648-1.js
 script regress-577648-2.js
 script regress-583429.js
 script regress-584355.js
+script regress-586482.js
 script regress-588339.js
 script regress-yarr-regexp.js
 script regress-592217.js
 script regress-592556-c35.js
 script regress-593256.js
 fails-if(!xulRuntime.shell) script regress-595230-1.js
 fails-if(!xulRuntime.shell) script regress-595230-2.js
 script regress-595365-1.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-586482.js
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var expect = true;
+var actual;
+
+var checkCaller = function(me) {
+    var caller = arguments.callee.caller;
+    var callerIsMethod = (caller === me['doThing']);
+    actual = callerIsMethod;
+};
+
+var MyObj = function() {
+};
+
+MyObj.prototype.doThing = function() {
+    checkCaller(this);
+};
+
+(new MyObj()).doThing();
+
+reportCompare(expect, actual, "ok");