Bug 737570 - Fix handling of overwritten arguments via non-toplevel function statement named 'arguments' (r=waldo)
authorLuke Wagner <luke@mozilla.com>
Mon, 19 Mar 2012 19:22:59 -0700
changeset 93721 31d88c5a7b187a25e495ab1ae8063bd1eef5fcb8
parent 93720 c5d6aa6be62caa20d7d878d2add10bbb26d8b023
child 93722 a679be907f55c4016cfdc6e0b8a654b4de19d8c9
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswaldo
bugs737570
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 737570 - Fix handling of overwritten arguments via non-toplevel function statement named 'arguments' (r=waldo)
js/src/frontend/BytecodeEmitter.h
js/src/frontend/Parser.cpp
js/src/jit-test/tests/basic/testFunctionStatementNamedArguments.js
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -438,16 +438,20 @@ struct TreeContext {                /* t
         JS_ASSERT(inFunction());
         JS_ASSERT_IF(inStrictMode(),
                      !(flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_LOCAL_ARGUMENTS)));
         return !inStrictMode() &&
                (callsEval() ||
                 flags & (TCF_FUN_PARAM_ARGUMENTS | TCF_FUN_LOCAL_ARGUMENTS));
     }
 
+    void noteLocalOverwritesArguments() {
+        flags |= TCF_FUN_LOCAL_ARGUMENTS;
+    }
+
     void noteArgumentsNameUse(ParseNode *node) {
         JS_ASSERT(inFunction());
         JS_ASSERT(node->isKind(PNK_NAME));
         JS_ASSERT(node->pn_atom == parser->context->runtime->atomState.argumentsAtom);
         countArgumentsUse(node);
         flags |= TCF_FUN_USES_ARGUMENTS;
         if (funbox)
             funbox->node->pn_dflags |= PND_FUNARG;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1633,41 +1633,36 @@ Parser::functionDef(PropertyName *funNam
      * If we collected flags that indicate nested heavyweight functions, or
      * this function contains heavyweight-making statements (with statement,
      * visible eval call, or assignment to 'arguments'), flag the function as
      * heavyweight (requiring a call object per invocation).
      */
     if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
         fun->flags |= JSFUN_HEAVYWEIGHT;
         outertc->flags |= TCF_FUN_HEAVYWEIGHT;
-    } else {
-        /*
-         * If this function is not at body level of a program or function (i.e.
-         * it is a function statement that is not a direct child of a program
-         * or function), then our enclosing function, if any, must be
-         * heavyweight.
-         */
-        if (!bodyLevel && kind == Statement)
-            outertc->flags |= TCF_FUN_HEAVYWEIGHT;
     }
 
     JSOp op = JSOP_NOP;
     if (kind == Expression) {
         op = JSOP_LAMBDA;
     } else {
         if (!bodyLevel) {
             /*
              * Extension: in non-strict mode code, a function statement not at
              * the top level of a function body or whole program, e.g., in a
              * compound statement such as the "then" part of an "if" statement,
              * binds a closure only if control reaches that sub-statement.
              */
             JS_ASSERT(!outertc->inStrictMode());
             op = JSOP_DEFFUN;
             outertc->noteMightAliasLocals();
+            outertc->noteHasExtensibleScope();
+            outertc->flags |= TCF_FUN_HEAVYWEIGHT;
+            if (fun->atom == context->runtime->atomState.argumentsAtom)
+                outertc->noteLocalOverwritesArguments();
         }
     }
 
     funbox->kids = funtc.functionList;
 
     pn->pn_funbox = funbox;
     pn->setOp(op);
     if (pn->pn_body) {
@@ -1845,19 +1840,22 @@ Parser::statements()
              *
              * TCF_HAS_FUNCTION_STMT is for the TOK_LC case in Statement. It
              * is relevant only for function definitions not at body-level,
              * which we call function statements.
              */
             if (tc->atBodyLevel()) {
                 pn->pn_xflags |= PNX_FUNCDEFS;
             } else {
+                /*
+                 * General deoptimization was done in functionDef, here we just
+                 * need to tell TOK_LC in Parser::statement to add braces.
+                 */
+                JS_ASSERT(tc->hasExtensibleScope());
                 tc->flags |= TCF_HAS_FUNCTION_STMT;
-                /* Function statements extend the Call object at runtime. */
-                tc->noteHasExtensibleScope();
             }
         }
         pn->append(next);
     }
 
     /*
      * Handle the case where there was a let declaration under this block.  If
      * it replaced tc->blockNode with a new block node then we must refresh pn
@@ -2426,17 +2424,18 @@ NoteLValue(JSContext *cx, ParseNode *pn,
      * depending on code strictness.  Assignment to arguments is a syntax error
      * in strict mode and thus cannot happen.  Outside strict mode, we optimize
      * away assignment to the function name.  For assignment to function name
      * to fail in strict mode, we must have a binding for it in the scope
      * chain; we ensure this happens by making such functions heavyweight.
      */
     JSAtom *lname = pn->pn_atom;
     if (lname == cx->runtime->atomState.argumentsAtom) {
-        tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+        tc->flags |= TCF_FUN_HEAVYWEIGHT;
+        tc->noteLocalOverwritesArguments();
         tc->countArgumentsUse(pn);
     } else if (tc->inFunction() && lname == tc->fun()->atom) {
         tc->flags |= TCF_FUN_HEAVYWEIGHT;
     }
 }
 
 #if JS_HAS_DESTRUCTURING
 
@@ -2447,18 +2446,20 @@ BindDestructuringVar(JSContext *cx, Bind
 
     /*
      * Destructuring is a form of assignment, so just as for an initialized
      * simple variable, we must check for assignment to 'arguments' and flag
      * the enclosing function (if any) as heavyweight.
      */
     JS_ASSERT(pn->isKind(PNK_NAME));
     atom = pn->pn_atom;
-    if (atom == cx->runtime->atomState.argumentsAtom)
-        tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+    if (atom == cx->runtime->atomState.argumentsAtom) {
+        tc->flags |= TCF_FUN_HEAVYWEIGHT;
+        tc->noteLocalOverwritesArguments();
+    }
 
     data->pn = pn;
     if (!data->binder(cx, data, atom, tc))
         return JS_FALSE;
 
     /*
      * Select the appropriate name-setting opcode, respecting eager selection
      * done by the data->binder function.
@@ -4418,18 +4419,20 @@ Parser::variables(ParseNodeKind kind, St
 
             NoteLValue(context, pn2, tc, data.fresh ? PND_INITIALIZED : PND_ASSIGNED);
 
             /* The declarator's position must include the initializer. */
             pn2->pn_pos.end = init->pn_pos.end;
 
             if (tc->inFunction() && name == context->runtime->atomState.argumentsAtom) {
                 tc->noteArgumentsNameUse(pn2);
-                if (!blockObj)
-                    tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+                if (!blockObj) {
+                    tc->flags |= TCF_FUN_HEAVYWEIGHT;
+                    tc->noteLocalOverwritesArguments();
+                }
             }
         }
     } while (tokenStream.matchToken(TOK_COMMA));
 
     pn->pn_pos.end = pn->last()->pn_pos.end;
     return pn;
 }
 
@@ -6076,17 +6079,18 @@ Parser::qualifiedSuffix(ParseNode *pn)
     JS_ASSERT(!tc->inStrictMode());
 
     JS_ASSERT(tokenStream.currentToken().type == TOK_DBLCOLON);
     ParseNode *pn2 = NameNode::create(PNK_DBLCOLON, NULL, tc);
     if (!pn2)
         return NULL;
 
     /* This qualifiedSuffice may refer to 'arguments'. */
-    tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+    tc->flags |= TCF_FUN_HEAVYWEIGHT;
+    tc->noteLocalOverwritesArguments();
 
     /* Left operand of :: must be evaluated if it is an identifier. */
     if (pn->isOp(JSOP_QNAMEPART))
         pn->setOp(JSOP_NAME);
 
     TokenKind tt = tokenStream.getToken(TSF_KEYWORD_IS_NAME);
     if (tt == TOK_STAR || tt == TOK_NAME) {
         /* Inline and specialize propertySelector for JSOP_QNAMECONST. */
@@ -6122,17 +6126,18 @@ Parser::qualifiedIdentifier()
 {
     JS_ASSERT(!tc->inStrictMode());
 
     ParseNode *pn = propertySelector();
     if (!pn)
         return NULL;
     if (tokenStream.matchToken(TOK_DBLCOLON)) {
         /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
-        tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+        tc->flags |= TCF_FUN_HEAVYWEIGHT;
+        tc->noteLocalOverwritesArguments();
         pn = qualifiedSuffix(pn);
     }
     return pn;
 }
 
 ParseNode *
 Parser::attributeIdentifier()
 {
@@ -6637,17 +6642,18 @@ ParseNode *
 Parser::propertyQualifiedIdentifier()
 {
     JS_ASSERT(!tc->inStrictMode());
     JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
     JS_ASSERT(tokenStream.currentToken().t_op == JSOP_NAME);
     JS_ASSERT(tokenStream.peekToken() == TOK_DBLCOLON);
 
     /* Deoptimize QualifiedIdentifier properties to avoid tricky analysis. */
-    tc->flags |= (TCF_FUN_HEAVYWEIGHT | TCF_FUN_LOCAL_ARGUMENTS);
+    tc->flags |= TCF_FUN_HEAVYWEIGHT;
+    tc->noteLocalOverwritesArguments();
 
     PropertyName *name = tokenStream.currentToken().name();
     ParseNode *node = NameNode::create(PNK_NAME, name, tc);
     if (!node)
         return NULL;
     node->setOp(JSOP_NAME);
     node->pn_dflags |= PND_DEOPTIMIZED;
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testFunctionStatementNamedArguments.js
@@ -0,0 +1,10 @@
+var g;
+function foo(b) {
+    if (b)
+        function arguments() {};
+    return arguments;
+}
+
+var a = foo(true);
+assertEq(typeof a, "function");
+assertEq(a.name, "arguments");