Skip to content

Commit

Permalink
LibJS: Handle escaped keywords in more cases and handle 'await' labels
Browse files Browse the repository at this point in the history
  • Loading branch information
davidot authored and linusg committed Sep 30, 2021
1 parent 79caca8 commit 4428e49
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
39 changes: 29 additions & 10 deletions Userland/Libraries/LibJS/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,7 @@ NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab
m_state.current_token = m_state.lexer.force_slash_as_regex();
[[fallthrough]];
default:
if (type == TokenType::EscapedKeyword
&& (m_state.strict_mode
|| (m_state.current_token.value() != "yield"sv && m_state.current_token.value() != "let"sv)))
if (match_invalid_escaped_keyword())
syntax_error("Keyword must not contain escaped characters");

if (match_identifier_name()) {
Expand All @@ -432,6 +430,20 @@ NonnullRefPtr<Statement> Parser::parse_statement(AllowLabelledFunction allow_lab
}
}

bool Parser::match_invalid_escaped_keyword() const
{
if (m_state.current_token.type() != TokenType::EscapedKeyword)
return false;
auto token_value = m_state.current_token.value();
if (token_value == "await"sv) {
return m_program_type == Program::Type::Module;
}
if (m_state.strict_mode) {
return true;
}
return token_value != "yield"sv && token_value != "let"sv;
}

static constexpr AK::Array<StringView, 9> strict_reserved_words = { "implements", "interface", "let", "package", "private", "protected", "public", "static", "yield" };

static bool is_strict_reserved_word(StringView str)
Expand Down Expand Up @@ -582,7 +594,10 @@ RefPtr<Statement> Parser::try_parse_labelled_statement(AllowLabelledFunction all
};

if (m_state.current_token.value() == "yield"sv && (m_state.strict_mode || m_state.in_generator_function_context)) {
syntax_error("'yield' label not allowed in this context");
return {};
}

if (m_state.current_token.value() == "await"sv && m_program_type == Program::Type::Module) {
return {};
}

Expand Down Expand Up @@ -2854,6 +2869,7 @@ bool Parser::match_expression() const
|| type == TokenType::TemplateLiteralStart
|| type == TokenType::NullLiteral
|| match_identifier()
|| type == TokenType::Await
|| type == TokenType::New
|| type == TokenType::Class
|| type == TokenType::CurlyOpen
Expand Down Expand Up @@ -3026,12 +3042,14 @@ bool Parser::match_identifier() const
return !m_state.strict_mode;
if (m_state.current_token.value() == "yield"sv)
return !m_state.strict_mode && !m_state.in_generator_function_context;
if (m_state.current_token.value() == "await"sv)
return m_program_type != Program::Type::Module;
return true;
}

return m_state.current_token.type() == TokenType::Identifier
|| (m_state.current_token.type() == TokenType::Let && !m_state.strict_mode)
|| (m_state.current_token.type() == TokenType::Await && !m_state.strict_mode)
|| (m_state.current_token.type() == TokenType::Await && m_program_type != Program::Type::Module)
|| (m_state.current_token.type() == TokenType::Yield && !m_state.in_generator_function_context && !m_state.strict_mode); // See note in Parser::parse_identifier().
}

Expand Down Expand Up @@ -3119,10 +3137,10 @@ Token Parser::consume_identifier_reference()

if (match(TokenType::EscapedKeyword)) {
auto name = m_state.current_token.value();
if (name == "await"sv)
syntax_error("Identifier reference may not be 'await'");
else if (m_state.strict_mode && (name == "let"sv || name == "yield"sv))
if (m_state.strict_mode && (name == "let"sv || name == "yield"sv))
syntax_error(String::formatted("'{}' is not allowed as an identifier in strict mode", name));
if (m_program_type == Program::Type::Module && name == "await"sv)
syntax_error("'await' is not allowed as an identifier in module");

return consume();
}
Expand All @@ -3141,7 +3159,8 @@ Token Parser::consume_identifier_reference()
}

if (match(TokenType::Await)) {
syntax_error("Identifier reference may not be 'await'");
if (m_program_type == Program::Type::Module)
syntax_error("'await' is not allowed as an identifier in module");
return consume();
}

Expand Down Expand Up @@ -3343,7 +3362,7 @@ NonnullRefPtr<ImportStatement> Parser::parse_import_statement(Program& program)
expected("import clauses");
}

auto from_statement = consume(TokenType::Identifier).value();
auto from_statement = consume(TokenType::Identifier).original_value();
if (from_statement != "from"sv)
syntax_error(String::formatted("Expected 'from' got {}", from_statement));

Expand Down
2 changes: 2 additions & 0 deletions Userland/Libraries/LibJS/Parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ class Parser {
bool try_parse_arrow_function_expression_failed_at_position(const Position&) const;
void set_try_parse_arrow_function_expression_failed_at_position(const Position&, bool);

bool match_invalid_escaped_keyword() const;

struct RulePosition {
AK_MAKE_NONCOPYABLE(RulePosition);
AK_MAKE_NONMOVABLE(RulePosition);
Expand Down
22 changes: 22 additions & 0 deletions Userland/Libraries/LibJS/Tests/labels.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ test("can use certain 'keywords' as labels", () => {
expect(`const: { break const; }`).not.toEval();
});

test("can use certain 'keywords' even in strict mode", () => {
"use strict";

let i = 0;
await: {
i++;
break await;
expect().fail();
}

async: {
i++;
break async;
expect().fail();
}
expect(i).toBe(2);

expect(`'use strict'; let: { break let; }`).not.toEval();

expect(`'use strict'; l\u0065t: { break l\u0065t; }`).not.toEval();
});

test("invalid label usage", () => {
expect(() =>
eval(`
Expand Down

0 comments on commit 4428e49

Please sign in to comment.