[parser] Correctly handle invalid escapes in adjacent template tokens.
A previous patch lifting the restriction on invalid escape sequences in tagged templates had a bug when two template tokens appeared immediately adject to each other. This moves invalid escape information from the tokenizer state proper into the TokenDesc, preventing the overwriting which caused this issue. Previous CL is at https://codereview.chromium.org/2665513002 BUG=v8:6029,v8:5546 Review-Url: https://codereview.chromium.org/2724003006 Cr-Commit-Position: refs/heads/master@{#43596}
This commit is contained in:
parent
3a20c322bb
commit
baa74e89b6
@ -797,10 +797,12 @@ class ParserBase {
|
||||
}
|
||||
|
||||
// Checks if an octal literal or an invalid hex or unicode escape sequence
|
||||
// appears in a template literal. In the presence of such, either
|
||||
// returns false or reports an error, depending on should_throw. Otherwise
|
||||
// returns true.
|
||||
// appears in the current template literal token. In the presence of such,
|
||||
// either returns false or reports an error, depending on should_throw.
|
||||
// Otherwise returns true.
|
||||
inline bool CheckTemplateEscapes(bool should_throw, bool* ok) {
|
||||
DCHECK(scanner()->current_token() == Token::TEMPLATE_SPAN ||
|
||||
scanner()->current_token() == Token::TEMPLATE_TAIL);
|
||||
if (!scanner()->has_invalid_template_escape()) {
|
||||
return true;
|
||||
}
|
||||
@ -811,7 +813,6 @@ class ParserBase {
|
||||
scanner()->invalid_template_escape_message());
|
||||
*ok = false;
|
||||
}
|
||||
scanner()->clear_invalid_template_escape();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,7 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Scoped helper for saving & restoring scanner error state.
|
||||
// This is used for tagged template literals, in which normally forbidden
|
||||
// escape sequences are allowed.
|
||||
class ErrorState {
|
||||
class Scanner::ErrorState {
|
||||
public:
|
||||
ErrorState(MessageTemplate::Template* message_stack,
|
||||
Scanner::Location* location_stack)
|
||||
@ -31,7 +28,7 @@ class ErrorState {
|
||||
location_stack_(location_stack),
|
||||
old_location_(*location_stack) {
|
||||
*message_stack_ = MessageTemplate::kNone;
|
||||
*location_stack_ = Scanner::Location::invalid();
|
||||
*location_stack_ = Location::invalid();
|
||||
}
|
||||
|
||||
~ErrorState() {
|
||||
@ -39,17 +36,16 @@ class ErrorState {
|
||||
*location_stack_ = old_location_;
|
||||
}
|
||||
|
||||
void MoveErrorTo(MessageTemplate::Template* message_dest,
|
||||
Scanner::Location* location_dest) {
|
||||
void MoveErrorTo(TokenDesc* dest) {
|
||||
if (*message_stack_ == MessageTemplate::kNone) {
|
||||
return;
|
||||
}
|
||||
if (*message_dest == MessageTemplate::kNone) {
|
||||
*message_dest = *message_stack_;
|
||||
*location_dest = *location_stack_;
|
||||
if (dest->invalid_template_escape_message == MessageTemplate::kNone) {
|
||||
dest->invalid_template_escape_message = *message_stack_;
|
||||
dest->invalid_template_escape_location = *location_stack_;
|
||||
}
|
||||
*message_stack_ = MessageTemplate::kNone;
|
||||
*location_stack_ = Scanner::Location::invalid();
|
||||
*location_stack_ = Location::invalid();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -397,6 +393,7 @@ Token::Value Scanner::Next() {
|
||||
next_.location.end_pos = pos + 1;
|
||||
next_.literal_chars = nullptr;
|
||||
next_.raw_literal_chars = nullptr;
|
||||
next_.invalid_template_escape_message = MessageTemplate::kNone;
|
||||
Advance();
|
||||
return current_.token;
|
||||
}
|
||||
@ -609,6 +606,7 @@ Token::Value Scanner::ScanHtmlComment() {
|
||||
void Scanner::Scan() {
|
||||
next_.literal_chars = NULL;
|
||||
next_.raw_literal_chars = NULL;
|
||||
next_.invalid_template_escape_message = MessageTemplate::kNone;
|
||||
Token::Value token;
|
||||
do {
|
||||
// Remember the position of the next token
|
||||
@ -889,6 +887,8 @@ void Scanner::SanityCheckTokenDesc(const TokenDesc& token) const {
|
||||
// - TEMPLATE_*: need both literal + raw literal chars.
|
||||
// - IDENTIFIERS, STRINGS, etc.: need a literal, but no raw literal.
|
||||
// - all others: should have neither.
|
||||
// Furthermore, only TEMPLATE_* tokens can have a
|
||||
// invalid_template_escape_message.
|
||||
|
||||
switch (token.token) {
|
||||
case Token::UNINITIALIZED:
|
||||
@ -909,10 +909,12 @@ void Scanner::SanityCheckTokenDesc(const TokenDesc& token) const {
|
||||
case Token::STRING:
|
||||
DCHECK_NOT_NULL(token.literal_chars);
|
||||
DCHECK_NULL(token.raw_literal_chars);
|
||||
DCHECK_EQ(token.invalid_template_escape_message, MessageTemplate::kNone);
|
||||
break;
|
||||
default:
|
||||
DCHECK_NULL(token.literal_chars);
|
||||
DCHECK_NULL(token.raw_literal_chars);
|
||||
DCHECK_EQ(token.invalid_template_escape_message, MessageTemplate::kNone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1117,10 +1119,8 @@ Token::Value Scanner::ScanTemplateSpan() {
|
||||
DCHECK_EQ(!success, has_error());
|
||||
// For templates, invalid escape sequence checking is handled in the
|
||||
// parser.
|
||||
scanner_error_state.MoveErrorTo(&invalid_template_escape_message_,
|
||||
&invalid_template_escape_location_);
|
||||
octal_error_state.MoveErrorTo(&invalid_template_escape_message_,
|
||||
&invalid_template_escape_location_);
|
||||
scanner_error_state.MoveErrorTo(&next_);
|
||||
octal_error_state.MoveErrorTo(&next_);
|
||||
}
|
||||
} else if (c < 0) {
|
||||
// Unterminated template literal
|
||||
@ -1736,7 +1736,9 @@ void Scanner::SeekNext(size_t position) {
|
||||
// 1, Reset the current_, next_ and next_next_ tokens
|
||||
// (next_ + next_next_ will be overwrittem by Next(),
|
||||
// current_ will remain unchanged, so overwrite it fully.)
|
||||
current_ = {{0, 0}, nullptr, nullptr, 0, Token::UNINITIALIZED};
|
||||
current_ = {
|
||||
{0, 0}, nullptr, nullptr, 0, Token::UNINITIALIZED, MessageTemplate::kNone,
|
||||
{0, 0}};
|
||||
next_.token = Token::UNINITIALIZED;
|
||||
next_next_.token = Token::UNINITIALIZED;
|
||||
// 2, reset the source to the desired position,
|
||||
|
@ -215,19 +215,15 @@ class Scanner {
|
||||
Location error_location() const { return scanner_error_location_; }
|
||||
|
||||
bool has_invalid_template_escape() const {
|
||||
return invalid_template_escape_message_ != MessageTemplate::kNone;
|
||||
return current_.invalid_template_escape_message != MessageTemplate::kNone;
|
||||
}
|
||||
MessageTemplate::Template invalid_template_escape_message() const {
|
||||
return invalid_template_escape_message_;
|
||||
DCHECK(has_invalid_template_escape());
|
||||
return current_.invalid_template_escape_message;
|
||||
}
|
||||
Location invalid_template_escape_location() const {
|
||||
return invalid_template_escape_location_;
|
||||
}
|
||||
|
||||
void clear_invalid_template_escape() {
|
||||
DCHECK(has_invalid_template_escape());
|
||||
invalid_template_escape_message_ = MessageTemplate::kNone;
|
||||
invalid_template_escape_location_ = Location::invalid();
|
||||
return current_.invalid_template_escape_location;
|
||||
}
|
||||
|
||||
// Similar functions for the upcoming token.
|
||||
@ -345,6 +341,11 @@ class Scanner {
|
||||
bool FoundHtmlComment() const { return found_html_comment_; }
|
||||
|
||||
private:
|
||||
// Scoped helper for saving & restoring scanner error state.
|
||||
// This is used for tagged template literals, in which normally forbidden
|
||||
// escape sequences are allowed.
|
||||
class ErrorState;
|
||||
|
||||
// Scoped helper for literal recording. Automatically drops the literal
|
||||
// if aborting the scanning before it's complete.
|
||||
class LiteralScope {
|
||||
@ -457,6 +458,8 @@ class Scanner {
|
||||
LiteralBuffer* raw_literal_chars;
|
||||
uint32_t smi_value_;
|
||||
Token::Value token;
|
||||
MessageTemplate::Template invalid_template_escape_message;
|
||||
Location invalid_template_escape_location;
|
||||
};
|
||||
|
||||
static const int kCharacterLookaheadBufferSize = 1;
|
||||
@ -475,15 +478,17 @@ class Scanner {
|
||||
current_.token = Token::UNINITIALIZED;
|
||||
current_.literal_chars = NULL;
|
||||
current_.raw_literal_chars = NULL;
|
||||
current_.invalid_template_escape_message = MessageTemplate::kNone;
|
||||
next_.token = Token::UNINITIALIZED;
|
||||
next_.literal_chars = NULL;
|
||||
next_.raw_literal_chars = NULL;
|
||||
next_.invalid_template_escape_message = MessageTemplate::kNone;
|
||||
next_next_.token = Token::UNINITIALIZED;
|
||||
next_next_.literal_chars = NULL;
|
||||
next_next_.raw_literal_chars = NULL;
|
||||
next_next_.invalid_template_escape_message = MessageTemplate::kNone;
|
||||
found_html_comment_ = false;
|
||||
scanner_error_ = MessageTemplate::kNone;
|
||||
invalid_template_escape_message_ = MessageTemplate::kNone;
|
||||
}
|
||||
|
||||
void ReportScannerError(const Location& location,
|
||||
@ -774,9 +779,6 @@ class Scanner {
|
||||
|
||||
MessageTemplate::Template scanner_error_;
|
||||
Location scanner_error_location_;
|
||||
|
||||
MessageTemplate::Template invalid_template_escape_message_;
|
||||
Location invalid_template_escape_location_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -7161,6 +7161,12 @@ TEST(TemplateEscapesPositiveTests) {
|
||||
"tag`\\u{110000}${0}right`",
|
||||
"tag`left${0}\\u{110000}`",
|
||||
"tag`left${0}\\u{110000}${1}right`",
|
||||
"tag` ${tag`\\u`}`",
|
||||
"tag` ``\\u`",
|
||||
"tag`\\u`` `",
|
||||
"tag`\\u``\\u`",
|
||||
"` ${tag`\\u`}`",
|
||||
"` ``\\u`",
|
||||
NULL};
|
||||
// clang-format on
|
||||
|
||||
@ -7239,6 +7245,8 @@ TEST(TemplateEscapesNegativeTests) {
|
||||
"`left${0}\\u{110000}`",
|
||||
"`left${0}\\u{110000}${1}right`",
|
||||
"`\\1``\\2`",
|
||||
"tag` ${`\\u`}`",
|
||||
"`\\u```",
|
||||
NULL};
|
||||
// clang-format on
|
||||
|
||||
|
@ -783,3 +783,90 @@ check({
|
||||
1
|
||||
]
|
||||
})`left${0}\u{110000}${1}right`;
|
||||
|
||||
|
||||
|
||||
function checkMultiple(expectedArray) {
|
||||
let results = [];
|
||||
return function consume(strs, ...args) {
|
||||
if (typeof strs === 'undefined') {
|
||||
assertArrayEquals(expectedArray, results);
|
||||
} else {
|
||||
results.push({cooked: strs, raw: strs.raw, exprs: args});
|
||||
return consume;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
checkMultiple([{
|
||||
'cooked': [
|
||||
undefined
|
||||
],
|
||||
'raw': [
|
||||
'\\u',
|
||||
],
|
||||
'exprs': []
|
||||
}, {
|
||||
'cooked': [
|
||||
undefined
|
||||
],
|
||||
'raw': [
|
||||
'\\u',
|
||||
],
|
||||
'exprs': []
|
||||
}])`\u``\u`();
|
||||
|
||||
checkMultiple([{
|
||||
'cooked': [
|
||||
' '
|
||||
],
|
||||
'raw': [
|
||||
' ',
|
||||
],
|
||||
'exprs': []
|
||||
}, {
|
||||
'cooked': [
|
||||
undefined
|
||||
],
|
||||
'raw': [
|
||||
'\\u',
|
||||
],
|
||||
'exprs': []
|
||||
}])` ``\u`();
|
||||
|
||||
checkMultiple([{
|
||||
'cooked': [
|
||||
undefined
|
||||
],
|
||||
'raw': [
|
||||
'\\u',
|
||||
],
|
||||
'exprs': []
|
||||
}, {
|
||||
'cooked': [
|
||||
' '
|
||||
],
|
||||
'raw': [
|
||||
' ',
|
||||
],
|
||||
'exprs': []
|
||||
}])`\u`` `();
|
||||
|
||||
checkMultiple([{
|
||||
'cooked': [
|
||||
' '
|
||||
],
|
||||
'raw': [
|
||||
' ',
|
||||
],
|
||||
'exprs': []
|
||||
}, {
|
||||
'cooked': [
|
||||
' '
|
||||
],
|
||||
'raw': [
|
||||
' ',
|
||||
],
|
||||
'exprs': []
|
||||
}])` `` `();
|
||||
|
Loading…
Reference in New Issue
Block a user