diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 7a5cef7d78..a4d761a2a1 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -994,12 +994,15 @@ Parser::parse() bool ParserBase::isValidStrictBinding(PropertyName* name) { - return name != context->names().eval && - name != context->names().arguments && - name != context->names().let && - name != context->names().static_ && - name != context->names().yield && - !IsStrictReservedWord(name); + TokenKind tt = ReservedWordTokenKind(name); + if (tt == TOK_NAME) { + return name != context->names().eval && + name != context->names().arguments; + } + return tt != TOK_LET && + tt != TOK_STATIC && + tt != TOK_YIELD && + !TokenKindIsStrictReservedWord(tt); } /* @@ -3725,6 +3728,7 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc->generatorKind()); AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc->asyncKind()); + bool inheritedStrict = pc->sc()->strict(); LexicalScopeNodeType body; { AutoAwaitIsKeyword awaitIsKeyword(this, bodyAwaitHandling); @@ -3733,9 +3737,15 @@ Parser::functionFormalParametersAndBody(InHandling inHandling, return false; } + // Revalidate the function name when we transitioned to strict mode. if ((kind == FunctionSyntaxKind::Statement || - kind == FunctionSyntaxKind::Expression) && fun->explicitName()) { - RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName()); + kind == FunctionSyntaxKind::Expression) && fun->explicitName() + && !inheritedStrict && pc->sc()->strict()) + { + MOZ_ASSERT(pc->sc()->hasExplicitUseStrict(), + "strict mode should only change when a 'use strict' directive is present"); + + PropertyName* propertyName = fun->explicitName()->asPropertyName(); YieldHandling nameYieldHandling; if (kind == FunctionSyntaxKind::Expression) { // Named lambda has binding inside it. @@ -9675,75 +9685,80 @@ Parser::memberCall( template bool -Parser::checkLabelOrIdentifierReference(HandlePropertyName ident, +Parser::checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset, - YieldHandling yieldHandling) + YieldHandling yieldHandling, + TokenKind hint /* = TOK_LIMIT */) { - if (ident == context->names().yield) { - if (yieldHandling == YieldIsKeyword || - versionNumber() >= JSVERSION_1_7) - { - errorAt(offset, JSMSG_RESERVED_ID, "yield"); - return false; + TokenKind tt; + if (hint == TOK_LIMIT) { + tt = ReservedWordTokenKind(ident); + } else { + MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind"); + tt = hint; + } + + if (tt == TOK_NAME) + return true; + if (TokenKindIsContextualKeyword(tt)) { + if (tt == TOK_YIELD) { + if (yieldHandling == YieldIsKeyword || versionNumber() >= JSVERSION_1_7) { + errorAt(offset, JSMSG_RESERVED_ID, "yield"); + return false; + } + if (pc->sc()->needStrictChecks()) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) + return false; + } + return true; + } + if (tt == TOK_AWAIT) { + if (awaitIsKeyword()) { + errorAt(offset, JSMSG_RESERVED_ID, "await"); + return false; + } + return true; } if (pc->sc()->needStrictChecks()) { - if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) - return false; - } - - return true; - } - - if (ident == context->names().await) { - if (awaitIsKeyword()) { - errorAt(offset, JSMSG_RESERVED_ID, "await"); - return false; + if (tt == TOK_LET) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) + return false; + return true; + } + if (tt == TOK_STATIC) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) + return false; + return true; + } } return true; } - - if (IsKeyword(ident) || IsReservedWordLiteral(ident)) { - errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(ident)); + if (TokenKindIsStrictReservedWord(tt)) { + if (pc->sc()->needStrictChecks()) { + if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt))) + return false; + } + return true; + } + if (TokenKindIsKeyword(tt) || TokenKindIsReservedWordLiteral(tt)) { + errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(tt)); return false; } - - if (IsFutureReservedWord(ident)) { - errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident)); + if (TokenKindIsFutureReservedWord(tt)) { + errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt)); return false; } - - if (pc->sc()->needStrictChecks()) { - if (IsStrictReservedWord(ident)) { - if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(ident))) - return false; - return true; - } - - if (ident == context->names().let) { - if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) - return false; - return true; - } - - if (ident == context->names().static_) { - if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) - return false; - return true; - } - } - - return true; + MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind."); + return false; } template bool -Parser::checkBindingIdentifier(HandlePropertyName ident, +Parser::checkBindingIdentifier(PropertyName* ident, uint32_t offset, - YieldHandling yieldHandling) + YieldHandling yieldHandling, + TokenKind hint /* = TOK_LIMIT */) { - if (!checkLabelOrIdentifierReference(ident, offset, yieldHandling)) - return false; - if (pc->sc()->needStrictChecks()) { if (ident == context->names().arguments) { if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments")) @@ -9758,7 +9773,7 @@ Parser::checkBindingIdentifier(HandlePropertyName ident, } } - return true; + return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint); } template @@ -9772,8 +9787,13 @@ Parser::labelOrIdentifierReference(YieldHandling yieldHandling) // // Use PropertyName* instead of TokenKind to reflect the normalization. + // Unless the name contains escapes, we can reuse the current TokenKind + // to determine if the name is a restricted identifier. + TokenKind hint = !tokenStream.currentNameHasEscapes() + ? tokenStream.currentToken().type + : TOK_LIMIT; RootedPropertyName ident(context, tokenStream.currentName()); - if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling)) + if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint)) return nullptr; return ident; } @@ -9782,8 +9802,11 @@ template PropertyName* Parser::bindingIdentifier(YieldHandling yieldHandling) { + TokenKind hint = !tokenStream.currentNameHasEscapes() + ? tokenStream.currentToken().type + : TOK_LIMIT; RootedPropertyName ident(context, tokenStream.currentName()); - if (!checkBindingIdentifier(ident, pos().begin, yieldHandling)) + if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint)) return nullptr; return ident; } diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 4dd9f64178..bd111e54fe 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -1479,17 +1479,19 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) ClassNodeType classDefinition(YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling); - bool checkLabelOrIdentifierReference(HandlePropertyName ident, + bool checkLabelOrIdentifierReference(PropertyName* ident, uint32_t offset, - YieldHandling yieldHandling); + YieldHandling yieldHandling, + TokenKind hint = TOK_LIMIT); - bool checkLocalExportName(HandlePropertyName ident, uint32_t offset) { + bool checkLocalExportName(PropertyName* ident, uint32_t offset) { return checkLabelOrIdentifierReference(ident, offset, YieldIsName); } - bool checkBindingIdentifier(HandlePropertyName ident, + bool checkBindingIdentifier(PropertyName* ident, uint32_t offset, - YieldHandling yieldHandling); + YieldHandling yieldHandling, + TokenKind hint = TOK_LIMIT); PropertyName* labelOrIdentifierReference(YieldHandling yieldHandling); diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index 2539249ad9..a537c5f275 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -194,41 +194,29 @@ frontend::IsKeyword(JSLinearString* str) return false; } -bool -frontend::IsFutureReservedWord(JSLinearString* str) +TokenKind +frontend::ReservedWordTokenKind(PropertyName* str) { if (const ReservedWordInfo* rw = FindReservedWord(str)) - return TokenKindIsFutureReservedWord(rw->tokentype); + return rw->tokentype; - return false; -} - -bool -frontend::IsStrictReservedWord(JSLinearString* str) -{ - if (const ReservedWordInfo* rw = FindReservedWord(str)) - return TokenKindIsStrictReservedWord(rw->tokentype); - - return false; -} - -bool -frontend::IsReservedWordLiteral(JSLinearString* str) -{ - if (const ReservedWordInfo* rw = FindReservedWord(str)) - return TokenKindIsReservedWordLiteral(rw->tokentype); - - return false; + return TOK_NAME; } const char* frontend::ReservedWordToCharZ(PropertyName* str) { - const ReservedWordInfo* rw = FindReservedWord(str); - if (rw == nullptr) - return nullptr; + if (const ReservedWordInfo* rw = FindReservedWord(str)) + return ReservedWordToCharZ(rw->tokentype); - switch (rw->tokentype) { + return nullptr; +} + +const char* +frontend::ReservedWordToCharZ(TokenKind tt) +{ + MOZ_ASSERT(tt != TOK_NAME); + switch (tt) { #define EMIT_CASE(word, name, type) case type: return js_##word##_str; FOR_EACH_JAVASCRIPT_RESERVED_WORD(EMIT_CASE) #undef EMIT_CASE diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 7129ae6d76..067f11c8d8 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -244,17 +244,14 @@ class CompileError : public JSErrorReport { void throwError(JSContext* cx); }; +extern TokenKind +ReservedWordTokenKind(PropertyName* str); + extern const char* ReservedWordToCharZ(PropertyName* str); -extern MOZ_MUST_USE bool -IsFutureReservedWord(JSLinearString* str); - -extern MOZ_MUST_USE bool -IsReservedWordLiteral(JSLinearString* str); - -extern MOZ_MUST_USE bool -IsStrictReservedWord(JSLinearString* str); +extern const char* +ReservedWordToCharZ(TokenKind tt); // Ideally, tokenizing would be entirely independent of context. But the // strict mode flag, which is in SharedContext, affects tokenizing, and @@ -355,6 +352,16 @@ class MOZ_STACK_CLASS TokenStream return reservedWordToPropertyName(currentToken().type); } + bool currentNameHasEscapes() const { + if (isCurrentTokenType(TOK_NAME)) { + TokenPos pos = currentToken().pos; + return (pos.end - pos.begin) != currentToken().name()->length(); + } + + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(currentToken().type)); + return false; + } + PropertyName* nextName() const { if (nextToken().type != TOK_NAME) { return nextToken().name();