Bug 473096 - js1_5/Regress/regress-366601.js - Internal Error: script too large. r=brendan
authorJeff Walden <jwalden@mit.edu>
Fri, 10 Apr 2009 17:41:13 -0700
changeset 24907 4948d39190a927d3a819c9432273c97e7f1c950d
parent 24906 ff2552207e39e4bc3fe3110d79fce961c3834fc3
child 24908 dd259477ab3b9a58c76dee035ab51208cf028e07
push id1267
push userrsayre@mozilla.com
push dateSun, 19 Apr 2009 02:47:24 +0000
reviewersbrendan
bugs473096, 366601
milestone1.9.1b4pre
Bug 473096 - js1_5/Regress/regress-366601.js - Internal Error: script too large. r=brendan
js/src/jsemit.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/jstracer.cpp
js/src/jsxdrapi.h
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -6269,51 +6269,58 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
       }
 #endif
 
       case TOK_RB:
 #if JS_HAS_GENERATORS
       case TOK_ARRAYCOMP:
 #endif
         /*
-         * Emit code for [a, b, c] of the form:
-         *
-         *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
+         * Emit code for [a, b, c] that is equivalent to constructing a new
+         * array and in source order evaluating each element value and adding
+         * it to the array, without invoking latent setters.  We use the
+         * JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to
+         * avoid dup'ing and popping the array as each element is added, as
+         * JSOP_SETELEM/JSOP_SETPROP would do.
          *
-         * but use a stack slot for t and avoid dup'ing and popping it using
-         * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
-         *
-         * If no sharp variable is defined and the initialiser is not for an
-         * array comprehension, use JSOP_NEWARRAY.
+         * If no sharp variable is defined, the initializer is not for an array
+         * comprehension, the initializer is not overlarge, and the initializer
+         * is not in global code (whose stack growth cannot be precisely modeled
+         * due to the need to reserve space for global variables and regular
+         * expressions), use JSOP_NEWARRAY to minimize opcodes and to create the
+         * array using a fast, all-at-once process rather than a slow, element-
+         * by-element process.
          */
 #if JS_HAS_SHARP_VARS
         sharpnum = -1;
       do_emit_array:
 #endif
 
-#if JS_HAS_GENERATORS || JS_HAS_SHARP_VARS
-        op = JSOP_NEWARRAY;
-# if JS_HAS_GENERATORS
+        op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && (cg->flags & TCF_IN_FUNCTION))
+             ? JSOP_NEWARRAY
+             : JSOP_NEWINIT;
+
+#if JS_HAS_GENERATORS
         if (pn->pn_type == TOK_ARRAYCOMP)
             op = JSOP_NEWINIT;
-# endif
-# if JS_HAS_SHARP_VARS
+#endif
+#if JS_HAS_SHARP_VARS
         JS_ASSERT_IF(sharpnum >= 0, cg->flags & TCF_HAS_SHARPS);
         if (cg->flags & TCF_HAS_SHARPS)
             op = JSOP_NEWINIT;
-# endif
+#endif
+
         if (op == JSOP_NEWINIT) {
             if (js_Emit2(cx, cg, op, (jsbytecode) JSProto_Array) < 0)
                 return JS_FALSE;
 #if JS_HAS_SHARP_VARS
             if (sharpnum >= 0)
                 EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum);
 # endif
         }
-#endif
 
 #if JS_HAS_GENERATORS
         if (pn->pn_type == TOK_ARRAYCOMP) {
             uintN saveDepth;
 
             /*
              * Pass the new array's stack index to the TOK_ARRAYPUSH case via
              * cg->arrayCompDepth, then simply traverse the TOK_FOR node and
@@ -6330,75 +6337,69 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
                 return JS_FALSE;
             break;
         }
 #endif /* JS_HAS_GENERATORS */
 
         pn2 = pn->pn_head;
         for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
-#if JS_HAS_SHARP_VARS
             if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg))
                 return JS_FALSE;
-#endif
             if (pn2->pn_type == TOK_COMMA) {
                 if (js_Emit1(cx, cg, JSOP_HOLE) < 0)
                     return JS_FALSE;
             } else {
                 if (!js_EmitTree(cx, cg, pn2))
                     return JS_FALSE;
             }
-#if JS_HAS_SHARP_VARS
             if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0)
                 return JS_FALSE;
-#endif
         }
         JS_ASSERT(atomIndex == pn->pn_count);
 
         if (pn->pn_xflags & PNX_ENDCOMMA) {
             /* Emit a source note so we know to decompile an extra comma. */
             if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
                 return JS_FALSE;
         }
 
-#if JS_HAS_SHARP_VARS
         if (op == JSOP_NEWINIT) {
-            /* Emit an op for sharp array cleanup and decompilation. */
+            /*
+             * Emit an op to finish the array and, secondarily, to aid in sharp
+             * array cleanup (if JS_HAS_SHARP_VARS) and decompilation.
+             */
             if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
                 return JS_FALSE;
             break;
         }
-#endif
-        off = js_EmitN(cx, cg, JSOP_NEWARRAY, 3);
-        if (off < 0)
-            return JS_FALSE;
-        pc = CG_CODE(cg, off);
-        SET_UINT24(pc, atomIndex);
-        UpdateDepth(cx, cg, off);
+
+        JS_ASSERT(atomIndex < JS_BIT(16));
+        EMIT_UINT16_IMM_OP(JSOP_NEWARRAY, atomIndex);
         break;
 
       case TOK_RC:
 #if JS_HAS_SHARP_VARS
         sharpnum = -1;
       do_emit_object:
 #endif
 #if JS_HAS_DESTRUCTURING_SHORTHAND
         if (pn->pn_xflags & PNX_DESTRUCT) {
             js_ReportCompileErrorNumber(cx, CG_TS(cg), pn, JSREPORT_ERROR,
                                         JSMSG_BAD_OBJECT_INIT);
             return JS_FALSE;
         }
 #endif
         /*
-         * Emit code for {p:a, '%q':b, 2:c} of the form:
-         *
-         *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
-         *
-         * but use a stack slot for t and avoid dup'ing and popping it via
-         * the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes.
+         * Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
+         * a new object and in source order evaluating each property value and
+         * adding the property to the object, without invoking latent setters.
+         * We use the JSOP_NEWINIT and JSOP_INITELEM/JSOP_INITPROP bytecodes to
+         * ignore setters and to avoid dup'ing and popping the object as each
+         * property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
          */
         if (js_Emit2(cx, cg, JSOP_NEWINIT, (jsbytecode) JSProto_Object) < 0)
             return JS_FALSE;
 
 #if JS_HAS_SHARP_VARS
         if (sharpnum >= 0)
             EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) sharpnum);
 #endif
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -6249,18 +6249,18 @@ js_Interpret(JSContext *cx)
             DO_NEXT_OP(len);
 #endif /* JS_HAS_GETTER_SETTER */
 
           BEGIN_CASE(JSOP_HOLE)
             PUSH_OPND(JSVAL_HOLE);
           END_CASE(JSOP_HOLE)
 
           BEGIN_CASE(JSOP_NEWARRAY)
-            len = GET_UINT24(regs.pc);
-            JS_ASSERT(len <= regs.sp - StackBase(fp));
+            len = GET_UINT16(regs.pc);
+            cx->fp->assertValidStackDepth(len);
             obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
             if (!obj)
                 goto error;
             regs.sp -= len - 1;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_NEWARRAY)
 
           BEGIN_CASE(JSOP_NEWINIT)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -127,16 +127,24 @@ struct JSStackFrame {
     uint32          flags;          /* frame flags -- see below */
     JSStackFrame    *dormantNext;   /* next dormant frame chain */
     JSObject        *xmlNamespace;  /* null or default xml namespace in E4X */
     JSStackFrame    *displaySave;   /* previous value of display entry for
                                        script->staticLevel */
 #ifdef DEBUG
     jsrefcount      pcDisabledSave; /* for balanced property cache control */
 #endif
+
+#ifdef __cplusplus /* Aargh, LiveConnect, bug 442399. */
+    void assertValidStackDepth(uintN depth) {
+        extern jsval *StackBase(JSStackFrame *fp);
+        JS_ASSERT(0 <= regs->sp - StackBase(this));
+        JS_ASSERT(depth <= uintptr_t(regs->sp - StackBase(this)));
+    }
+#endif
 };
 
 #ifdef __cplusplus
 static JS_INLINE uintN
 FramePCOffset(JSStackFrame* fp)
 {
     return uintN((fp->imacpc ? fp->imacpc : fp->regs->pc) - fp->script->code);
 }
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -209,17 +209,17 @@ js_GetVariableStackUses(JSOp op, jsbytec
     switch (op) {
       case JSOP_POPN:
         return GET_UINT16(pc);
       case JSOP_LEAVEBLOCK:
         return GET_UINT16(pc);
       case JSOP_LEAVEBLOCKEXPR:
         return GET_UINT16(pc) + 1;
       case JSOP_NEWARRAY:
-        return GET_UINT24(pc);
+        return GET_UINT16(pc);
       default:
         /* stack: fun, this, [argc arguments] */
         JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
                   op == JSOP_EVAL || op == JSOP_SETCALL ||
                   op == JSOP_APPLY);
         return 2 + GET_ARGC(pc);
     }
 }
@@ -4346,17 +4346,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 todo = -2;
                 break;
 
               case JSOP_HOLE:
                 todo = SprintPut(&ss->sprinter, "", 0);
                 break;
 
               case JSOP_NEWARRAY:
-                argc = GET_UINT24(pc);
+                argc = GET_UINT16(pc);
                 LOCAL_ASSERT(ss->top >= (uintN) argc);
                 if (argc == 0) {
                     todo = SprintCString(&ss->sprinter, "[]");
                     break;
                 }
 
                 argv = (char **) JS_malloc(cx, size_t(argc) * sizeof *argv);
                 if (!argv)
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -549,19 +549,19 @@ OPDEF(JSOP_INT32,         220, "int32", 
  */
 OPDEF(JSOP_LENGTH,        221, "length",       NULL,  1,  1,  1, 18,  JOF_BYTE|JOF_PROP)
 
 /*
  * Construct a new dense array whose contents are the values provided on the
  * stack, consuming those values and replacing them with the newly-constructed
  * array.  The topmost value is the last value in the new array, and the
  * bottommost value is the first value in the array; the array length is a
- * 24-bit immediate operand to the instruction.
+ * 16-bit immediate operand to the instruction.
  */
-OPDEF(JSOP_NEWARRAY,      222, "newarray",     NULL,  4, -1,  1, 19,  JOF_UINT24)
+OPDEF(JSOP_NEWARRAY,      222, "newarray",     NULL,  3, -1,  1, 19,  JOF_UINT16)
 
 /*
  * Push a JSVAL_HOLE value onto the stack, representing an omitted property in
  * an array literal (e.g. property 0 in the array [, 1]).  This opcode is used
  * with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes.
  */
 OPDEF(JSOP_HOLE,          223, "hole",         NULL,  1,  0,  1,  0,  JOF_BYTE)
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -7174,17 +7174,19 @@ TraceRecorder::functionCall(bool constru
     }
 
     return callNative(fun, argc, constructing);
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_NEW()
 {
-    return functionCall(true, GET_ARGC(cx->fp->regs->pc));
+    uintN argc = GET_ARGC(cx->fp->regs->pc);
+    cx->fp->assertValidStackDepth(argc + 2);
+    return functionCall(true, argc);
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_DELNAME()
 {
     return false;
 }
 
@@ -7887,17 +7889,19 @@ TraceRecorder::interpretedFunctionCall(j
 
     atoms = fun->u.i.script->atomMap.vector;
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_CALL()
 {
-    return functionCall(false, GET_ARGC(cx->fp->regs->pc));
+    uintN argc = GET_ARGC(cx->fp->regs->pc);
+    cx->fp->assertValidStackDepth(argc + 2);
+    return functionCall(false, argc);
 }
 
 static jsbytecode* apply_imacro_table[] = {
     apply_imacros.apply0,
     apply_imacros.apply1,
     apply_imacros.apply2,
     apply_imacros.apply3,
     apply_imacros.apply4,
@@ -7920,18 +7924,19 @@ static jsbytecode* call_imacro_table[] =
 };
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_APPLY()
 {
     JSStackFrame* fp = cx->fp;
     jsbytecode *pc = fp->regs->pc;
     uintN argc = GET_ARGC(pc);
+    cx->fp->assertValidStackDepth(argc + 2);
+
     jsval* vp = fp->regs->sp - (argc + 2);
-    JS_ASSERT(vp >= StackBase(fp));
     jsuint length = 0;
     JSObject* aobj = NULL;
     LIns* aobj_ins = NULL;
 
     JS_ASSERT(!fp->imacpc);
 
     if (!VALUE_IS_FUNCTION(cx, vp[0]))
         return record_JSOP_CALL();
@@ -9913,17 +9918,19 @@ TraceRecorder::record_JSOP_LENGTH()
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_NEWARRAY()
 {
     LIns *proto_ins;
     if (!getClassPrototype(JSProto_Array, proto_ins))
         return false;
 
-    uint32 len = GET_UINT24(cx->fp->regs->pc);
+    uint32 len = GET_UINT16(cx->fp->regs->pc);
+    cx->fp->assertValidStackDepth(len);
+
     LIns* args[] = { lir->insImm(len), proto_ins, cx_ins };
     LIns* v_ins = lir->insCall(&js_NewUninitializedArray_ci, args);
     guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
 
     LIns* dslots_ins = NULL;
     for (uint32 i = 0; i < len; i++) {
         jsval& v = stackval(int(i) - int(len));
         LIns* elt_ins = get(&v);
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 45)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 46)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool