[INFER] When pushing new frame don't repoint cx->regs until after checking there is space to push, bug 669304.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 05 Jul 2011 10:12:23 -0700
changeset 75176 c5e43682922d87d6217b6abe363a242262e4959a
parent 75175 279a046a56cd4ef5fb087715ec140a28f52a3953
child 75177 7083531ddbd4b9e67e30a668b5dcc5d5e544ab0c
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs669304
milestone7.0a1
[INFER] When pushing new frame don't repoint cx->regs until after checking there is space to push, bug 669304.
js/src/methodjit/InvokeHelpers.cpp
js/src/vm/Stack-inl.h
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -310,26 +310,31 @@ UncachedInlineCall(VMFrame &f, MaybeCons
 
     bool newType = construct && cx->typeInferenceEnabled() &&
         types::UseNewType(cx, f.script(), f.pc());
 
     types::TypeMonitorCall(cx, args, construct);
 
     /*
      * Preserve f.regs.fp while pushing the new frame, for the invariant that
-     * f.regs reflects the state when we entered the stub call.
+     * f.regs reflects the state when we entered the stub call. This handoff is
+     * tricky: we need to make sure that f.regs is not updated to the new
+     * frame, and we also need to ensure that cx->regs still points to f.regs
+     * when space is reserved, in case doing so throws an exception.
      */
     FrameRegs regs = f.regs;
-    PreserveRegsGuard regsGuard(cx, regs);
 
     /* Get pointer to new frame/slots, prepare arguments. */
     LimitCheck check(&f.stackLimit, NULL);
     if (!cx->stack.pushInlineFrame(cx, regs, args, callee, newfun, newscript, construct, check))
         return false;
 
+    /* Finish the handoff to the new frame regs. */
+    PreserveRegsGuard regsGuard(cx, regs);
+
     /* Scope with a call object parented by callee's parent. */
     if (newfun->isHeavyweight() && !js::CreateFunCallObject(cx, regs.fp()))
         return false;
 
     /* Try to compile if not already compiled. */
     if (newscript->getJITStatus(f.fp()->isConstructing()) == JITScript_None) {
         CompileStatus status = CanMethodJIT(cx, newscript, regs.fp(), CompileRequest_Interpreter);
         if (status == Compile_Error) {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -541,29 +541,33 @@ ContextStack::getCallFrame(JSContext *cx
 
 template <class Check>
 JS_ALWAYS_INLINE bool
 ContextStack::pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                               JSObject &callee, JSFunction *fun, JSScript *script,
                               MaybeConstruct construct, Check check)
 {
     JS_ASSERT(onTop());
-    JS_ASSERT(&regs == &seg_->regs());
     JS_ASSERT(regs.sp == args.end());
     /* Cannot assert callee == args.callee() since this is called from LeaveTree. */
     JS_ASSERT(callee.getFunctionPrivate() == fun);
     JS_ASSERT(fun->script() == script);
 
     StackFrame::Flags flags = ToFrameFlags(construct);
     StackFrame *fp = getCallFrame(cx, args, fun, script, &flags, check);
     if (!fp)
         return false;
 
     /* Initialize frame, locals, regs. */
     fp->initCallFrame(cx, callee, fun, script, args.argc(), flags);
+
+    /*
+     * N.B. regs may differ from the active registers, if the parent is about
+     * to repoint the active registers to regs. See UncachedInlineCall.
+     */
     regs.prepareToRun(*fp, script);
     return true;
 }
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getFixupFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
                             JSFunction *fun, JSScript *script, void *ncode,
                             MaybeConstruct construct, LimitCheck check)