Bug 679977 - Guard on callee when accessing upvar through deactivated stack frame. (r=gal)
authorShu-yu Guo <shu@rfrn.org>
Tue, 30 Aug 2011 22:45:31 +0100
changeset 76234 72974e2ef258a29a36a866f2ab44ba2b7c02592e
parent 76233 9eaca4ef5880a4b7f8916476061891164a61327f
child 76235 734bf8fbdb814bfeba0c29cc83a250b02eae50ad
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersgal
bugs679977
milestone9.0a1
Bug 679977 - Guard on callee when accessing upvar through deactivated stack frame. (r=gal)
js/src/jit-test/tests/basic/bug679977.js
js/src/jstracer.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug679977.js
@@ -0,0 +1,26 @@
+var Test = function (foo) {
+    var a = [];
+
+    this.fillArray = function() {
+        a = [];
+        for (var i = 0; i < 10; i++)
+            a.push(0);
+        assertEq(a.length, 10);
+    }
+
+    foo.go(this);
+};
+
+// Import assertEq now to prevent global object shape from changing.
+assertEq(true, true);
+
+(new Test({ go: function(p) {
+    p.fill = function() {
+        p.fillArray();
+    }
+}})).fill();
+
+new Test({ go: function(p) {
+    for (var k = 0; k < 10; k++)
+        p.fillArray();
+}});
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -8288,39 +8288,51 @@ TraceRecorder::callProp(JSObject* obj, J
                                  ? JSGET_NO_METHOD_BARRIER
                                  : JSGET_METHOD_BARRIER,
                                  &nr.v);
         JS_ASSERT(rv);
     }
 
     LIns* obj_ins;
     JSObject* parent = cx->fp()->callee().getParent();
-    LIns* parent_ins = w.ldpObjParent(get(&cx->fp()->calleev()));
+    LIns *callee_ins = get(&cx->fp()->calleev());
+    LIns* parent_ins = w.ldpObjParent(callee_ins);
     CHECK_STATUS(traverseScopeChain(parent, parent_ins, obj, obj_ins));
 
     if (!cfp) {
-        // Because the parent guard in guardCallee ensures this Call object
-        // will be the same object now and on trace, and because once a Call
-        // object loses its frame it never regains one, on trace we will also
-        // have a null private in the Call object. So all we need to do is
-        // write the value to the Call object's slot.
+        // We need to guard that this Call object will be the same one on
+        // trace, as we can arrive here without having gone through
+        // guardCallee. Once a Call object loses its frame it never regains
+        // one, so on trace we will also have a null private in the Call
+        // object. All we need to do is write the value to the Call object's
+        // slot.
+        VMSideExit *branchExit = snapshot(BRANCH_EXIT);
+        if (parent != globalObj) {
+            if (!parent->isCall())
+                RETURN_STOP("closure scoped by neither the global object nor a Call object");
+
+            guard(true,
+                  w.eqp(w.ldpObjParent(callee_ins), w.immpObjGC(parent)),
+                  branchExit);
+        }
+
         if (shape->getterOp() == GetCallArg) {
             JS_ASSERT(slot < ArgClosureTraits::slot_count(obj));
             slot += ArgClosureTraits::slot_offset(obj);
         } else if (shape->getterOp() == GetCallVar) {
             JS_ASSERT(slot < VarClosureTraits::slot_count(obj));
             slot += VarClosureTraits::slot_offset(obj);
         } else {
             RETURN_STOP("dynamic property of Call object");
         }
 
         // Now assert that our use of shape->shortid was in fact kosher.
         JS_ASSERT(shape->hasShortID());
 
-        ins = unbox_slot(obj, obj_ins, slot, snapshot(BRANCH_EXIT));
+        ins = unbox_slot(obj, obj_ins, slot, branchExit);
     } else {
         ClosureVarInfo* cv = new (traceAlloc()) ClosureVarInfo();
         cv->slot = slot;
 #ifdef DEBUG
         cv->callDepth = callDepth;
 #endif
 
         // Even though the frame is out of range, later we might be called as an