Bug 907958 - Disallow |function function() {}| and similar unreadabilities. r=jorendorff, r=wingo for the yield interactions, r=luke for the asm.js interactions

This commit is contained in:
Jeff Walden 2013-08-15 10:07:40 -07:00
parent adc074bd27
commit 38577c2633
8 changed files with 67 additions and 142 deletions

View File

@ -1642,7 +1642,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
#endif /* JS_HAS_DESTRUCTURING */
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL))
if (!checkYieldNameValidity())
return false;
goto TOK_NAME;
@ -1720,20 +1720,6 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkFunctionName(HandlePropertyName funName)
{
if (pc->isStarGenerator() && funName == context->names().yield) {
// The name of a named function expression is specified to be bound in
// the outer context as if via "let". In an ES6 generator, "yield" is
// not a valid name for a let-bound variable.
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <>
bool
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
@ -1746,9 +1732,6 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -1940,9 +1923,6 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -2419,15 +2399,11 @@ Parser<SyntaxParseHandler>::moduleDecl()
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkYieldNameValidity(unsigned errorNumber)
Parser<ParseHandler>::checkYieldNameValidity()
{
// In star generators and in JS >= 1.7, yield is a keyword.
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) {
report(ParseError, false, null(), errorNumber);
return false;
}
// Otherwise in strict mode, yield is a future reserved word.
if (pc->sc->strict) {
// In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
// strict mode, yield is a future reserved word.
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
@ -2445,16 +2421,20 @@ Parser<ParseHandler>::functionStmt()
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
TokenKind tt = tokenStream.getToken();
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
tt = tokenStream.getToken();
generatorKind = StarGenerator;
}
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
if (!checkYieldNameValidity())
return null();
name = tokenStream.currentName();
} else {
/* Unnamed function expressions are forbidden in statement context. */
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
@ -2478,20 +2458,25 @@ Parser<ParseHandler>::functionExpr()
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
TokenKind tt = tokenStream.getToken();
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
tt = tokenStream.getToken();
generatorKind = StarGenerator;
}
if (tt == TOK_NAME)
RootedPropertyName name(context);
if (tt == TOK_NAME) {
name = tokenStream.currentName();
else
} else if (tt == TOK_YIELD) {
if (!checkYieldNameValidity())
return null();
name = tokenStream.currentName();
} else {
tokenStream.ungetToken();
}
return functionDef(name, start, Normal, Expression, generatorKind);
}
@ -3570,7 +3555,7 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
if (tt != TOK_NAME) {
if (tt == TOK_YIELD) {
if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME))
if (!checkYieldNameValidity())
return null();
} else {
if (tt != TOK_ERROR)
@ -4882,7 +4867,7 @@ Parser<ParseHandler>::tryStatement()
#endif
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER))
if (!checkYieldNameValidity())
return null();
// Fall through.
case TOK_NAME:

View File

@ -458,6 +458,11 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
FunctionSyntaxKind kind, Directives *newDirectives);
// Determine whether |yield| is a valid name in the current context, or
// whether it's prohibited due to strictness, JS version, or occurrence
// inside a star generator.
bool checkYieldNameValidity();
virtual bool strictMode() { return pc->sc->strict; }
const CompileOptions &options() const {
@ -546,7 +551,6 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node identifierName();
bool matchLabel(MutableHandle<PropertyName*> label);
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
bool allowsForEachIn() {
#if !JS_HAS_FOR_EACH_IN
@ -568,7 +572,6 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionName(HandlePropertyName funName);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);

View File

@ -4588,10 +4588,18 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
DebugOnly<TokenKind> tk = tokenStream.getToken();
JS_ASSERT(tk == TOK_FUNCTION);
if (tokenStream.getToken(TokenStream::KeywordIsName) != TOK_NAME)
return false; // This will throw a SyntaxError, no need to m.fail.
RootedPropertyName name(m.cx());
RootedPropertyName name(m.cx(), tokenStream.currentToken().name());
TokenKind tt = tokenStream.getToken();
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else if (tt == TOK_YIELD) {
if (!m.parser().checkYieldNameValidity())
return false;
name = m.cx()->names().yield;
} else {
return false; // The regular parser will throw a SyntaxError, no need to m.fail.
}
ParseNode *fn = m.parser().handler.newFunctionDefinition();
if (!fn)

View File

@ -37,7 +37,7 @@ var strictFutureReservedWords =
"yield", // enabled: this file doesn't execute as JS1.7
];
function testWord(word, wordKind, expectNormal, expectStrict)
function testWord(word, expectNormal, expectStrict)
{
var actual, status;
@ -355,22 +355,19 @@ function testWord(word, wordKind, expectNormal, expectStrict)
// USE AS FUNCTION NAME IN FUNCTION DECLARATION
if (wordKind !== "reserved")
actual = "";
status = summary + ": " + word + ": normal function name";
try
{
actual = "";
status = summary + ": " + word + ": normal function name";
try
{
eval("function " + word + "() { }");
actual = "no error";
}
catch (e)
{
actual = e.name;
status += ", " + e.name + ": " + e.message + " ";
}
reportCompare(expectNormal, actual, status);
eval("function " + word + "() { }");
actual = "no error";
}
catch (e)
{
actual = e.name;
status += ", " + e.name + ": " + e.message + " ";
}
reportCompare(expectNormal, actual, status);
actual = "";
status = summary + ": " + word + ": strict function name";
@ -402,22 +399,19 @@ function testWord(word, wordKind, expectNormal, expectStrict)
// USE AS FUNCTION NAME IN FUNCTION EXPRESSION
if (wordKind !== "reserved")
actual = "";
status = summary + ": " + word + ": normal function expression name";
try
{
actual = "";
status = summary + ": " + word + ": normal function expression name";
try
{
eval("var s = (function " + word + "() { });");
actual = "no error";
}
catch (e)
{
actual = e.name;
status += ", " + e.name + ": " + e.message + " ";
}
reportCompare(expectNormal, actual, status);
eval("var s = (function " + word + "() { });");
actual = "no error";
}
catch (e)
{
actual = e.name;
status += ", " + e.name + ": " + e.message + " ";
}
reportCompare(expectNormal, actual, status);
actual = "";
status = summary + ": " + word + ": strict function expression name";
@ -450,12 +444,12 @@ function testWord(word, wordKind, expectNormal, expectStrict)
function testFutureReservedWord(word)
{
testWord(word, "reserved", "SyntaxError", "SyntaxError");
testWord(word, "SyntaxError", "SyntaxError");
}
function testStrictFutureReservedWord(word)
{
testWord(word, "strict reserved", "no error", "SyntaxError");
testWord(word, "no error", "SyntaxError");
}
futureReservedWords.forEach(testFutureReservedWord);

View File

@ -1,49 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//-----------------------------------------------------------------------------
var BUGNUMBER = 343675;
var summary = 'Allow keywords, reserved words as function names';
var actual = '';
var expect = 'No Error';
//-----------------------------------------------------------------------------
test();
//-----------------------------------------------------------------------------
function test()
{
enterFunc ('test');
printBugNumber(BUGNUMBER);
printStatus (summary);
var words = [
'break', 'else', 'new', 'var', 'case', 'finally', 'return', 'void',
'catch', 'for', 'switch', 'while', 'continue', 'function', 'this',
'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'do',
'instanceof', 'typeof',
'abstract', 'enum', 'int', 'short', 'boolean', 'export', 'interface',
'static', 'byte', 'extends', 'long', 'super', 'char', 'final', 'native',
'synchronized', 'class', 'float', 'package', 'throws', 'const', 'goto',
'private', 'transient', 'debugger', 'implements', 'protected', 'volatile',
'double', 'import', 'public'];
for (var i = 0; i < words.length; i++)
{
try
{
actual = 'No Error';
eval('function ' + words[i] + '() {}');
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': ' + words[i]);
}
exitFunc ('test');
}

View File

@ -93,17 +93,5 @@ function test()
}
reportCompare(expect, actual, summary + ': function () { var let;}');
try
{
expect = 'No Error';
function yield() {}
actual = 'No Error';
}
catch(ex)
{
actual = ex + '';
}
reportCompare(expect, actual, summary + ': function yield()');
exitFunc ('test');
}

View File

@ -36,11 +36,7 @@ function test()
// Assertion failure: !pn->isPlaceholder(), at ../jsparse.cpp:4876
// =====
(function(){ var x; eval("var x; x = null"); })()
// Assertion failure: regs.sp == StackBase(fp), at ../jsinterp.cpp:2984
// =====
function this ({x}) { function x(){} }
(function(){ var x; eval("var x; x = null"); })();
// Assertion failure: !(pnu->pn_dflags & PND_BOUND), at ../jsemit.cpp:1818
// =====

View File

@ -111,7 +111,7 @@ ServerBSO.prototype = {
return obj;
},
delete: function delete() {
delete: function delete_() {
this.deleted = true;
delete this.payload;
@ -571,7 +571,7 @@ StorageServerCollection.prototype = {
return {success: success, failed: failed};
},
delete: function delete(options) {
delete: function delete_(options) {
options = options || {};
// Protocol 2.0 only allows the "ids" query string argument.