Issue #2142 - Reduce calls to FindReservedWord when checking for forbidden identifiers during parsing

Based-on: m-c 1351913/{1,2}
This commit is contained in:
Martok 2023-04-26 18:22:30 +02:00 • committed by roytam1
commit 6abb54ff11
4 changed files with 116 additions and 96 deletions

View file

@ -994,12 +994,15 @@ Parser<ParseHandler>::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<ParseHandler>::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<ParseHandler> awaitIsKeyword(this, bodyAwaitHandling);
@ -3733,9 +3737,15 @@ Parser<ParseHandler>::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<ParseHandler>::memberCall(
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkLabelOrIdentifierReference(HandlePropertyName ident,
Parser<ParseHandler>::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 <typename ParseHandler>
bool
Parser<ParseHandler>::checkBindingIdentifier(HandlePropertyName ident,
Parser<ParseHandler>::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<ParseHandler>::checkBindingIdentifier(HandlePropertyName ident,
}
}
return true;
return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint);
}
template <typename ParseHandler>
@ -9772,8 +9787,13 @@ Parser<ParseHandler>::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 <typename ParseHandler>
PropertyName*
Parser<ParseHandler>::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;
}

View file

@ -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);

View file

@ -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

View file

@ -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();