Bug 721322 - Functions containing |expr.arguments| should be marked (conservatively) as using arguments. r=jorendorff

--HG--
extra : rebase_source : 3ef23e440de3244ffff8ceb30e75fe086ae3b577
This commit is contained in:
Jeff Walden 2012-01-26 17:04:00 -08:00
parent d9e9735f31
commit 6cb3d45766
9 changed files with 169 additions and 28 deletions

View File

@ -434,18 +434,47 @@ struct TreeContext { /* tree context for semantic checks */
return flags & TCF_FUN_MUTATES_PARAMETER;
}
void noteArgumentsUse(ParseNode *pn) {
/*
* Accessing the implicit |arguments| local binding in a function must
* trigger appropriate code generation such that the access works.
*/
void noteArgumentsNameUse(ParseNode *node) {
JS_ASSERT(inFunction());
countArgumentsUse(pn);
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;
}
void countArgumentsUse(ParseNode *pn) {
JS_ASSERT(pn->pn_atom == parser->context->runtime->atomState.argumentsAtom);
/*
* Non-dynamic accesses to a property named "arguments" inside a function
* have to deoptimize the function in case those accesses are to the
* function's arguments. (However, this is unnecessary in strict mode
* functions because of the f.arguments poison-pill. O frabjous day!)
*/
void noteArgumentsPropertyAccess(ParseNode *node) {
JS_ASSERT(inFunction());
JS_ASSERT(&node->asPropertyAccess().name() ==
parser->context->runtime->atomState.argumentsAtom);
if (!inStrictMode()) {
flags |= TCF_FUN_USES_ARGUMENTS;
if (funbox)
funbox->node->pn_dflags |= PND_FUNARG;
}
}
/*
* Uses of |arguments| must be noted so that such uses can be forbidden if
* they occur inside generator expressions (which desugar to functions and
* yields, in which |arguments| would have an entirely different meaning).
*/
void countArgumentsUse(ParseNode *node) {
JS_ASSERT(node->isKind(PNK_NAME));
JS_ASSERT(node->pn_atom == parser->context->runtime->atomState.argumentsAtom);
argumentsCount++;
argumentsNode = pn;
argumentsNode = node;
}
bool needsEagerArguments() const {

View File

@ -484,6 +484,7 @@ class BreakStatement;
class ContinueStatement;
class XMLProcessingInstruction;
class ConditionalExpression;
class PropertyAccess;
struct ParseNode {
private:
@ -927,6 +928,7 @@ struct ParseNode {
inline XMLProcessingInstruction &asXMLProcessingInstruction();
#endif
inline ConditionalExpression &asConditionalExpression();
inline PropertyAccess &asPropertyAccess();
};
struct NullaryNode : public ParseNode {
@ -1230,6 +1232,14 @@ class PropertyAccess : public ParseNode {
}
};
inline PropertyAccess &
ParseNode::asPropertyAccess()
{
JS_ASSERT(isKind(PNK_DOT));
JS_ASSERT(pn_arity == PN_NAME);
return *static_cast<PropertyAccess *>(this);
}
class PropertyByValue : public ParseNode {
public:
PropertyByValue(ParseNode *lhs, ParseNode *propExpr,

View File

@ -4393,7 +4393,7 @@ Parser::variables(ParseNodeKind kind, StaticBlockObject *blockObj, VarContext va
pn2->pn_pos.end = init->pn_pos.end;
if (tc->inFunction() && name == context->runtime->atomState.argumentsAtom) {
tc->noteArgumentsUse(pn2);
tc->noteArgumentsNameUse(pn2);
if (!blockObj)
tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
@ -5746,11 +5746,21 @@ Parser::memberExpr(JSBool allowCallSyntax)
} else
#endif
{
nextMember = new_<PropertyAccess>(lhs, tokenStream.currentToken().name(),
PropertyName *field = tokenStream.currentToken().name();
nextMember = new_<PropertyAccess>(lhs, field,
lhs->pn_pos.begin,
tokenStream.currentToken().pos.end);
if (!nextMember)
return NULL;
/*
* A property access of the form |<expr>.arguments| might
* access this function's arguments, so we need to flag a
* potential arguments use to ensure an arguments object
* will be created. See bug 721322.
*/
if (tc->inFunction() && field == context->runtime->atomState.argumentsAtom)
tc->noteArgumentsPropertyAccess(nextMember);
}
}
#if JS_HAS_XML_SUPPORT
@ -6614,7 +6624,7 @@ Parser::propertyQualifiedIdentifier()
#endif
ParseNode *
Parser::identifierName(bool afterDot)
Parser::identifierName(bool afterDoubleDot)
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
@ -6626,26 +6636,25 @@ Parser::identifierName(bool afterDot)
node->setOp(JSOP_NAME);
if ((tc->flags & (TCF_IN_FUNCTION | TCF_FUN_PARAM_ARGUMENTS)) == TCF_IN_FUNCTION &&
name == context->runtime->atomState.argumentsAtom) {
/*
* Flag arguments usage so we can avoid unsafe optimizations such
* as formal parameter assignment analysis (because of the hated
* feature whereby arguments alias formals). We do this even for
* a reference of the form foo.arguments, which ancient code may
* still use instead of arguments (more hate).
*/
tc->noteArgumentsUse(node);
name == context->runtime->atomState.argumentsAtom)
{
/*
* 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 && !(tc->flags & TCF_DECL_DESTRUCTURING)
&& !tc->inStatement(STMT_WITH)) {
node->setOp(JSOP_ARGUMENTS);
node->pn_dflags |= PND_BOUND;
if (!afterDoubleDot) {
/*
* Note use of |arguments| to ensure we can properly create the
* |arguments| object for this function.
*/
tc->noteArgumentsNameUse(node);
if (!(tc->flags & TCF_DECL_DESTRUCTURING) && !tc->inStatement(STMT_WITH)) {
node->setOp(JSOP_ARGUMENTS);
node->pn_dflags |= PND_BOUND;
}
}
} else if ((!afterDot
} else if ((!afterDoubleDot
#if JS_HAS_XML_SUPPORT
|| (!tc->inStrictMode() && tokenStream.peekToken() == TOK_DBLCOLON)
#endif
@ -6708,7 +6717,7 @@ Parser::identifierName(bool afterDot)
#if JS_HAS_XML_SUPPORT
if (!tc->inStrictMode() && tokenStream.matchToken(TOK_DBLCOLON)) {
if (afterDot) {
if (afterDoubleDot) {
if (!checkForFunctionNode(name, node))
return NULL;
}
@ -6735,7 +6744,7 @@ Parser::starOrAtPropertyIdentifier(TokenKind tt)
#endif
ParseNode *
Parser::primaryExpr(TokenKind tt, JSBool afterDot)
Parser::primaryExpr(TokenKind tt, bool afterDoubleDot)
{
JS_ASSERT(tokenStream.isCurrentTokenType(tt));
@ -7168,7 +7177,7 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot)
#endif
case TOK_NAME:
pn = identifierName(afterDot);
pn = identifierName(afterDoubleDot);
break;
case TOK_REGEXP:

View File

@ -234,7 +234,7 @@ struct Parser : private AutoGCRooter
ParseNode *mulExpr1n();
ParseNode *unaryExpr();
ParseNode *memberExpr(JSBool allowCallSyntax);
ParseNode *primaryExpr(TokenKind tt, JSBool afterDot);
ParseNode *primaryExpr(TokenKind tt, bool afterDoubleDot);
ParseNode *parenExpr(JSBool *genexp = NULL);
/*
@ -259,7 +259,7 @@ struct Parser : private AutoGCRooter
bool checkForFunctionNode(PropertyName *name, ParseNode *node);
ParseNode *identifierName(bool afterDot);
ParseNode *identifierName(bool afterDoubleDot);
#if JS_HAS_XML_SUPPORT
ParseNode *endBracketedExpr();

View File

@ -0,0 +1,6 @@
function f()
{
var x = <><arguments/><arguments/></>;
x..arguments;
}
f();

View File

@ -0,0 +1,58 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor:
* Jeff Walden <jwalden+code@mit.edu>
*/
//-----------------------------------------------------------------------------
var BUGNUMBER = 721322;
var summary =
'f.arguments must trigger an arguments object in non-strict mode functions';
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var obj =
{
test: function()
{
var args = obj.test.arguments;
assertEq(args !== null, true);
assertEq(args[0], 5);
assertEq(args[1], undefined);
assertEq(args.length, 2);
}
};
obj.test(5, undefined);
var sobj =
{
test: function()
{
"use strict";
try
{
var args = sobj.test.arguments;
throw new Error("access to arguments property of strict mode " +
"function didn't throw");
}
catch (e)
{
assertEq(e instanceof TypeError, true,
"should have thrown TypeError, instead got: " + e);
}
}
};
sobj.test(5, undefined);
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -1,4 +1,5 @@
url-prefix ../../jsreftest.html?test=ecma_5/extensions/
script arguments-property-access-in-function.js
script 8.12.5-01.js
script 15.4.4.11.js
script 15.9.4.2.js

View File

@ -0,0 +1,27 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributor:
* Jeff Walden <jwalden+code@mit.edu>
*/
//-----------------------------------------------------------------------------
var BUGNUMBER = 721322;
var summary = 'Allow f.arguments in generator expressions';
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
eval("(function() { return (f.arguments for (x in [1])); })()");
eval("(function() { var f = { arguments: 12 }; return [f.arguments for (x in [1])]; })()");
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("Tests complete");

View File

@ -1,4 +1,5 @@
url-prefix ../../jsreftest.html?test=js1_8/genexps/
script arguments-property-access-in-generator.js
script regress-347739.js
script regress-349012-01.js
script regress-349326.js