Bug 488015 - Crash [@ js_GetUpvar ] (also bogus JS errors, also probably Crash [@js_Interpret]) (future r=mrbkap, see bug).
authorBrendan Eich <brendan@mozilla.org>
Wed, 15 Apr 2009 01:57:13 -0700
changeset 24926 ec54f7218d9aa5c5deafbae9d96cc0decd36cc55
parent 24925 b481eb1839adc5a8ed4a222a7d4161f0aa09355c
child 24927 1ce06e13aa9786ec02e02307c8a95230b05775bb
push id1267
push userrsayre@mozilla.com
push dateSun, 19 Apr 2009 02:47:24 +0000
reviewersmrbkap, see
bugs488015
milestone1.9.1b4pre
Bug 488015 - Crash [@ js_GetUpvar ] (also bogus JS errors, also probably Crash [@js_Interpret]) (future r=mrbkap, see bug).
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsparse.cpp
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2095,17 +2095,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         }
 
         return MakeUpvarForEval(pn, cg);
     }
 
     uintN skip = cg->staticLevel - level;
     if (skip != 0) {
         JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
-        JS_ASSERT(cg->upvars.lookup(atom));
+        JS_ASSERT(cg->lexdeps.lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
 
         /*
          * If op is a mutating opcode, this upvar's static level is too big to
          * index into the display, or the function is heavyweight, we fall back
          * on JSOP_*NAME*.
          */
         if (op != JSOP_NAME)
@@ -2149,17 +2149,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             ale = cg->upvarList.add(cg->compiler, atom);
             if (!ale)
                 return JS_FALSE;
             index = ALE_INDEX(ale);
             JS_ASSERT(index == cg->upvarList.count - 1);
 
             uint32 *vector = cg->upvarMap.vector;
             if (!vector) {
-                uint32 length = cg->upvars.count;
+                uint32 length = cg->lexdeps.count;
 
                 vector = (uint32 *) calloc(length, sizeof *vector);
                 if (!vector) {
                     JS_ReportOutOfMemory(cx);
                     return JS_FALSE;
                 }
                 cg->upvarMap.vector = vector;
                 cg->upvarMap.length = length;
@@ -4332,19 +4332,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         break;
       }
 
       case TOK_ARGSBODY:
         ok = js_EmitTree(cx, cg, pn->last());
         break;
 
       case TOK_UPVARS:
-        JS_ASSERT(cg->upvars.count == 0);
+        JS_ASSERT(cg->lexdeps.count == 0);
         JS_ASSERT(pn->pn_names.count != 0);
-        cg->upvars = pn->pn_names;
+        cg->lexdeps = pn->pn_names;
         ok = js_EmitTree(cx, cg, pn->pn_tree);
         break;
 
       case TOK_IF:
         /* Initialize so we can detect else-if chains and avoid recursion. */
         stmtInfo.type = STMT_IF;
         beq = jmp = -1;
         noteIndex = -1;
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -180,17 +180,16 @@ struct JSTreeContext {              /* t
 
     union {
         JSFunction  *fun;           /* function to store argument and variable
                                        names when flags & TCF_IN_FUNCTION */
         JSObject    *scopeChain;    /* scope chain object for the script */
     };
 
     JSAtomList      lexdeps;        /* unresolved lexical name dependencies */
-    JSAtomList      upvars;         /* resolved lexical name dependencies */
     JSTreeContext   *parent;        /* enclosing function or global context */
     uintN           staticLevel;    /* static compilation unit nesting level */
 
     JSFunctionBox   *funbox;        /* null or box for function we're compiling
                                        if (flags & TCF_IN_FUNCTION) and not in
                                        JSCompiler::compileFunctionBody */
     JSFunctionBox   *functionList;
 
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1214,18 +1214,16 @@ Define(JSParseNode *pn, JSAtom *atom, JS
 
     JSHashEntry **hep;
     JSAtomListElement *ale = NULL;
     JSAtomList *list = NULL;
 
     if (let)
         ale = (list = &tc->decls)->rawLookup(atom, hep);
     if (!ale)
-        ale = (list = &tc->upvars)->rawLookup(atom, hep);
-    if (!ale)
         ale = (list = &tc->lexdeps)->rawLookup(atom, hep);
 
     if (ale) {
         JSDefinition *dn = ALE_DEFN(ale);
         if (dn != pn) {
             JSParseNode **pnup = &dn->dn_uses;
             JSParseNode *pnu;
             uintN start = let ? pn->pn_blockid : tc->bodyid;
@@ -1237,21 +1235,18 @@ Define(JSParseNode *pn, JSAtom *atom, JS
                 pnup = &pnu->pn_link;
             }
 
             if (pnu != dn->dn_uses) {
                 *pnup = pn->dn_uses;
                 pn->dn_uses = dn->dn_uses;
                 dn->dn_uses = pnu;
 
-                if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls) {
+                if ((!pnu || pnu->pn_blockid < tc->bodyid) && list != &tc->decls)
                     list->rawRemove(tc->compiler, ale, hep);
-                    ((list == &tc->upvars) ? &tc->lexdeps : &tc->upvars)
-                        ->remove(tc->compiler, atom);
-                }
             }
         }
     }
 
     ale = tc->decls.add(tc->compiler, atom, let ? JSAtomList::SHADOW : JSAtomList::UNIQUE);
     if (!ale)
         return false;
     ALE_SET_DEFN(ale, pn);
@@ -2180,23 +2175,25 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
     fn->pn_dflags |= PND_INITIALIZED;
     JS_ASSERT_IF(tc->atTopLevel() && lambda == 0 && funAtom,
                  fn->pn_dflags & PND_TOPLEVEL);
     if (!tc->topStmt || tc->topStmt->type == STMT_BLOCK)
         fn->pn_dflags |= PND_BLOCKCHILD;
 
     /*
      * Propagate unresolved lexical names up to tc->lexdeps, and save a copy
-     * of funtc->upvars in a TOK_UPVARS node wrapping the function's formal
-     * params and body. We do this only if there are lexical dependencies or
-     * upvars, to avoid penalizing functions that use only their arguments.
+     * of funtc->lexdeps in a TOK_UPVARS node wrapping the function's formal
+     * params and body. We do this only if there are lexical dependencies not
+     * satisfied by the function's declarations, to avoid penalizing functions
+     * that use only their arguments and other local bindings.
      */
     if (funtc->lexdeps.count != 0) {
         JSAtomListIterator iter(&funtc->lexdeps);
         JSAtomListElement *ale;
+        int foundCallee = 0;
 
         while ((ale = iter()) != NULL) {
             JSAtom *atom = ALE_ATOM(ale);
             JSDefinition *dn = ALE_DEFN(ale);
             JS_ASSERT(dn->isPlaceholder());
 
             if (atom == funAtom && lambda != 0) {
                 dn->pn_op = JSOP_CALLEE;
@@ -2214,16 +2211,17 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
                  * flag is interpreted in its broader sense, not only to mean
                  * "this function might leak arguments.callee"), we can perhaps
                  * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
                  * use that more precisely, both here and for unnamed function
                  * expressions.
                  */
                 if (dn->isFunArg())
                     fn->pn_funbox->tcflags |= TCF_FUN_USES_ARGUMENTS;
+                foundCallee = 1;
                 continue;
             }
 
             if (!(fn->pn_funbox->tcflags & TCF_FUN_SETS_OUTER_NAME) &&
                 dn->isAssigned()) {
                 /*
                  * Make sure we do not fail to set TCF_FUN_SETS_OUTER_NAME if
                  * any use of dn in funtc assigns. See NoteLValue for the easy
@@ -2233,17 +2231,19 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
                 for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
                     if (pnu->isAssigned() && pnu->pn_blockid >= funtc->bodyid) {
                         fn->pn_funbox->tcflags |= TCF_FUN_SETS_OUTER_NAME;
                         break;
                     }
                 }
             }
 
-            JSAtomListElement *outer_ale = tc->lexdeps.lookup(atom);
+            JSAtomListElement *outer_ale = tc->decls.lookup(atom);
+            if (!outer_ale)
+                outer_ale = tc->lexdeps.lookup(atom);
             if (outer_ale) {
                 /*
                  * Insert dn's uses list at the front of outer_dn's list.
                  *
                  * Without loss of generality or correctness, we allow a dn to
                  * be in inner and outer lexdeps, since the purpose of lexdeps
                  * is one-pass coordination of name use and definition across
                  * functions, and if different dn's are used we'll merge lists
@@ -2267,46 +2267,48 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
                     /*
                      * Make dn be a use that redirects to outer_dn, because we
                      * can't replace dn with outer_dn in all the pn_namesets in
                      * the AST where it may be. Instead we make it forward to
                      * outer_dn. See JSDefinition::resolve.
                      */
                     *pnup = outer_dn->dn_uses;
                     outer_dn->dn_uses = dn;
-                    outer_dn->pn_dflags |= dn->pn_dflags;
+                    outer_dn->pn_dflags |= (dn->pn_dflags & ~PND_PLACEHOLDER);
                     dn->pn_defn = false;
                     dn->pn_used = true;
                     dn->pn_lexdef = outer_dn;
                 }
             } else {
                 /* Add an outer lexical dependency for ale's definition. */
                 outer_ale = tc->lexdeps.add(tc->compiler, atom);
                 if (!outer_ale)
                     return false;
                 ALE_SET_DEFN(outer_ale, ALE_DEFN(ale));
             }
         }
 
+        if (funtc->lexdeps.count - foundCallee != 0) {
+            JSParseNode *body = fn->pn_body;
+
+            fn->pn_body = NewParseNode(PN_NAMESET, tc);
+            if (!fn->pn_body)
+                return false;
+
+            fn->pn_body->pn_type = TOK_UPVARS;
+            fn->pn_body->pn_pos = body->pn_pos;
+            if (foundCallee)
+                funtc->lexdeps.remove(tc->compiler, funAtom);
+            fn->pn_body->pn_names = funtc->lexdeps;
+            fn->pn_body->pn_tree = body;
+        }
+
         funtc->lexdeps.clear();
     }
 
-    if (funtc->upvars.count != 0) {
-        JSParseNode *body = fn->pn_body;
-
-        fn->pn_body = NewParseNode(PN_NAMESET, tc);
-        if (!fn->pn_body)
-            return false;
-        fn->pn_body->pn_type = TOK_UPVARS;
-        fn->pn_body->pn_pos = body->pn_pos;
-        fn->pn_body->pn_names = funtc->upvars;
-        fn->pn_body->pn_tree = body;
-        funtc->upvars.clear();
-    }
-
     return true;
 }
 
 static JSParseNode *
 FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
             uintN lambda)
 {
     JSOp op;
@@ -2406,17 +2408,16 @@ FunctionDef(JSContext *cx, JSTokenStream
                 JS_ASSERT(fn->pn_defn);
                 fn->pn_type = TOK_FUNCTION;
                 fn->pn_arity = PN_FUNC;
                 fn->pn_pos.begin = pn->pn_pos.begin;
                 fn->pn_body = NULL;
                 fn->pn_cookie = FREE_UPVAR_COOKIE;
 
                 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
-                tc->upvars.remove(tc->compiler, funAtom);
                 RecycleTree(pn, tc);
                 pn = fn;
             }
 
             if (!Define(pn, funAtom, tc))
                 return NULL;
         }
 
@@ -3099,17 +3100,16 @@ BindVarOrConst(JSContext *cx, BindData *
          */
         if (!pn->pn_defn) {
             JSHashEntry **hep;
 
             ale = tc->lexdeps.rawLookup(atom, hep);
             if (ale) {
                 pn = ALE_DEFN(ale);
                 tc->lexdeps.rawRemove(tc->compiler, ale, hep);
-                tc->upvars.remove(tc->compiler, atom);
             } else {
                 JSParseNode *pn2 = NewNameNode(cx, TS(tc->compiler), atom, tc);
                 if (!pn2)
                     return JS_FALSE;
 
                 /* The token stream may be past the location for pn. */
                 pn2->pn_type = TOK_NAME;
                 pn2->pn_pos = pn->pn_pos;
@@ -4046,21 +4046,16 @@ NewBindingNode(JSTokenStream *ts, JSAtom
                      pn->pn_blockid != tc->bodyid);
 
         if (pn->isPlaceholder() && pn->pn_blockid >= (let ? tc->blockid() : tc->bodyid)) {
             JS_ASSERT(pn->isForward());
             if (let)
                 pn->pn_blockid = tc->blockid();
 
             tc->lexdeps.remove(tc->compiler, atom);
-
-            JSHashEntry **hep;
-            ale = tc->upvars.rawLookup(atom, hep);
-            if (ale && ALE_DEFN(ale) == pn)
-                tc->upvars.rawRemove(tc->compiler, ale, hep);
             return pn;
         }
     }
 
     /* Make a new node for this declarator name (or destructuring pattern). */
     pn = NewNameNode(tc->compiler->context, ts, atom, tc);
     if (!pn)
         return NULL;
@@ -4113,29 +4108,26 @@ RebindLets(JSParseNode *pn, JSTreeContex
                     while ((ale = ALE_NEXT(ale)) != NULL) {
                         if (ALE_ATOM(ale) == pn->pn_atom) {
                             LinkUseToDef(pn, ALE_DEFN(ale), tc);
                             return true;
                         }
                     }
                 }
 
-                ale = tc->upvars.lookup(pn->pn_atom);
+                ale = tc->lexdeps.lookup(pn->pn_atom);
                 if (!ale) {
-                    ale = tc->lexdeps.lookup(pn->pn_atom);
-                    if (!ale) {
-                        ale = MakePlaceholder(pn, tc);
-                        if (!ale)
-                            return NULL;
-
-                        JSDefinition *dn = ALE_DEFN(ale);
-                        dn->pn_type = TOK_NAME;
-                        dn->pn_op = JSOP_NOP;
-                        dn->pn_dflags |= pn->pn_dflags & PND_FUNARG;
-                    }
+                    ale = MakePlaceholder(pn, tc);
+                    if (!ale)
+                        return NULL;
+
+                    JSDefinition *dn = ALE_DEFN(ale);
+                    dn->pn_type = TOK_NAME;
+                    dn->pn_op = JSOP_NOP;
+                    dn->pn_dflags |= pn->pn_dflags & PND_FUNARG;
                 }
                 LinkUseToDef(pn, ALE_DEFN(ale), tc);
             }
         }
         break;
 
       case PN_NAMESET:
         RebindLets(pn->pn_tree, tc);
@@ -6127,29 +6119,44 @@ CompExprTransplanter::transplant(JSParse
 #ifdef DEBUG
             JSStmtInfo *stmt = js_LexicalLookup(tc, atom, NULL);
             JS_ASSERT(!stmt || stmt != tc->topStmt);
 #endif
             if (genexp && PN_OP(dn) != JSOP_CALLEE) {
                 JS_ASSERT(!tc->decls.lookup(atom));
 
                 if (dn->pn_pos < root->pn_pos || dn->isPlaceholder()) {
-                    JSAtomListElement *ale = tc->upvars.add(tc->compiler, atom);
+                    JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, dn->pn_atom);
                     if (!ale)
-                        return false;
-                    ALE_SET_DEFN(ale, dn);
-
-                    if (dn->pn_pos >= root->pn_pos)
-                        tc->parent->upvars.remove(tc->compiler, atom);
-                }
-
-                if (dn->isPlaceholder()) {
-                    JSAtomListElement *ale = tc->lexdeps.add(tc->compiler, atom);
-                    if (!ale)
-                        return false;
+                        return NULL;
+
+                    if (!dn->isPlaceholder()) {
+                        JSDefinition *dn2 = (JSDefinition *)
+                            NewNameNode(tc->compiler->context, TS(tc->compiler), dn->pn_atom, tc);
+                        if (!dn2)
+                            return NULL;
+
+                        dn2->pn_type = dn->pn_type;
+                        dn2->pn_pos = dn->pn_pos;
+                        dn2->pn_defn = true;
+                        dn2->pn_dflags |= PND_FORWARD | PND_PLACEHOLDER;
+
+                        JSParseNode **pnup = &dn->dn_uses;
+                        JSParseNode *pnu;
+                        while ((pnu = *pnup) != NULL && pnu->pn_pos >= root->pn_pos) {
+                            pnu->pn_lexdef = dn2;
+                            pnup = &pnu->pn_link;
+                        }
+                        dn2->dn_uses = dn->dn_uses;
+                        dn->dn_uses = *pnup;
+                        *pnup = NULL;
+
+                        dn = dn2;
+                    }
+
                     ALE_SET_DEFN(ale, dn);
 
                     if (dn->pn_pos >= root->pn_pos)
                         tc->parent->lexdeps.remove(tc->compiler, atom);
                 }
             }
         }
 
@@ -7421,19 +7428,21 @@ JSCompiler::parseXMLText(JSObject *chain
  * bindings push on the front of the tc->decls JSAtomList (either the singular
  * list or on a hash chain -- see JSAtomList::AddHow) in order to shadow outer
  * scope bindings of the same name.
  *
  * This simplifies binding lookup code at the price of a linear search here,
  * but only if code uses let (var predominates), and even then this function's
  * loop iterates more than once only in crazy cases.
  */
-static bool
-BlockIdIsScope(uintN blockid, JSTreeContext *tc)
-{
+static inline bool
+BlockIdInScope(uintN blockid, JSTreeContext *tc)
+{
+    if (blockid > tc->blockid())
+        return false;
     for (JSStmtInfo *stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
         if (stmt->blockid == blockid)
             return true;
     }
     return false;
 }
 #endif
 
@@ -7878,116 +7887,79 @@ PrimaryExpr(JSContext *cx, JSTokenStream
              * Bind early to JSOP_ARGUMENTS to relieve later code from having
              * to do this work (new rule for the emitter to count on).
              */
             if (!afterDot && !(ts->flags & TSF_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
                 pn->pn_op = JSOP_ARGUMENTS;
                 pn->pn_dflags |= PND_BOUND;
             }
         } else if (!afterDot && !(ts->flags & TSF_DESTRUCTURING)) {
-            JSAtomListElement *ale = NULL;
-            JSTreeContext *tcx = tc;
-            JSDefinition *dn;
-
-            do {
-                JSStmtInfo *stmt = js_LexicalLookup(tcx, pn->pn_atom, NULL);
-
-                if (stmt && stmt->type == STMT_WITH)
-                    goto losing_with;
-                ale = tcx->decls.lookup(pn->pn_atom);
+            JSStmtInfo *stmt = js_LexicalLookup(tc, pn->pn_atom, NULL);
+            if (!stmt || stmt->type != STMT_WITH) {
+                JSDefinition *dn;
+
+                JSAtomListElement *ale = tc->decls.lookup(pn->pn_atom);
                 if (ale) {
                     dn = ALE_DEFN(ale);
 #if JS_HAS_BLOCK_SCOPE
-                    if (!dn->isLet())
-                        break;
-                    if (dn->pn_blockid <= tc->blockid() && BlockIdIsScope(dn->pn_blockid, tcx))
-                        break;
-                    ale = NULL;
-#else
-                    break;
+                    if (dn->isLet() && !BlockIdInScope(dn->pn_blockid, tc))
+                        ale = NULL;
 #endif
                 }
 
-                /* If this id names the current lambda's name, we are done. */
-                if ((tcx->flags & TCF_IN_FUNCTION) &&
-                    (tcx->fun->flags & JSFUN_LAMBDA) &&
-                    tcx->fun->atom == pn->pn_atom) {
-                    break;
-                }
-            } while ((tcx = tcx->parent) != NULL);
-
-            if (!ale) {
-                ale = tc->lexdeps.lookup(pn->pn_atom);
-                if (!ale) {
-                    /*
-                     * No definition before this use in any lexical scope. Add
-                     * a mapping in tc->lexdeps from pn->pn_atom to a new node
-                     * for the forward-referenced definition. This placeholder
-                     * definition node will be adopted when we parse the real
-                     * defining declaration form, or left as a free variable
-                     * definition if we never see the real definition.
-                     */
-                    ale = MakePlaceholder(pn, tc);
-                    if (!ale)
-                        return NULL;
+                if (ale) {
                     dn = ALE_DEFN(ale);
-
-                    /*
-                     * In case this is a forward reference to a function, we
-                     * pessimistically set PND_FUNARG if the next token is not
-                     * a left parenthesis. If the eventual definition parsed
-                     * into dn is not a function, this flag won't hurt, and if
-                     * it is a function, the flag is necessary for safe display
-                     * optimization of the closure's static link.
-                     */
-                    JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
-                    JS_ASSERT(dn->pn_op == JSOP_NOP);
-                    if (js_PeekToken(cx, ts) != TOK_LP)
-                        dn->pn_dflags |= PND_FUNARG;
+                } else {
+                    ale = tc->lexdeps.lookup(pn->pn_atom);
+                    if (ale) {
+                        dn = ALE_DEFN(ale);
+                    } else {
+                        /*
+                         * No definition before this use in any lexical scope.
+                         * Add a mapping in tc->lexdeps from pn->pn_atom to a
+                         * new node for the forward-referenced definition. This
+                         * placeholder definition node will be adopted when we
+                         * parse the real defining declaration form, or left as
+                         * a free variable definition if we never see the real
+                         * definition.
+                         */
+                        ale = MakePlaceholder(pn, tc);
+                        if (!ale)
+                            return NULL;
+                        dn = ALE_DEFN(ale);
+
+                        /*
+                         * In case this is a forward reference to a function,
+                         * we pessimistically set PND_FUNARG if the next token
+                         * is not a left parenthesis.
+                         *
+                         * If the definition eventually parsed into dn is not a
+                         * function, this flag won't hurt, and if we do parse a
+                         * function with pn's name, then the PND_FUNARG flag is
+                         * necessary for safe cx->display-based optimization of
+                         * the closure's static link.
+                         */
+                        JS_ASSERT(PN_TYPE(dn) == TOK_NAME);
+                        JS_ASSERT(dn->pn_op == JSOP_NOP);
+                        if (js_PeekToken(cx, ts) != TOK_LP)
+                            dn->pn_dflags |= PND_FUNARG;
+                    }
                 }
-            }
-
-            dn = ALE_DEFN(ale);
-            JS_ASSERT(dn->pn_defn);
-            LinkUseToDef(pn, dn, tc);
-
-            /*
-             * For an upvar reference, map pn->pn_atom to dn in tc->upvars. The
-             * subtleties here include:
-             *
-             * (a) tcx could be null, meaning we add an upvar speculatively for
-             * what looks like a free variable reference (it will be removed if
-             * a backward definition appears later; see NewBindingNode/Define).
-             *
-             * (b) If pn names the named function expression whose body we are
-             * parsing, there's no way an upvar above tcx's static level could
-             * be referenced here. However, we add to upvars anyway, to treat
-             * the function's name as an upvar in case it is used in a nested
-             * function.
-             *
-             * (a) is is an optimization to handle forward upvar refs. Without
-             * it, if we add only a lexdep, then inner functions making forward
-             * refs to upvars will lose track of those upvars as their lexdeps
-             * entries are propagated upward to their parent functions.
-             */
-            if (tcx != tc) {
-                ale = tc->upvars.add(tc->compiler, pn->pn_atom);
-                if (!ale)
-                    return NULL;
-                ALE_SET_DEFN(ale, dn);
-            }
-
-            /* Here we handle the backward function reference case. */
-            if (js_PeekToken(cx, ts) != TOK_LP)
-                dn->pn_dflags |= PND_FUNARG;
-
-            pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
-        }
-
-      losing_with:
+
+                JS_ASSERT(dn->pn_defn);
+                LinkUseToDef(pn, dn, tc);
+
+                /* Here we handle the backward function reference case. */
+                if (js_PeekToken(cx, ts) != TOK_LP)
+                    dn->pn_dflags |= PND_FUNARG;
+
+                pn->pn_dflags |= (dn->pn_dflags & PND_FUNARG);
+            }
+        }
+
 #if JS_HAS_XML_SUPPORT
         if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
             if (afterDot) {
                 JSString *str;
 
                 /*
                  * Here PrimaryExpr is called after . or .. followed by a name
                  * followed by ::. This is the only case where a keyword after