[INFER] Make sure to get the topmost scripted frame's pc in ContextStack::currentScript, bug 662562.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 07 Jun 2011 17:44:07 -0700
changeset 75152 afe33041f4819e18a263ce778ceafed1dd262644
parent 75151 380d5080ea15e6d0b59919483c78b72fde20886f
child 75153 a53db4f2d235f538b283d85bfba2163816e13e30
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs662562
milestone7.0a1
[INFER] Make sure to get the topmost scripted frame's pc in ContextStack::currentScript, bug 662562.
js/src/jit-test/tests/basic/bug662562.js
js/src/jsdbgapi.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/methodjit/Retcon.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug662562.js
@@ -0,0 +1,6 @@
+// |jit-test| error: TypeError
+function f(o) {
+    o.watch("x", this);
+}
+var c = evalcx("");
+f(c);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1435,17 +1435,17 @@ JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
 {
     return Valueify(fp)->maybeScript();
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
 {
-    return Valueify(fp)->pcQuadratic(cx);
+    return Valueify(fp)->pcQuadratic(cx->stack);
 }
 
 JS_PUBLIC_API(JSStackFrame *)
 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
 {
     return Jsvalify(js_GetScriptedCaller(cx, Valueify(fp)));
 }
 
@@ -2490,19 +2490,19 @@ jstv_Filename(JSStackFrame *fp)
         fp = fp->prev();
     return (fp && fp->maybeScript() && fp->script()->filename)
            ? (char *)fp->script()->filename
            : jstv_empty;
 }
 inline uintN
 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
 {
-    while (fp && fp->pcQuadratic(cx) == NULL)
+    while (fp && fp->pcQuadratic(cx->stack) == NULL)
         fp = fp->prev();
-    return (fp && fp->pcQuadratic(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
+    return (fp && fp->pcQuadratic(cx->stack)) ? js_FramePCToLineNumber(cx, fp) : 0;
 }
 
 /* Collect states here and distribute to a matching buffer, if any */
 JS_FRIEND_API(void)
 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
 {
     StackFrame *fp = cx->fp();
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1637,17 +1637,17 @@ fun_getProperty(JSContext *cx, JSObject 
 #ifdef JS_METHODJIT
     if (slot == FUN_CALLER && fp && fp->prev()) {
         /*
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
         JSInlinedSite *inlined;
-        fp->prev()->pcQuadratic(cx, fp, &inlined);
+        fp->prev()->pcQuadratic(cx->stack, fp, &inlined);
         if (inlined) {
             JSFunction *fun = fp->prev()->jit()->inlineFrames()[inlined->inlineIndex].fun;
             MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
         }
     }
 #endif
 
     switch (slot) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2968,17 +2968,17 @@ TypeObject::clearNewScript(JSContext *cx
                     } else if (init->offset > offset) {
                         /* Advanced past all properties which have been initialized. */
                         break;
                     } else if (init->offset == offset) {
                         StackSegment &seg = cx->stack.space().containingSegment(fp);
                         if (seg.currentFrame() == fp)
                             break;
                         fp = seg.computeNextFrame(fp);
-                        pc = fp->pcQuadratic(cx);
+                        pc = fp->pcQuadratic(cx->stack);
                     } else {
                         /* This call has already finished. */
                         depth = 1;
                     }
                 } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) {
                     if (depth) {
                         depth--;
                     } else {
@@ -4717,17 +4717,17 @@ MarkTypeCallerUnexpectedSlow(JSContext *
      * called, such as on property getters or setters. This filtering is not
      * perfect, but we only need to make sure the type result is added wherever
      * the native's type handler was used, i.e. at scripted callsites directly
      * calling the native.
      */
 
     jsbytecode *pc;
     JSScript *script = cx->stack.currentScript(&pc);
-    if (!script)
+    if (!script || !pc)
         return;
 
     /*
      * Watch out if the caller is in a different compartment from this one.
      * This must have gone through a cross-compartment wrapper.
      */
     if (script->compartment != cx->compartment)
         return;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -150,17 +150,17 @@ js::GetScopeChain(JSContext *cx)
  */
 JSObject *
 js::GetBlockChain(JSContext *cx, StackFrame *fp)
 {
     if (!fp->isScriptFrame())
         return NULL;
 
     /* Assume that imacros don't affect blockChain */
-    jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pcQuadratic(cx);
+    jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pcQuadratic(cx->stack);
 
     JSScript *script = fp->script();
     jsbytecode *start = script->code;
     JS_ASSERT(target >= start && target < start + script->length);
 
     JSObject *blockChain = NULL;
     uintN indexBase = 0;
     ptrdiff_t oplen;
@@ -196,17 +196,17 @@ js::GetBlockChain(JSContext *cx, StackFr
  * instruction appears immediately after the current PC.
  * We ensure this happens for a few important ops like DEFFUN.
  * |oplen| is the length of opcode at the current PC.
  */
 JSObject *
 js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
 {
     /* Assume that we're in a script frame. */
-    jsbytecode *pc = fp->pcQuadratic(cx);
+    jsbytecode *pc = fp->pcQuadratic(cx->stack);
     JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
 
     pc += oplen;
     op = JSOp(*pc);
 
     /* The fast paths assume no JSOP_RESETBASE/INDEXBASE or JSOP_TRAP noise. */
     if (op == JSOP_NULLBLOCKCHAIN)
         return NULL;
@@ -2569,17 +2569,17 @@ Interpret(JSContext *cx, StackFrame *ent
         goto error;
 
     /* Don't call the script prologue if executing between Method and Trace JIT. */
     if (interpMode == JSINTERP_NORMAL) {
         StackFrame *fp = regs.fp();
         JS_ASSERT_IF(!fp->isGeneratorFrame(), regs.pc == script->code);
         bool newType = fp->isConstructing() && cx->typeInferenceEnabled() &&
             fp->prev() && fp->prev()->isScriptFrame() &&
-            UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx, fp));
+            UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx->stack, fp));
         if (!ScriptPrologueOrGeneratorResume(cx, fp, newType))
             goto error;
     }
 
     /* The REJOIN mode acts like the normal mode, except the prologue is skipped. */
     if (interpMode == JSINTERP_REJOIN)
         interpMode = JSINTERP_NORMAL;
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1162,17 +1162,17 @@ EvalKernel(JSContext *cx, const CallArgs
      * way so that the compiler can make assumptions about what bindings may or
      * may not exist in the current frame if it doesn't see 'eval'.)
      */
     uintN staticLevel;
     if (evalType == DIRECT_EVAL) {
         staticLevel = caller->script()->staticLevel + 1;
 
 #ifdef DEBUG
-        jsbytecode *callerPC = caller->pcQuadratic(cx);
+        jsbytecode *callerPC = caller->pcQuadratic(cx->stack);
         JS_ASSERT_IF(caller->isFunctionFrame(), caller->fun()->isHeavyweight());
         JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
 #endif
     } else {
         JS_ASSERT(call.callee().getGlobal() == &scopeobj);
         staticLevel = 0;
     }
 
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -319,17 +319,17 @@ ExpandInlineFrames(JSContext *cx, bool a
                 mjit::Recompiler::expandInlineFrames(cx, f->fp(), f->regs.inlined(), nnext, f);
             }
         }
 
         StackFrame *end = f->entryfp->prev();
         StackFrame *next = NULL;
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             mjit::CallSite *inlined;
-            fp->pcQuadratic(cx, next, &inlined);
+            fp->pcQuadratic(cx->stack, next, &inlined);
             if (next && inlined) {
                 mjit::Recompiler::expandInlineFrames(cx, fp, inlined, next, f);
                 fp = next;
                 next = NULL;
             } else {
                 if (fp->downFramesExpanded())
                     break;
                 next = fp;
@@ -426,17 +426,17 @@ Recompiler::recompile(bool resetUses)
          */
         StackFrame *fp = f->fp();
         void **addr = f->returnAddressLocation();
         RejoinState rejoin = (RejoinState) f->stubRejoin;
         if (rejoin == REJOIN_NATIVE ||
             rejoin == REJOIN_NATIVE_LOWERED) {
             /* Native call. */
             if (fp->script() == script) {
-                patchNative(cx, fp->jit(), fp, fp->pcQuadratic(cx, NULL), NULL, rejoin);
+                patchNative(cx, fp->jit(), fp, fp->pcQuadratic(cx->stack, NULL), NULL, rejoin);
                 f->stubRejoin = REJOIN_NATIVE_PATCHED;
             }
         } else if (rejoin == REJOIN_NATIVE_PATCHED) {
             /* Already patched, don't do anything. */
         } else if (rejoin) {
             /* Recompilation triggered by CompileFunction. */
             if (fp->script() == script) {
                 fp->setRejoin(StubRejoin(rejoin));
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -447,17 +447,17 @@ StackFrame::initEvalFrame(JSContext *cx,
         exec = prev->exec;
         args.script = script;
     } else {
         exec.script = script;
     }
 
     scopeChain_ = &prev->scopeChain();
     prev_ = prev;
-    prevpc_ = prev->pcQuadratic(cx, NULL, &prevInline_);
+    prevpc_ = prev->pcQuadratic(cx->stack, NULL, &prevInline_);
     JS_ASSERT(!hasImacropc());
     JS_ASSERT(!hasHookData());
     setAnnotation(prev->annotation());
 }
 
 inline void
 StackFrame::initGlobalFrame(JSScript *script, JSObject &chain, StackFrame *prev, uint32 flagsArg)
 {
@@ -1105,17 +1105,17 @@ ContextStack::currentScript(jsbytecode *
         mjit::InlineFrame *frame = &fp->jit()->inlineFrames()[inlined->inlineIndex];
         if (ppc)
             *ppc = frame->fun->script()->code + inlined->pcOffset;
         return frame->fun->script();
     }
 #endif
 
     if (ppc)
-        *ppc = regs_->pc;
+        *ppc = fp->pcQuadratic(*this);
     return fp->script();
 }
 
 inline JSObject *
 ContextStack::currentScriptedScopeChain() const
 {
     return &regs_->fp()->scopeChain();
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -87,21 +87,21 @@ StackFrame::prevpcSlow(JSInlinedSite **p
     return prevpc_;
 #else
     JS_NOT_REACHED("Unknown PC for frame");
     return NULL;
 #endif
 }
 
 jsbytecode *
-StackFrame::pcQuadratic(JSContext *cx, StackFrame *next, JSInlinedSite **pinlined)
+StackFrame::pcQuadratic(const ContextStack &stack, StackFrame *next, JSInlinedSite **pinlined)
 {
     JS_ASSERT_IF(next, next->prev() == this);
 
-    StackSegment &seg = cx->stack.space().containingSegment(this);
+    StackSegment &seg = stack.space().containingSegment(this);
     FrameRegs &regs = seg.currentRegs();
 
     /*
      * This isn't just an optimization; seg->computeNextFrame(fp) is only
      * defined if fp != seg->currentFrame.
      */
     if (regs.fp() == this) {
         if (pinlined)
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -458,26 +458,26 @@ class StackFrame
      * If the frame is inside an inline call made within the pc, the pc will
      * be that of the outermost call and the state of any inlined frame(s) is
      * returned through pinlined.
      *
      * Beware, as the name implies, pcQuadratic can lead to quadratic behavior
      * in loops such as:
      *
      *   for ( ...; fp; fp = fp->prev())
-     *     ... fp->pcQuadratic(cx);
+     *     ... fp->pcQuadratic(cx->stack);
      *
      * Using next can avoid this, but in most cases prefer FrameRegsIter;
      * it is amortized O(1).
      *
      *   When I get to the bottom I go back to the top of the stack
      *   Where I stop and I turn and I go right back
      *   Till I get to the bottom and I see you again...
      */
-    jsbytecode *pcQuadratic(JSContext *cx, StackFrame *next = NULL,
+    jsbytecode *pcQuadratic(const ContextStack &stack, StackFrame *next = NULL,
                             JSInlinedSite **pinlined = NULL);
 
     jsbytecode *prevpc(JSInlinedSite **pinlined) {
         if (flags_ & HAS_PREVPC) {
             if (pinlined)
                 *pinlined = prevInline_;
             return prevpc_;
         }