Bug 463239 - JS_SetTrap alters code execution. r=brendan.
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 18 Feb 2009 18:31:09 -0600
changeset 25215 949d496b5a84b5bee2455ef79dbc96890aa7f292
parent 25214 e8cc3865df16b81786eb4090626ac93bc8bb8bda
child 25216 1ed2d8b3c28dfdae135a9de7d1024ac7b4cc6d0c
push id5455
push userrsayre@mozilla.com
push dateThu, 19 Feb 2009 16:48:29 +0000
treeherdermozilla-central@95cc774339a0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs463239
milestone1.9.2a1pre
Bug 463239 - JS_SetTrap alters code execution. r=brendan.
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscript.h
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -185,17 +185,17 @@ js_FillPropertyCache(JSContext *cx, JSOb
         *entryp = NULL;
         return;
     }
 
     /*
      * Optimize the cached vword based on our parameters and the current pc's
      * opcode format flags.
      */
-    op = (JSOp) *pc;
+    op = js_GetOpcode(cx, cx->fp->script, pc);
     cs = &js_CodeSpec[op];
 
     do {
         /*
          * Check for a prototype "plain old method" callee computation. What
          * is a plain old method? It's a function-valued property with stub
          * getter and setter, so get of a function is idempotent and set is
          * transparent.
@@ -314,17 +314,17 @@ js_FullTestPropertyCache(JSContext *cx, 
     JSAtom *atom;
     JSObject *obj, *pobj, *tmp;
     JSPropCacheEntry *entry;
     uint32 vcap;
 
     JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
               < cx->fp->script->length);
 
-    op = (JSOp) *pc;
+    op = js_GetOpcode(cx, cx->fp->script, pc);
     cs = &js_CodeSpec[op];
     if (op == JSOP_LENGTH) {
         atom = cx->runtime->atomState.lengthAtom;
     } else {
         pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;
         GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
     }
 
@@ -3073,17 +3073,18 @@ js_Interpret(JSContext *cx)
                 /* Restore the calling script's interpreter registers. */
                 script = fp->script;
                 atoms = FrameAtomBase(cx, fp);
 
                 /* Resume execution in the calling frame. */
                 inlineCallCount--;
                 if (JS_LIKELY(ok)) {
                     TRACE_0(LeaveFrame);
-                    JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
+                    JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
+                              == JSOP_CALL_LENGTH);
                     len = JSOP_CALL_LENGTH;
                     DO_NEXT_OP(len);
                 }
                 goto error;
             }
             goto exit;
 
           BEGIN_CASE(JSOP_DEFAULT)
@@ -3398,17 +3399,17 @@ js_Interpret(JSContext *cx)
         JSObject *obj_, *pobj_;                                               \
         JSProperty *prop_;                                                    \
         JSScopeProperty *sprop_;                                              \
         uint32 sample_ = rt->gcNumber;                                        \
         if (pcoff >= 0)                                                       \
             GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_);            \
         else                                                                  \
             atom_ = rt->atomState.lengthAtom;                                 \
-        if (JOF_OPMODE(*regs.pc) == JOF_NAME) {                               \
+        if (JOF_OPMODE(op) == JOF_NAME) {                                     \
             ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_,      \
                                  &prop_);                                     \
         } else {                                                              \
             obj_ = obj;                                                       \
             ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_,      \
                                    &prop_);                                   \
         }                                                                     \
         if (!ok)                                                              \
@@ -5069,17 +5070,17 @@ js_Interpret(JSContext *cx)
             argc = GET_ARGC(regs.pc);
             vp = regs.sp - argc - 2;
             ok = js_Invoke(cx, argc, vp, 0);
             regs.sp = vp + 1;
             CHECK_INTERRUPT_HANDLER();
             if (!ok)
                 goto error;
             if (!cx->rval2set) {
-                op2 = (JSOp) regs.pc[JSOP_SETCALL_LENGTH];
+                op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH);
                 if (op2 != JSOP_DELELEM) {
                     JS_ASSERT(!(js_CodeSpec[op2].format & JOF_DEL));
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_BAD_LEFTSIDE_OF_ASS);
                     goto error;
                 }
 
                 /*
@@ -5131,17 +5132,17 @@ js_Interpret(JSContext *cx)
             }
 
             id = ATOM_TO_JSID(atom);
             if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
                 goto error;
             if (!prop) {
                 /* Kludge to allow (typeof foo == "undefined") tests. */
                 endpc = script->code + script->length;
-                op2 = (JSOp) regs.pc[JSOP_NAME_LENGTH];
+                op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
                 if (op2 == JSOP_TYPEOF) {
                     PUSH_OPND(JSVAL_VOID);
                     len = JSOP_NAME_LENGTH;
                     DO_NEXT_OP(len);
                 }
                 goto atom_not_defined;
             }
 
@@ -6286,17 +6287,17 @@ js_Interpret(JSContext *cx)
              * If rval is a hole, do not call OBJ_DEFINE_PROPERTY. In this case,
              * obj must be an array, so if the current op is the last element
              * initialiser, set the array length to one greater than id.
              */
             if (rval == JSVAL_HOLE) {
                 JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
                 JS_ASSERT(JSID_IS_INT(id));
                 JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT);
-                if ((JSOp) regs.pc[JSOP_INITELEM_LENGTH] == JSOP_ENDINIT &&
+                if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
                     !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
                     goto error;
                 }
             } else {
                 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
                     goto error;
             }
             regs.sp -= 2;
@@ -7035,17 +7036,17 @@ js_Interpret(JSContext *cx)
                  * Restart the handler search with updated pc and stack depth
                  * to properly notify the debugger.
                  */
                 goto error;
             }
 
             switch (tn->kind) {
               case JSTRY_CATCH:
-                JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
+                JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENTERBLOCK);
 
 #if JS_HAS_GENERATORS
                 /* Catch cannot intercept the closing of a generator. */
                 if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN))
                     break;
 #endif
 
                 /*
@@ -7070,17 +7071,17 @@ js_Interpret(JSContext *cx)
               case JSTRY_ITER:
                 /*
                  * This is similar to JSOP_ENDITER in the interpreter loop,
                  * except the code now uses the stack slot normally used by
                  * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
                  * adjustment and regs.sp[1] after, to save and restore the
                  * pending exception.
                  */
-                JS_ASSERT(*regs.pc == JSOP_ENDITER);
+                JS_ASSERT(js_GetOpcode(cx, fp->script, regs.pc) == JSOP_ENDITER);
                 regs.sp[-1] = cx->exception;
                 cx->throwing = JS_FALSE;
                 ok = js_CloseIterator(cx, regs.sp[-2]);
                 regs.sp -= 2;
                 if (!ok)
                     goto error;
                 cx->throwing = JS_TRUE;
                 cx->exception = regs.sp[1];
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -67,17 +67,17 @@
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jstracer.h"
-#include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
+#include "jsdbgapi.h"
 
 #if JS_HAS_GENERATORS
 #include "jsiter.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
@@ -1188,19 +1188,20 @@ js_ComputeFilename(JSContext *cx, JSStac
     flags = JS_GetScriptFilenameFlags(caller->script);
     if ((flags & JSFILENAME_PROTECTED) &&
         principals &&
         strcmp(principals->codebase, "[System Principal]")) {
         *linenop = 0;
         return principals->codebase;
     }
 
-    if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
-        JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO);
-        *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH);
+    jsbytecode *pc = caller->regs->pc;
+    if (caller->regs && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) {
+        JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
+        *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
     } else {
         *linenop = js_FramePCToLineNumber(cx, caller);
     }
     return caller->script->filename;
 }
 
 #ifndef EVAL_CACHE_CHAIN_LIMIT
 # define EVAL_CACHE_CHAIN_LIMIT 4
@@ -2012,67 +2013,63 @@ js_Object(JSContext *cx, JSObject *obj, 
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
  * checking whether document.all is defined.
  */
 static JS_REQUIRES_STACK JSBool
 Detecting(JSContext *cx, jsbytecode *pc)
 {
     JSScript *script;
-    jsbytecode *endpc;
     JSOp op;
     JSAtom *atom;
 
     if (!cx->fp)
         return JS_FALSE;
     script = cx->fp->script;
-    for (endpc = script->code + script->length;
-         pc < endpc;
-         pc += js_CodeSpec[op].length) {
+    for (;; pc += js_CodeSpec[op].length) {
         /* General case: a branch or equality op follows the access. */
-        op = (JSOp) *pc;
+        op = js_GetOpcode(cx, script, pc);
         if (js_CodeSpec[op].format & JOF_DETECTING)
             return JS_TRUE;
 
         switch (op) {
           case JSOP_NULL:
             /*
              * Special case #1: handle (document.all == null).  Don't sweat
              * about JS1.2's revision of the equality operators here.
              */
-            if (++pc < endpc)
-                return *pc == JSOP_EQ || *pc == JSOP_NE;
-            return JS_FALSE;
+            pc++;
+            op = js_GetOpcode(cx, script, pc);
+            return op == JSOP_EQ || op == JSOP_NE;
 
           case JSOP_NAME:
             /*
              * Special case #2: handle (document.all == undefined).  Don't
              * worry about someone redefining undefined, which was added by
              * Edition 3, so is read/write for backward compatibility.
              */
             GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
-            if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
-                (pc += js_CodeSpec[op].length) < endpc) {
-                op = (JSOp) *pc;
+            if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID]) {
+                pc += js_CodeSpec[op].length;
+                op = js_GetOpcode(cx, script, pc);
                 return op == JSOP_EQ || op == JSOP_NE ||
                        op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
             }
             return JS_FALSE;
 
           default:
             /*
              * At this point, anything but an extended atom index prefix means
              * we're not detecting.
              */
             if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
                 return JS_FALSE;
             break;
         }
     }
-    return JS_FALSE;
 }
 
 /*
  * Infer lookup flags from the currently executing bytecode. This does
  * not attempt to infer JSRESOLVE_WITH, because the current bytecode
  * does not indicate whether we are in a with statement. Return defaultFlags
  * if a currently executing bytecode cannot be determined.
  */
@@ -2084,17 +2081,17 @@ InferFlags(JSContext *cx, uintN defaultF
     const JSCodeSpec *cs;
     uint32 format;
     uintN flags = 0;
 
     fp = js_GetTopStackFrame(cx);
     if (!fp || !fp->regs)
         return defaultFlags;
     pc = fp->regs->pc;
-    cs = &js_CodeSpec[*pc];
+    cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)];
     format = cs->format;
     if (JOF_MODE(format) != JOF_NAME)
         flags |= JSRESOLVE_QUALIFIED;
     if ((format & (JOF_SET | JOF_FOR)) ||
         (fp->flags & JSFRAME_ASSIGNING)) {
         flags |= JSRESOLVE_ASSIGNING;
     } else {
         pc += cs->length;
@@ -3941,18 +3938,21 @@ js_GetCurrentBytecodePC(JSContext* cx)
 {
     jsbytecode *pc = cx->pcHint;
     if (!pc || !JS_ON_TRACE(cx)) {
         JSStackFrame* fp = js_GetTopStackFrame(cx);
         if (fp && fp->regs) {
             pc = fp->regs->pc;
             // FIXME: Set pc to imacpc when recording JSOP_CALL inside the 
             //        JSOP_GETELEM imacro (bug 476559).
-            if (*pc == JSOP_CALL && fp->imacpc && *fp->imacpc == JSOP_GETELEM)
+            if (*pc == JSOP_CALL &&
+                fp->imacpc &&
+                js_GetOpcode(cx, fp->script, fp->imacpc) == JSOP_GETELEM) {
                 pc = fp->imacpc;
+            }
         } else {
             pc = NULL;
         }
     }
     return pc;
 }
 
 JSBool
@@ -3990,16 +3990,20 @@ js_GetPropertyHelper(JSContext *cx, JSOb
          * object foo with no property named 'bar'.
          */
         jsbytecode *pc;
         if (JSVAL_IS_VOID(*vp) && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
             JSOp op;
             uintN flags;
 
             op = (JSOp) *pc;
+            if (op == JSOP_TRAP) {
+                JS_ASSERT_NOT_ON_TRACE(cx);
+                op = JS_GetTrapOpcode(cx, cx->fp->script, pc);
+            }
             if (op == JSOP_GETXPROP) {
                 flags = JSREPORT_ERROR;
             } else {
                 if (!JS_HAS_STRICT_OPTION(cx) ||
                     (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
                     return JS_TRUE;
                 }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -133,19 +133,17 @@ GetJumpOffset(jsbytecode *pc, jsbytecode
 
 uintN
 js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
                         ptrdiff_t pcoff)
 {
     JSOp op;
     uintN span, base;
 
-    op = (JSOp)*pc;
-    if (op == JSOP_TRAP)
-        op = JS_GetTrapOpcode(cx, script, pc);
+    op = js_GetOpcode(cx, script, pc);
     JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
 
     /*
      * We need to detect index base prefix. It presents when resetbase
      * follows the bytecode.
      */
     span = js_CodeSpec[op].length;
     base = 0;
@@ -5206,19 +5204,17 @@ ReconstructPCStack(JSContext *cx, JSScri
      * operand-generating opcode PCs in pcstack.
      *
      * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
      * FIXME: Optimize to use last empty-stack sequence point.
      */
     LOCAL_ASSERT(script->main <= target && target < script->code + script->length);
     pcdepth = 0;
     for (pc = script->main; pc < target; pc += oplen) {
-        op = (JSOp) *pc;
-        if (op == JSOP_TRAP)
-            op = JS_GetTrapOpcode(cx, script, pc);
+        op = js_GetOpcode(cx, script, pc);
         cs = &js_CodeSpec[op];
         oplen = cs->length;
         if (oplen < 0)
             oplen = js_GetVariableBytecodeLength(pc);
 
         /*
          * A (C ? T : E) expression requires skipping either T (if target is in
          * E) or both T and E (if target is after the whole expression) before
@@ -5228,17 +5224,17 @@ ReconstructPCStack(JSContext *cx, JSScri
          */
         sn = js_GetSrcNote(script, pc);
         if (sn && SN_TYPE(sn) == SRC_COND) {
             ptrdiff_t jmpoff, jmplen;
 
             jmpoff = js_GetSrcNoteOffset(sn, 0);
             if (pc + jmpoff < target) {
                 pc += jmpoff;
-                op = (JSOp) *pc;
+                op = js_GetOpcode(cx, script, pc);
                 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
                 cs = &js_CodeSpec[op];
                 oplen = cs->length;
                 JS_ASSERT(oplen > 0);
                 jmplen = GetJumpOffset(pc, pc);
                 if (pc + jmplen < target) {
                     oplen = (uintN) jmplen;
                     continue;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1751,32 +1751,34 @@ uintN
 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
 {
     return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc);
 }
 
 uintN
 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
+    JSOp op;
     JSFunction *fun;
     uintN lineno;
     ptrdiff_t offset, target;
     jssrcnote *sn;
     JSSrcNoteType type;
 
     /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
     if (!pc)
         return 0;
 
     /*
      * Special case: function definition needs no line number note because
      * the function's script contains its starting line number.
      */
-    if (js_CodeSpec[*pc].format & JOF_INDEXBASE)
-        pc += js_CodeSpec[*pc].length;
+    op = js_GetOpcode(cx, script, pc);
+    if (js_CodeSpec[op].format & JOF_INDEXBASE)
+        pc += js_CodeSpec[op].length;
     if (*pc == JSOP_DEFFUN) {
         GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun);
         return fun->u.i.script->lineno;
     }
 
     /*
      * General case: walk through source notes accumulating their deltas,
      * keeping track of line-number notes, until we pass the note for pc's
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -40,16 +40,17 @@
 
 #ifndef jsscript_h___
 #define jsscript_h___
 /*
  * JS script descriptor.
  */
 #include "jsatom.h"
 #include "jsprvtd.h"
+#include "jsdbgapi.h"
 
 JS_BEGIN_EXTERN_C
 
 /*
  * Type of try note associated with each catch or finally block, and also with
  * for-in loops.
  */
 typedef enum JSTryNoteKind {
@@ -310,16 +311,25 @@ extern uintN
 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern jsbytecode *
 js_LineNumberToPC(JSScript *script, uintN lineno);
 
 extern JS_FRIEND_API(uintN)
 js_GetScriptLineExtent(JSScript *script);
 
+static JS_INLINE JSOp
+js_GetOpcode(JSContext *cx, JSScript *script, jsbytecode *pc)
+{
+    JSOp op = (JSOp) *pc;
+    if (op == JSOP_TRAP)
+        op = JS_GetTrapOpcode(cx, script, pc);
+    return op;
+}
+
 /*
  * If magic is non-null, js_XDRScript succeeds on magic number mismatch but
  * returns false in *magic; it reflects a match via a true *magic out param.
  * If magic is null, js_XDRScript returns false on bad magic number errors,
  * which it reports.
  *
  * NB: callers must call js_CallNewScriptHook after successful JSXDR_DECODE
  * and subsequent set-up of owning function or script object, if any.