1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 14:18:12 +00:00

LibJS: Allow invalid string in tagged template literals

Since tagged template literals can inspect the raw string it is not a
syntax error to have invalid escapes. However the cooked value should be
`undefined`.
We accomplish this by tracking whether parse_string_literal
fails and then using a NullLiteral (since UndefinedLiteral is not a
thing) and finally converting null in tagged template execution to
undefined.
This commit is contained in:
davidot 2022-08-17 02:04:27 +02:00 committed by Linus Groh
parent 0f9434a02c
commit e5adc51e27
5 changed files with 67 additions and 8 deletions

View file

@ -1811,16 +1811,18 @@ NonnullRefPtr<ArrayExpression> Parser::parse_array_expression()
return create_ast_node<ArrayExpression>({ m_state.current_token.filename(), rule_start.position(), position() }, move(elements));
}
NonnullRefPtr<StringLiteral> Parser::parse_string_literal(Token const& token, bool in_template_literal)
NonnullRefPtr<StringLiteral> Parser::parse_string_literal(Token const& token, StringLiteralType string_literal_type, bool* contains_invalid_escape)
{
auto rule_start = push_start();
auto status = Token::StringValueStatus::Ok;
auto string = token.string_value(status);
// NOTE: Tagged templates should not fail on invalid strings as their raw contents can still be accessed.
if (status != Token::StringValueStatus::Ok) {
String message;
if (status == Token::StringValueStatus::LegacyOctalEscapeSequence) {
m_state.string_legacy_octal_escape_sequence_in_scope = true;
if (in_template_literal)
// It is a Syntax Error if the [Tagged] parameter was not set and Template{Head, Middle, Tail} Contains NotEscapeSequence.
if (string_literal_type != StringLiteralType::Normal)
message = "Octal escape sequence not allowed in template literal";
else if (m_state.strict_mode)
message = "Octal escape sequence in string literal not allowed in strict mode";
@ -1833,11 +1835,17 @@ NonnullRefPtr<StringLiteral> Parser::parse_string_literal(Token const& token, bo
VERIFY_NOT_REACHED();
}
if (!message.is_empty())
syntax_error(message, Position { token.line_number(), token.line_column() });
if (!message.is_empty()) {
if (contains_invalid_escape != nullptr) {
VERIFY(string_literal_type == StringLiteralType::TaggedTemplate);
*contains_invalid_escape = true;
} else {
syntax_error(message, Position { token.line_number(), token.line_column() });
}
}
}
auto is_use_strict_directive = !in_template_literal && (token.value() == "'use strict'" || token.value() == "\"use strict\"");
auto is_use_strict_directive = string_literal_type == StringLiteralType::Normal && (token.value() == "'use strict'" || token.value() == "\"use strict\"");
return create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, string, is_use_strict_directive);
}
@ -1863,7 +1871,15 @@ NonnullRefPtr<TemplateLiteral> Parser::parse_template_literal(bool is_tagged)
while (!done() && !match(TokenType::TemplateLiteralEnd) && !match(TokenType::UnterminatedTemplateLiteral)) {
if (match(TokenType::TemplateLiteralString)) {
auto token = consume();
expressions.append(parse_string_literal(token, true));
bool contains_invalid_escape = false;
auto parsed_string_value = parse_string_literal(token,
is_tagged ? StringLiteralType::TaggedTemplate : StringLiteralType::NonTaggedTemplate,
is_tagged ? &contains_invalid_escape : nullptr);
// An invalid string leads to a cooked value of `undefined` but still gives the raw string.
if (contains_invalid_escape)
expressions.append(create_ast_node<NullLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }));
else
expressions.append(move(parsed_string_value));
if (is_tagged)
raw_strings.append(create_ast_node<StringLiteral>({ m_state.current_token.filename(), rule_start.position(), position() }, token.raw_template_value()));
} else if (match(TokenType::TemplateLiteralExprStart)) {
@ -4011,7 +4027,7 @@ FlyString Parser::consume_string_value()
{
VERIFY(match(TokenType::StringLiteral));
auto string_token = consume();
FlyString value = parse_string_literal(string_token, false)->value();
FlyString value = parse_string_literal(string_token)->value();
// This also checks IsStringWellFormedUnicode which makes sure there is no unpaired surrogate
// Surrogates are at least 3 bytes