diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index d5e62064eed08..61fa3ba69b438 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -58,7 +58,7 @@ impl<'a> ParserImpl<'a> { /// Get current template string pub(crate) fn cur_template_string(&self) -> Option<&'a str> { - self.lexer.get_template_string(self.token) + self.lexer.get_template_string(self.token.start) } /// Peek next token, returns EOF for final peek diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index c7b8f10ee58b0..78a2352250691 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -546,18 +546,26 @@ impl<'a> ParserImpl<'a> { _ => unreachable!(), }; - // `cooked = None` when template literal has invalid escape sequence - // This is matched by `is_valid_escape_sequence` in `Lexer::read_template_literal` - let cooked = self.cur_template_string(); - let lone_surrogates = self.cur_token().lone_surrogates; + // Get `raw` + let raw_span = self.cur_token().span(); + let mut raw = Atom::from( + &self.source_text[raw_span.start as usize + 1..(raw_span.end - end_offset) as usize], + ); - let cur_src = self.cur_src(); - let raw = &cur_src[1..cur_src.len() - end_offset as usize]; - let raw = Atom::from(if cooked.is_some() && raw.contains('\r') { - self.ast.str(&raw.cow_replace("\r\n", "\n").cow_replace('\r', "\n")) + // Get `cooked`. + // Also replace `\r` with `\n` in `raw`. + // If contains `\r`, then `escaped` must be `true` (because `\r` needs unescaping), + // so we can skip searching for `\r` in common case where contains no escapes. + let (cooked, lone_surrogates) = if self.cur_token().escaped { + // `cooked = None` when template literal has invalid escape sequence + let cooked = self.cur_template_string().map(Atom::from); + if cooked.is_some() && raw.contains('\r') { + raw = self.ast.atom(&raw.cow_replace("\r\n", "\n").cow_replace('\r', "\n")); + } + (cooked, self.cur_token().lone_surrogates) } else { - raw - }); + (Some(raw), false) + }; self.bump_any(); @@ -572,7 +580,7 @@ impl<'a> ParserImpl<'a> { let tail = matches!(cur_kind, Kind::TemplateTail | Kind::NoSubstitutionTemplate); self.ast.template_element_with_lone_surrogates( span, - TemplateElementValue { raw, cooked: cooked.map(Atom::from) }, + TemplateElementValue { raw, cooked }, tail, lone_surrogates, ) diff --git a/crates/oxc_parser/src/lexer/template.rs b/crates/oxc_parser/src/lexer/template.rs index c43f22a28e175..80ed89742085b 100644 --- a/crates/oxc_parser/src/lexer/template.rs +++ b/crates/oxc_parser/src/lexer/template.rs @@ -399,20 +399,8 @@ impl<'a> Lexer<'a> { self.token.escaped = true; } - pub(crate) fn get_template_string(&self, token: Token) -> Option<&'a str> { - if token.escaped { - return self.escaped_templates[&token.start]; - } - let raw = &self.source.whole()[token.start as usize..token.end as usize]; - Some(match token.kind { - Kind::NoSubstitutionTemplate | Kind::TemplateTail => { - &raw[1..raw.len() - 1] // omit surrounding quotes or leading "}" and trailing "`" - } - Kind::TemplateHead | Kind::TemplateMiddle => { - &raw[1..raw.len() - 2] // omit leading "`" or "}" and trailing "${" - } - _ => raw, - }) + pub(crate) fn get_template_string(&self, span_start: u32) -> Option<&'a str> { + self.escaped_templates[&span_start] } }