Bug 756919 - Handle prologue failure in ScopeIter (r=jimb)
authorLuke Wagner <luke@mozilla.com>
Fri, 25 May 2012 11:44:41 +0200
changeset 94954 f15f13527d494b9d6c977838865e8b543b953244
parent 94953 1c64ae5ff661baa0cd5284c4ca9763aadc4547f0
child 94955 8da4ff25a24a16a52d624a1b7f3586babef6bd81
push id22770
push userryanvm@gmail.com
push dateSat, 26 May 2012 12:07:39 +0000
treeherdermozilla-central@cd62c4b8f500 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs756919
milestone15.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
Bug 756919 - Handle prologue failure in ScopeIter (r=jimb)
js/src/jit-test/jit_test.py
js/src/jit-test/tests/basic/testBug756919.js
js/src/jsinterpinlines.h
js/src/vm/ScopeObject.cpp
--- a/js/src/jit-test/jit_test.py
+++ b/js/src/jit-test/jit_test.py
@@ -243,17 +243,17 @@ def check_output(out, err, rc, test):
 
     for line in err.split('\n'):
         if 'Assertion failed:' in line:
             return False
 
     if rc != test.expect_status:
         # Allow a non-zero exit code if we want to allow OOM, but only if we
         # actually got OOM.
-        return test.allow_oom and ': out of memory' in err
+        return test.allow_oom and ': out of memory' in err and 'Assertion failure' not in err
 
     return True
 
 def print_tinderbox(label, test, message=None):
     jitflags = " ".join(test.jitflags)
     result = "%s | jit_test.py %-15s| %s" % (label, jitflags, test.path)
     if message:
         result += ": " + message
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testBug756919.js
@@ -0,0 +1,8 @@
+// |jit-test| allow-oom
+
+gcparam("maxBytes", gcparam("gcBytes") + 1024);
+test();
+function test() {
+  test();
+  eval('');
+}
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -523,17 +523,17 @@ AssertValidFunctionScopeChainAtExit(Stac
     if (fp->isEvalFrame()) {
         AssertValidEvalFrameScopeChainAtExit(fp);
         return;
     }
 
     JS_ASSERT(!fp->hasBlockChain());
     JSObject &scope = *fp->scopeChain();
 
-    if (fp->fun()->isHeavyweight())
+    if (fp->fun()->isHeavyweight() && fp->hasCallObj())
         JS_ASSERT(scope.asCall().maybeStackFrame() == fp);
     else if (scope.isCall() || scope.isBlock())
         JS_ASSERT(scope.asScope().maybeStackFrame() != fp);
 #endif
 }
 
 static JS_ALWAYS_INLINE bool
 AddOperation(JSContext *cx, const Value &lhs, const Value &rhs, Value *res)
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1146,22 +1146,25 @@ ScopeIter::settle()
      * Given an iterator state (cur_, block_), figure out which (potentially
      * optimized) scope the iterator should report. Thus, the result is a pair
      * (type_, hasScopeObject_) where hasScopeObject_ indicates whether the
      * scope object has been optimized away and does not exist on the scope
      * chain. Beware: while ScopeIter iterates over the scopes of a single
      * frame, the scope chain (pointed to by cur_) continues into the scopes of
      * enclosing frames. Thus, it is important not to look at cur_ until it is
      * certain that cur_ points to a scope object in the current frame. In
-     * particular, there are two tricky corner cases:
-     *  - nested non-heavyweight functions;
+     * particular, there are three tricky corner cases:
+     *  - non-heavyweight functions;
      *  - non-strict direct eval.
-     * In both cases, cur_ can already be pointing into an enclosing frame's
-     * scope chain. As a final twist: even if cur_ points into an enclosing
-     * frame's scope chain, the current frame may still have uncloned blocks.
+     *  - heavyweight functions observed before the prologue has finished;
+     * In all cases, cur_ can already be pointing into an enclosing frame's
+     * scope chain. Furthermore, in the first two cases: even if cur_ points
+     * into an enclosing frame's scope chain, the current frame may still have
+     * uncloned blocks. In the last case, since we haven't entered the
+     * function, we simply return a ScopeIter where done() == true.
      *
      * Note: DebugScopeObject falls nicely into this plan: since they are only
      * ever introduced as the *enclosing* scope of a frame, they should never
      * show up in scope iteration and fall into the final non-scope case.
      */
     if (fp_->isNonEvalFunctionFrame() && !fp_->fun()->isHeavyweight()) {
         if (block_) {
             type_ = Block;
@@ -1173,16 +1176,19 @@ ScopeIter::settle()
     } else if (fp_->isNonStrictDirectEvalFrame() && cur_ == fp_->prev()->scopeChain()) {
         if (block_) {
             JS_ASSERT(!block_->needsClone());
             type_ = Block;
             hasScopeObject_ = false;
         } else {
             fp_ = NULL;
         }
+    } else if (fp_->isNonEvalFunctionFrame() && !fp_->hasCallObj()) {
+        JS_ASSERT(cur_ == fp_->fun()->environment());
+        fp_ = NULL;
     } else if (cur_->isWith()) {
         JS_ASSERT_IF(fp_->isFunctionFrame(), fp_->fun()->isHeavyweight());
         JS_ASSERT_IF(block_, block_->needsClone());
         JS_ASSERT_IF(block_, block_->stackDepth() < cur_->asWith().stackDepth());
         type_ = With;
         hasScopeObject_ = true;
     } else if (block_) {
         type_ = Block;