diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 1d4fc0d0a..4670b64af 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -905,22 +905,30 @@ class FullParseHandler return pn->isConstant(); } - PropertyName* maybeUnparenthesizedName(ParseNode* pn) { - if (!pn->isInParens() && pn->isKind(PNK_NAME)) - return pn->pn_atom->asPropertyName(); - return nullptr; + bool isUnparenthesizedName(ParseNode* node) { + return node->isKind(PNK_NAME) && !node->isInParens(); } - PropertyName* maybeParenthesizedName(ParseNode* pn) { - if (pn->isInParens() && pn->isKind(PNK_NAME)) - return pn->pn_atom->asPropertyName(); - return nullptr; + bool isNameAnyParentheses(ParseNode* node) { + return node->isKind(PNK_NAME); } - PropertyName* maybeNameAnyParentheses(ParseNode* node) { - if (PropertyName* name = maybeUnparenthesizedName(node)) - return name; - return maybeParenthesizedName(node); + bool nameIsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + return node->pn_atom == cx->names().eval; + } + + const char* nameIsArgumentsEvalAnyParentheses(ParseNode* node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + + if (nameIsEvalAnyParentheses(node, cx)) + return js_eval_str; + if (node->pn_atom == cx->names().arguments) + return js_arguments_str; + return nullptr; } bool isCall(ParseNode* pn) { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 245056c86..13bf6c604 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4117,7 +4117,7 @@ Parser::checkDestructuringName(BindData* dat if (data) { // Destructuring patterns in declarations must only contain // unparenthesized names. - if (!handler.maybeUnparenthesizedName(expr)) { + if (!handler.isUnparenthesizedName(expr)) { report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME); return false; } @@ -4133,7 +4133,7 @@ Parser::checkDestructuringName(BindData* dat "function calls shouldn't be considered valid targets in " "destructuring patterns"); - if (handler.maybeNameAnyParentheses(expr)) { + if (handler.isNameAnyParentheses(expr)) { // The arguments/eval identifiers are simple in non-strict mode code. // Warn to discourage their use nonetheless. if (!reportIfArgumentsEvalTarget(expr)) @@ -5665,7 +5665,7 @@ Parser::validateForInOrOfLHSExpression(Node target) if (handler.isPropertyAccess(target)) return true; - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // The arguments/eval identifiers are simple in non-strict mode code, // but warn to discourage use nonetheless. if (!reportIfArgumentsEvalTarget(target)) @@ -5953,7 +5953,7 @@ Parser::forStatement(YieldHandling yieldHandling) pn3 = iteratedExpr; - if (handler.maybeNameAnyParentheses(pn2)) { + if (handler.isNameAnyParentheses(pn2)) { // Beware 'for (arguments in ...)' with or without a 'var'. handler.markAsAssigned(pn2); } @@ -7516,7 +7516,7 @@ Parser::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor if (handler.isPropertyAccess(target)) return true; - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // The arguments/eval identifiers are simple in non-strict mode code, // but warn to discourage use nonetheless. if (!reportIfArgumentsEvalTarget(target)) @@ -7697,11 +7697,11 @@ Parser::isValidSimpleAssignmentTarget(Node node, // error for the various syntaxes that fail this, and warning for the // various syntaxes that "pass" this but should not, occurs elsewhere. - if (PropertyName* name = handler.maybeNameAnyParentheses(node)) { + if (handler.isNameAnyParentheses(node)) { if (!pc->sc->strict()) return true; - return name != context->names().arguments && name != context->names().eval; + return !handler.nameIsArgumentsEvalAnyParentheses(node, context); } if (handler.isPropertyAccess(node)) @@ -7719,21 +7719,16 @@ template bool Parser::reportIfArgumentsEvalTarget(Node nameNode) { - PropertyName* name = handler.maybeNameAnyParentheses(nameNode); - MOZ_ASSERT(name, "must only call this function on known names"); - - const char* chars = (name == context->names().arguments) - ? js_arguments_str - : (name == context->names().eval) - ? js_eval_str - : nullptr; + const char* chars = handler.nameIsArgumentsEvalAnyParentheses(nameNode, context); if (!chars) return true; if (!report(ParseStrictError, pc->sc->strict(), nameNode, JSMSG_BAD_STRICT_ASSIGN, chars)) return false; - MOZ_ASSERT(!pc->sc->strict(), "in strict mode an error should have been reported"); + MOZ_ASSERT(!pc->sc->strict(), + "an error should have been reported if this was strict mode " + "code"); return true; } @@ -7747,7 +7742,7 @@ Parser::reportIfNotValidSimpleAssignmentTarget(Node target, Assign if (isValidSimpleAssignmentTarget(target, behavior)) return true; - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // Use a special error if the target is arguments/eval. This ensures // targeting these names is consistently a SyntaxError (which error numbers // below don't guarantee) while giving us a nicer error message. @@ -7798,7 +7793,7 @@ Parser::checkAndMarkAsIncOperand(Node target, AssignmentFlavor fla return false; // Mark. - if (handler.maybeNameAnyParentheses(target)) { + if (handler.isNameAnyParentheses(target)) { // Assignment to arguments/eval is allowed outside strict mode code, // but it's dodgy. Report a strict warning (error, if werror was set). if (!reportIfArgumentsEvalTarget(target)) @@ -7889,7 +7884,7 @@ Parser::unaryExpr(YieldHandling yieldHandling, TripledotHandling t // Per spec, deleting any unary expression is valid -- it simply // returns true -- except for one case that is illegal in strict mode. - if (handler.maybeNameAnyParentheses(expr)) { + if (handler.isNameAnyParentheses(expr)) { if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND)) return null(); pc->sc->setBindingsAccessedDynamically(); @@ -9091,8 +9086,8 @@ Parser::memberExpr(YieldHandling yieldHandling, TripledotHandling return null(); JSOp op = JSOP_CALL; - if (PropertyName* name = handler.maybeNameAnyParentheses(lhs)) { - if (tt == TOK_LP && name == context->names().eval) { + if (handler.isNameAnyParentheses(lhs)) { + if (tt == TOK_LP && handler.nameIsEvalAnyParentheses(lhs, context)) { /* Select JSOP_EVAL and flag pc as needsCallObject. */ op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL; pc->sc->setBindingsAccessedDynamically(); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index cc82870da..a230345f9 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -371,7 +371,7 @@ class SyntaxParseHandler // Careful: we're asking this well after the name was parsed, so the // value returned may not correspond to |kid|'s actual name. But it // *will* be truthy iff |kid| was a name, so we're safe. - MOZ_ASSERT(maybeUnparenthesizedName(kid)); + MOZ_ASSERT(isUnparenthesizedName(kid)); return NodeGeneric; } @@ -573,30 +573,35 @@ class SyntaxParseHandler bool isConstant(Node pn) { return false; } - PropertyName* maybeUnparenthesizedName(Node node) { - if (node == NodeUnparenthesizedName || - node == NodeUnparenthesizedArgumentsName || - node == NodeUnparenthesizedEvalName) - { - return lastAtom->asPropertyName(); - } - return nullptr; + bool isUnparenthesizedName(Node node) { + return node == NodeUnparenthesizedArgumentsName || + node == NodeUnparenthesizedEvalName || + node == NodeUnparenthesizedName; } - PropertyName* maybeParenthesizedName(Node node) { - if (node == NodeParenthesizedName || - node == NodeParenthesizedArgumentsName || - node == NodeParenthesizedEvalName) - { - return lastAtom->asPropertyName(); - } - return nullptr; + bool isNameAnyParentheses(Node node) { + if (isUnparenthesizedName(node)) + return true; + return node == NodeParenthesizedArgumentsName || + node == NodeParenthesizedEvalName || + node == NodeParenthesizedName; } - PropertyName* maybeNameAnyParentheses(Node node) { - if (PropertyName* name = maybeUnparenthesizedName(node)) - return name; - return maybeParenthesizedName(node); + bool nameIsEvalAnyParentheses(Node node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this function on known names"); + return node == NodeUnparenthesizedEvalName || node == NodeParenthesizedEvalName; + } + + const char* nameIsArgumentsEvalAnyParentheses(Node node, ExclusiveContext* cx) { + MOZ_ASSERT(isNameAnyParentheses(node), + "must only call this method on known names"); + + if (nameIsEvalAnyParentheses(node, cx)) + return js_eval_str; + if (node == NodeUnparenthesizedArgumentsName || node == NodeParenthesizedArgumentsName) + return js_arguments_str; + return nullptr; } PropertyName* maybeDottedProperty(Node node) { diff --git a/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js b/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js new file mode 100644 index 000000000..212b34db9 --- /dev/null +++ b/js/src/tests/ecma_6/Statements/for-inof-name-iteration-expression-contains-index-string.js @@ -0,0 +1,43 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var gTestfile = "for-inof-name-iteration-expression-contains-index-string.js"; +var BUGNUMBER = 1235640; +var summary = + "Don't assert parsing a for-in/of loop whose target is a name, where the " + + "expression being iterated over contains a string containing an index"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +function f() +{ + var x; + for (x in "9") + continue; + assertEq(x, "0"); +} + +f(); + +function g() +{ + "use strict"; + var x = "unset"; + for (x in arguments) + continue; + assertEq(x, "unset"); +} + +g(); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete");