Bug 1147216 - Part 1: Give JSOP_LINENO a 4-byte instead of 2-byte operand. r=luke, a=lizzard
authorJan de Mooij <jdemooij@mozilla.com>
Fri, 10 Apr 2015 11:38:50 +0200
changeset 265555 2deec68d84bb52f6116365f2b588e77188895ef9
parent 265554 07cb25ccc7da38208a8942f7d9c5b4e59fab5947
child 265556 729003f39455b1f1bd8d152b1b326ef791d736c9
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, lizzard
bugs1147216
milestone39.0a2
Bug 1147216 - Part 1: Give JSOP_LINENO a 4-byte instead of 2-byte operand. r=luke, a=lizzard
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit-test/tests/basic/bug1147216.js
js/src/jsopcode.cpp
js/src/jsopcode.h
js/src/jsscript.cpp
js/src/vm/Opcodes.h
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -531,21 +531,32 @@ BytecodeEmitter::checkTypeSet(JSOp op)
 {
     if (js_CodeSpec[op].format & JOF_TYPESET) {
         if (typesetCount < UINT16_MAX)
             typesetCount++;
     }
 }
 
 bool
-BytecodeEmitter::emitUint16Operand(JSOp op, uint32_t i)
-{
-    MOZ_ASSERT(i <= UINT16_MAX);
-    if (!emit3(op, UINT16_HI(i), UINT16_LO(i)))
-        return false;
+BytecodeEmitter::emitUint16Operand(JSOp op, uint32_t operand)
+{
+    MOZ_ASSERT(operand <= UINT16_MAX);
+    if (!emit3(op, UINT16_HI(operand), UINT16_LO(operand)))
+        return false;
+    checkTypeSet(op);
+    return true;
+}
+
+bool
+BytecodeEmitter::emitUint32Operand(JSOp op, uint32_t operand)
+{
+    ptrdiff_t off;
+    if (!emitN(op, 4, &off))
+        return false;
+    SET_UINT32(code(off), operand);
     checkTypeSet(op);
     return true;
 }
 
 bool
 BytecodeEmitter::flushPops(int* npops)
 {
     MOZ_ASSERT(*npops != 0);
@@ -6178,17 +6189,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
     }
     checkTypeSet(pn->getOp());
     if (pn->isOp(JSOP_EVAL) ||
         pn->isOp(JSOP_STRICTEVAL) ||
         pn->isOp(JSOP_SPREADEVAL) ||
         pn->isOp(JSOP_STRICTSPREADEVAL))
     {
         uint32_t lineNum = parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
-        if (!emitUint16Operand(JSOP_LINENO, lineNum))
+        if (!emitUint32Operand(JSOP_LINENO, lineNum))
             return false;
     }
     if (pn->pn_xflags & PNX_SETCALL) {
         if (!emit1(JSOP_SETCALL))
             return false;
     }
     return true;
 }
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -388,17 +388,20 @@ struct BytecodeEmitter
     // you don't know the size of the fixed stack segment (nfixed), as is the case
     // when compiling scripts (because each statement is parsed and compiled
     // separately, but they all together form one script with one fixed stack
     // frame).
     bool emitDupAt(unsigned slot);
 
     // Emit a bytecode followed by an uint16 immediate operand stored in
     // big-endian order.
-    bool emitUint16Operand(JSOp op, uint32_t i);
+    bool emitUint16Operand(JSOp op, uint32_t operand);
+
+    // Emit a bytecode followed by an uint32 immediate operand.
+    bool emitUint32Operand(JSOp op, uint32_t operand);
 
     // Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand.
     bool emitN(JSOp op, size_t extra, ptrdiff_t* offset = nullptr);
 
     bool emitNumberOp(double dval);
 
     bool emitJump(JSOp op, ptrdiff_t off, ptrdiff_t* jumpOffset = nullptr);
     bool emitCall(JSOp op, uint16_t argc, ParseNode* pn = nullptr);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1147216.js
@@ -0,0 +1,14 @@
+// Ensure JSOP_LINENO (emitted after JSOP_EVAL) handles big line
+// numbers correctly.
+function f() {
+    var s = "";
+    var stack;
+    for (var i=0; i<66002; i++) {
+	s += "\n";
+	if (i === 66000)
+	    s += "eval('stack = Error().stack');";
+    }
+    eval(s);
+    assertEq(stack.indexOf("line 66002") > 0, true);
+}
+f();
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1039,16 +1039,20 @@ js::Disassemble1(JSContext* cx, HandleSc
       case JOF_QARG:
         Sprint(sp, " %u", GET_ARGNO(pc));
         break;
 
       case JOF_LOCAL:
         Sprint(sp, " %u", GET_LOCALNO(pc));
         break;
 
+      case JOF_UINT32:
+        Sprint(sp, " %u", GET_UINT32(pc));
+        break;
+
       {
         int i;
 
       case JOF_UINT16:
         i = (int)GET_UINT16(pc);
         goto print_int;
 
       case JOF_UINT24:
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -43,18 +43,18 @@ enum {
     /* 5 is unused */
     JOF_QARG            = 6,        /* quickened get/set function argument ops */
     JOF_LOCAL           = 7,        /* var or block-local variable */
     JOF_DOUBLE          = 8,        /* uint32_t index for double value */
     JOF_UINT24          = 12,       /* extended unsigned 24-bit literal (index) */
     JOF_UINT8           = 13,       /* uint8_t immediate, e.g. top 8 bits of 24-bit
                                        atom index */
     JOF_INT32           = 14,       /* int32_t immediate operand */
-    JOF_OBJECT          = 15,       /* unsigned 16-bit object index */
-    /* 16 is unused */
+    JOF_UINT32          = 15,       /* uint32_t immediate operand */
+    JOF_OBJECT          = 16,       /* unsigned 16-bit object index */
     JOF_REGEXP          = 17,       /* unsigned 32-bit regexp index */
     JOF_INT8            = 18,       /* int8_t immediate operand */
     JOF_ATOMOBJECT      = 19,       /* uint16_t constant index + object index */
     /* 20 is unused */
     JOF_SCOPECOORD      = 21,       /* embedded ScopeCoordinate immediate */
     JOF_TYPEMASK        = 0x001f,   /* mask for above immediate types */
 
     JOF_NAME            = 1 << 5,   /* name operation */
@@ -225,32 +225,44 @@ SET_UINT24(jsbytecode* pc, unsigned i)
 }
 
 static MOZ_ALWAYS_INLINE int8_t
 GET_INT8(const jsbytecode* pc)
 {
     return int8_t(pc[1]);
 }
 
-static MOZ_ALWAYS_INLINE int32_t
-GET_INT32(const jsbytecode* pc)
+static MOZ_ALWAYS_INLINE uint32_t
+GET_UINT32(const jsbytecode* pc)
 {
     return  (uint32_t(pc[1]) << 24) |
             (uint32_t(pc[2]) << 16) |
             (uint32_t(pc[3]) << 8)  |
             uint32_t(pc[4]);
 }
 
 static MOZ_ALWAYS_INLINE void
-SET_INT32(jsbytecode* pc, uint32_t i)
+SET_UINT32(jsbytecode* pc, uint32_t u)
 {
-    pc[1] = jsbytecode(uint32_t(i) >> 24);
-    pc[2] = jsbytecode(uint32_t(i) >> 16);
-    pc[3] = jsbytecode(uint32_t(i) >> 8);
-    pc[4] = jsbytecode(uint32_t(i));
+    pc[1] = jsbytecode(u >> 24);
+    pc[2] = jsbytecode(u >> 16);
+    pc[3] = jsbytecode(u >> 8);
+    pc[4] = jsbytecode(u);
+}
+
+static MOZ_ALWAYS_INLINE int32_t
+GET_INT32(const jsbytecode* pc)
+{
+    return static_cast<int32_t>(GET_UINT32(pc));
+}
+
+static MOZ_ALWAYS_INLINE void
+SET_INT32(jsbytecode* pc, int32_t i)
+{
+    SET_UINT32(pc, static_cast<uint32_t>(i));
 }
 
 /* Index limit is determined by SN_4BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */
 static const unsigned INDEX_LIMIT_LOG2  = 31;
 static const uint32_t INDEX_LIMIT       = uint32_t(1) << INDEX_LIMIT_LOG2;
 
 static inline jsbytecode
 ARGC_HI(uint16_t argc)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2914,17 +2914,17 @@ js::DescribeScriptedCallerForCompilation
         static_assert(JSOP_EVAL_LENGTH == JSOP_STRICTEVAL_LENGTH,
                     "next op after a direct eval must be at consistent offset");
         MOZ_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_STRICTEVAL ||
                    JSOp(*pc) == JSOP_SPREADEVAL || JSOp(*pc) == JSOP_STRICTSPREADEVAL);
         mozilla::DebugOnly<bool> isSpread = JSOp(*pc) == JSOP_SPREADEVAL ||
                                             JSOp(*pc) == JSOP_STRICTSPREADEVAL;
         MOZ_ASSERT(*(pc + (isSpread ? JSOP_SPREADEVAL_LENGTH : JSOP_EVAL_LENGTH)) == JSOP_LINENO);
         *file = maybeScript->filename();
-        *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
+        *linenop = GET_UINT32(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
                                                            : JSOP_SPREADEVAL_LENGTH));
         *pcOffset = pc - maybeScript->code();
         *mutedErrors = maybeScript->mutedErrors();
         return;
     }
 
     NonBuiltinFrameIter iter(cx);
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1129,17 +1129,17 @@ 1234567890123456789012345678901234567890
     macro(JSOP_EXCEPTION, 118,"exception",  NULL,         1,  0,  1,  JOF_BYTE) \
     \
     /*
      * Embedded lineno to speedup 'pc->line' mapping.
      *   Category: Other
      *   Operands: uint32_t lineno
      *   Stack: =>
      */ \
-    macro(JSOP_LINENO,    119,"lineno",     NULL,         3,  0,  0,  JOF_UINT16) \
+    macro(JSOP_LINENO,    119,"lineno",     NULL,         5,  0,  0,  JOF_UINT32) \
     \
     /*
      * This no-op appears after the bytecode for EXPR in 'switch (EXPR) {...}'
      * if the switch cannot be optimized using JSOP_TABLESWITCH.
      * For a non-optimized switch statement like this:
      *
      *     switch (EXPR) {
      *       case V0:
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 269;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 276;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 390,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");