Bug 610350 - Assigning to a named function's name in strict mode code should throw. r=brendan

This commit is contained in:
Jeff Walden 2010-11-12 14:33:46 -08:00
parent fa572289d5
commit c137b2d4d9
4 changed files with 75 additions and 8 deletions

View File

@ -2500,15 +2500,28 @@ BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT((cg->fun()->flags & JSFUN_LAMBDA) && atom == cg->fun()->atom);
/*
* Leave pn->pn_op == JSOP_NAME if cg->fun() is heavyweight, as we
* cannot be sure cg->fun() is not something of the form:
* Leave pn->pn_op == JSOP_NAME if cg->fun is heavyweight to
* address two cases: a new binding introduced by eval, and
* assignment to the name in strict mode.
*
* var ff = (function f(s) { eval(s); return f; });
* var fun = (function f(s) { eval(s); return f; });
* assertEq(fun("var f = 42"), 42);
*
* where a caller invokes ff("var f = 42"). The result returned for
* such an invocation must be 42, since the callee name is
* lexically bound in an outer declarative environment from the
* function's activation. See jsfun.cpp:call_resolve.
* ECMAScript specifies that a function expression's name is bound
* in a lexical environment distinct from that used to bind its
* named parameters, the arguments object, and its variables. The
* new binding for "var f = 42" shadows the binding for the
* function itself, so the name of the function will not refer to
* the function.
*
* (function f() { "use strict"; f = 12; })();
*
* Outside strict mode, assignment to a function expression's name
* has no effect. But in strict mode, this attempt to mutate an
* immutable binding must throw a TypeError. We implement this by
* not optimizing such assignments and by marking such functions as
* heavyweight, ensuring that the function name is represented in
* the scope chain so that assignment will throw a TypeError.
*/
JS_ASSERT(op != JSOP_DELNAME);
if (!(cg->flags & TCF_FUN_HEAVYWEIGHT)) {

View File

@ -3843,9 +3843,20 @@ NoteLValue(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, uintN dflag = PND_
pn->pn_dflags |= dflag;
/*
* Both arguments and the enclosing function's name are immutable bindings
* in ES5, so assignments to them must do nothing or throw a TypeError
* 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)
if (lname == cx->runtime->atomState.argumentsAtom ||
(tc->inFunction() && lname == tc->fun()->atom)) {
tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
}
#if JS_HAS_DESTRUCTURING

View File

@ -0,0 +1,42 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var gTestfile = 'assign-to-callee-name.js';
var BUGNUMBER = 610350;
var summary =
"Assigning to a function expression's name within that function should " +
"throw a TypeError in strict mode code";
print(BUGNUMBER + ": " + summary);
/**************
* BEGIN TEST *
**************/
var f = function assignSelfStrict() { "use strict"; assignSelfStrict = 12; };
try
{
var r = f();
throw new Error("should have thrown a TypeError, returned " + r);
}
catch (e)
{
assertEq(e instanceof TypeError, true,
"didn't throw a TypeError: " + e);
}
var assignSelf = 42;
var f2 = function assignSelf() { assignSelf = 12; };
f2(); // shouldn't throw, does nothing
assertEq(assignSelf, 42);
/******************************************************************************/
if (typeof reportCompare === "function")
reportCompare(true, true);
print("All tests passed!");

View File

@ -37,4 +37,5 @@ script regress-532254.js
script regress-532041.js
script unbrand-this.js
script this-for-function-expression-recursion.js
script assign-to-callee-name.js
script directive-prologue-01.js