Bug 704369: Move function emit. (r=Waldo)
authorChris Leary <cdleary@mozilla.com>
Mon, 21 Nov 2011 17:29:56 -0800
changeset 82884 6bc8f2c740f769b8c775b474bf5f11677e314bd3
parent 82883 319c74e59fd4161a78b8a0f87bf4a7289e35f7d0
child 82885 c27aaef8236b4093b4c24117b853bbda35d36cea
push idunknown
push userunknown
push dateunknown
reviewersWaldo
bugs704369
milestone11.0a1
Bug 704369: Move function emit. (r=Waldo)
js/src/frontend/BytecodeEmitter.cpp
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5764,27 +5764,172 @@ static inline bool
 EmitFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top)
 {
     JS_ASSERT(pn->pn_left->isKind(PNK_FORIN) || pn->pn_left->isKind(PNK_FORHEAD));
     return pn->pn_left->isKind(PNK_FORIN)
            ? EmitForIn(cx, bce, pn, top)
            : EmitNormalFor(cx, bce, pn, top);
 }
 
+static bool
+EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
+{
+    JSFunction *fun;
+    uintN slot;
+
+#if JS_HAS_XML_SUPPORT
+    if (pn->isArity(PN_NULLARY)) {
+        if (Emit1(cx, bce, JSOP_GETFUNNS) < 0)
+            return false;
+        return true;
+    }
+#endif
+
+    fun = pn->pn_funbox->function();
+    JS_ASSERT(fun->isInterpreted());
+    if (fun->script()) {
+        /*
+         * This second pass is needed to emit JSOP_NOP with a source note
+         * for the already-emitted function definition prolog opcode. See
+         * comments in the PNK_STATEMENTLIST case.
+         */
+        JS_ASSERT(pn->isOp(JSOP_NOP));
+        JS_ASSERT(bce->inFunction());
+        if (!EmitFunctionDefNop(cx, bce, pn->pn_index))
+            return false;
+        return true;
+    }
+
+    JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
+                 fun->kind() == JSFUN_INTERPRETED);
+
+    /*
+     * Generate code for the function's body.  bce2 is not allocated on the
+     * stack because doing so significantly reduces the maximum depth of
+     * nested functions we can handle.  See bug 696284.
+     */
+    BytecodeEmitter *bce2 = cx->new_<BytecodeEmitter>(bce->parser, pn->pn_pos.begin.lineno);
+    if (!bce2) {
+        js_ReportOutOfMemory(cx);
+        return JS_FALSE;
+    }
+    if (!bce2->init(cx))
+        return JS_FALSE;
+
+    bce2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
+                 (bce->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
+    bce2->bindings.transfer(cx, &pn->pn_funbox->bindings);
+#if JS_HAS_SHARP_VARS
+    if (bce2->flags & TCF_HAS_SHARPS) {
+        bce2->sharpSlotBase = bce2->bindings.sharpSlotBase(cx);
+        if (bce2->sharpSlotBase < 0)
+            return JS_FALSE;
+    }
+#endif
+    bce2->setFunction(fun);
+    bce2->funbox = pn->pn_funbox;
+    bce2->parent = bce;
+    bce2->globalScope = bce->globalScope;
+
+    /*
+     * js::frontend::SetStaticLevel limited static nesting depth to fit in
+     * 16 bits and to reserve the all-ones value, thereby reserving the
+     * magic FREE_UPVAR_COOKIE value. Note the bce2->staticLevel assignment
+     * below.
+     */
+    JS_ASSERT(bce->staticLevel < JS_BITMASK(16) - 1);
+    bce2->staticLevel = bce->staticLevel + 1;
+
+    /* We measured the max scope depth when we parsed the function. */
+    if (!EmitFunctionScript(cx, bce2, pn->pn_body))
+        pn = NULL;
+
+    cx->delete_(bce2);
+    bce2 = NULL;
+    if (!pn)
+        return JS_FALSE;
+
+    /* Make the function object a literal in the outer script's pool. */
+    uintN index = bce->objectList.index(pn->pn_funbox);
+
+    /* Emit a bytecode pointing to the closure object in its immediate. */
+    JSOp op = pn->getOp();
+    if (op != JSOP_NOP) {
+        if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) &&
+            NewSrcNote(cx, bce, SRC_GENEXP) < 0)
+        {
+            return JS_FALSE;
+        }
+        EMIT_INDEX_OP(op, index);
+
+        /* Make blockChain determination quicker. */
+        if (EmitBlockChain(cx, bce) < 0)
+            return JS_FALSE;
+        return true;
+    }
+
+    /*
+     * For a script we emit the code as we parse. Thus the bytecode for
+     * top-level functions should go in the prolog to predefine their
+     * names in the variable object before the already-generated main code
+     * is executed. This extra work for top-level scripts is not necessary
+     * when we emit the code for a function. It is fully parsed prior to
+     * invocation of the emitter and calls to EmitTree for function
+     * definitions can be scheduled before generating the rest of code.
+     */
+    if (!bce->inFunction()) {
+        JS_ASSERT(!bce->topStmt);
+        if (!BindGlobal(cx, bce, pn, fun->atom))
+            return false;
+        if (pn->pn_cookie.isFree()) {
+            bce->switchToProlog();
+            op = fun->isFlatClosure() ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
+            EMIT_INDEX_OP(op, index);
+
+            /* Make blockChain determination quicker. */
+            if (EmitBlockChain(cx, bce) < 0)
+                return JS_FALSE;
+            bce->switchToMain();
+        }
+
+        /* Emit NOP for the decompiler. */
+        if (!EmitFunctionDefNop(cx, bce, index))
+            return JS_FALSE;
+    } else {
+        DebugOnly<BindingKind> kind = bce->bindings.lookup(cx, fun->atom, &slot);
+        JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
+        JS_ASSERT(index < JS_BIT(20));
+        pn->pn_index = index;
+        op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
+        if (pn->isClosed() &&
+            !bce->callsEval() &&
+            !bce->closedVars.append(pn->pn_cookie.slot())) {
+            return JS_FALSE;
+        }
+        if (!EmitSlotIndexOp(cx, op, slot, index, bce))
+            return JS_FALSE;
+
+        /* Make blockChain determination quicker. */
+        if (EmitBlockChain(cx, bce) < 0)
+            return JS_FALSE;
+    }
+
+    return true;
+}
+
 JSBool
 frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     JSBool useful, wantval;
     StmtInfo stmtInfo;
     StmtInfo *stmt;
     ptrdiff_t top, off, tmp, beq, jmp;
     ParseNode *pn2, *pn3;
     JSAtom *atom;
     jsatomid atomIndex;
-    uintN index;
     ptrdiff_t noteIndex, noteIndex2;
     SrcNoteType noteType;
     jsbytecode *pc;
     JSOp op;
     uint32 argc;
     EmitLevelManager elm(bce);
     jsint sharpnum = -1;
 
@@ -5793,158 +5938,18 @@ frontend::EmitTree(JSContext *cx, Byteco
     JSBool ok = true;
     pn->pn_offset = top = bce->offset();
 
     /* Emit notes to tell the current bytecode's source line number. */
     UPDATE_LINE_NUMBER_NOTES(cx, bce, pn->pn_pos.begin.lineno);
 
     switch (pn->getKind()) {
       case PNK_FUNCTION:
-      {
-        JSFunction *fun;
-        uintN slot;
-
-#if JS_HAS_XML_SUPPORT
-        if (pn->isArity(PN_NULLARY)) {
-            if (Emit1(cx, bce, JSOP_GETFUNNS) < 0)
-                return JS_FALSE;
-            break;
-        }
-#endif
-
-        fun = pn->pn_funbox->function();
-        JS_ASSERT(fun->isInterpreted());
-        if (fun->script()) {
-            /*
-             * This second pass is needed to emit JSOP_NOP with a source note
-             * for the already-emitted function definition prolog opcode. See
-             * comments in the PNK_STATEMENTLIST case.
-             */
-            JS_ASSERT(pn->isOp(JSOP_NOP));
-            JS_ASSERT(bce->inFunction());
-            if (!EmitFunctionDefNop(cx, bce, pn->pn_index))
-                return JS_FALSE;
-            break;
-        }
-
-        JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
-                     fun->kind() == JSFUN_INTERPRETED);
-
-        /*
-         * Generate code for the function's body.  bce2 is not allocated on the
-         * stack because doing so significantly reduces the maximum depth of
-         * nested functions we can handle.  See bug 696284.
-         */
-        BytecodeEmitter *bce2 = cx->new_<BytecodeEmitter>(bce->parser, pn->pn_pos.begin.lineno);
-        if (!bce2) {
-            js_ReportOutOfMemory(cx);
-            return JS_FALSE;
-        }
-        if (!bce2->init(cx))
-            return JS_FALSE;
-
-        bce2->flags = pn->pn_funbox->tcflags | TCF_COMPILING | TCF_IN_FUNCTION |
-                     (bce->flags & TCF_FUN_MIGHT_ALIAS_LOCALS);
-        bce2->bindings.transfer(cx, &pn->pn_funbox->bindings);
-#if JS_HAS_SHARP_VARS
-        if (bce2->flags & TCF_HAS_SHARPS) {
-            bce2->sharpSlotBase = bce2->bindings.sharpSlotBase(cx);
-            if (bce2->sharpSlotBase < 0)
-                return JS_FALSE;
-        }
-#endif
-        bce2->setFunction(fun);
-        bce2->funbox = pn->pn_funbox;
-        bce2->parent = bce;
-        bce2->globalScope = bce->globalScope;
-
-        /*
-         * js::frontend::SetStaticLevel limited static nesting depth to fit in
-         * 16 bits and to reserve the all-ones value, thereby reserving the
-         * magic FREE_UPVAR_COOKIE value. Note the bce2->staticLevel assignment
-         * below.
-         */
-        JS_ASSERT(bce->staticLevel < JS_BITMASK(16) - 1);
-        bce2->staticLevel = bce->staticLevel + 1;
-
-        /* We measured the max scope depth when we parsed the function. */
-        if (!EmitFunctionScript(cx, bce2, pn->pn_body))
-            pn = NULL;
-
-        cx->delete_(bce2);
-        bce2 = NULL;
-        if (!pn)
-            return JS_FALSE;
-
-        /* Make the function object a literal in the outer script's pool. */
-        index = bce->objectList.index(pn->pn_funbox);
-
-        /* Emit a bytecode pointing to the closure object in its immediate. */
-        op = pn->getOp();
-        if (op != JSOP_NOP) {
-            if ((pn->pn_funbox->tcflags & TCF_GENEXP_LAMBDA) &&
-                NewSrcNote(cx, bce, SRC_GENEXP) < 0)
-            {
-                return JS_FALSE;
-            }
-            EMIT_INDEX_OP(op, index);
-
-            /* Make blockChain determination quicker. */
-            if (EmitBlockChain(cx, bce) < 0)
-                return JS_FALSE;
-            break;
-        }
-
-        /*
-         * For a script we emit the code as we parse. Thus the bytecode for
-         * top-level functions should go in the prolog to predefine their
-         * names in the variable object before the already-generated main code
-         * is executed. This extra work for top-level scripts is not necessary
-         * when we emit the code for a function. It is fully parsed prior to
-         * invocation of the emitter and calls to EmitTree for function
-         * definitions can be scheduled before generating the rest of code.
-         */
-        if (!bce->inFunction()) {
-            JS_ASSERT(!bce->topStmt);
-            if (!BindGlobal(cx, bce, pn, fun->atom))
-                return false;
-            if (pn->pn_cookie.isFree()) {
-                bce->switchToProlog();
-                op = fun->isFlatClosure() ? JSOP_DEFFUN_FC : JSOP_DEFFUN;
-                EMIT_INDEX_OP(op, index);
-
-                /* Make blockChain determination quicker. */
-                if (EmitBlockChain(cx, bce) < 0)
-                    return JS_FALSE;
-                bce->switchToMain();
-            }
-
-            /* Emit NOP for the decompiler. */
-            if (!EmitFunctionDefNop(cx, bce, index))
-                return JS_FALSE;
-        } else {
-            DebugOnly<BindingKind> kind = bce->bindings.lookup(cx, fun->atom, &slot);
-            JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
-            JS_ASSERT(index < JS_BIT(20));
-            pn->pn_index = index;
-            op = fun->isFlatClosure() ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
-            if (pn->isClosed() &&
-                !bce->callsEval() &&
-                !bce->closedVars.append(pn->pn_cookie.slot())) {
-                return JS_FALSE;
-            }
-            if (!EmitSlotIndexOp(cx, op, slot, index, bce))
-                return JS_FALSE;
-
-            /* Make blockChain determination quicker. */
-            if (EmitBlockChain(cx, bce) < 0)
-                return JS_FALSE;
-        }
+        ok = EmitFunc(cx, bce, pn);
         break;
-      }
 
       case PNK_ARGSBODY:
       {
         ParseNode *pnlast = pn->last();
         for (ParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
             if (!pn2->isDefn())
                 continue;
             if (!BindNameToSlot(cx, bce, pn2))