From e423d1bcedc24803eaeeed4c72cc84fe9a938f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 21 Jun 2024 17:49:43 +0900 Subject: [PATCH] feat(common): Add fallible methods to `swc_common::SourceMap` (#9090) **Description:** This PR will make `swc_ecma_codegen` not `panic` for invalid spans. --- crates/swc_common/src/source_map.rs | 92 ++++++++++++++++++++--------- crates/swc_common/src/syntax_pos.rs | 23 ++++++++ 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/crates/swc_common/src/source_map.rs b/crates/swc_common/src/source_map.rs index 30fc3eb1ead7..0283d67a3017 100644 --- a/crates/swc_common/src/source_map.rs +++ b/crates/swc_common/src/source_map.rs @@ -276,8 +276,13 @@ impl SourceMap { /// Lookup source information about a BytePos pub fn lookup_char_pos(&self, pos: BytePos) -> Loc { - let fm = self.lookup_source_file(pos); - self.lookup_char_pos_with(fm, pos) + self.try_lookup_char_pos(pos).unwrap() + } + + /// Lookup source information about a BytePos + pub fn try_lookup_char_pos(&self, pos: BytePos) -> Result { + let fm = self.try_lookup_source_file(pos)?; + self.try_lookup_char_pos_with(fm, pos) } /// Lookup source information about a BytePos @@ -287,6 +292,20 @@ impl SourceMap { /// api. #[doc(hidden)] pub fn lookup_char_pos_with(&self, fm: Lrc, pos: BytePos) -> Loc { + self.try_lookup_char_pos_with(fm, pos).unwrap() + } + + /// Lookup source information about a BytePos + /// + /// + /// This method exists only for optimization and it's not part of public + /// api. + #[doc(hidden)] + pub fn try_lookup_char_pos_with( + &self, + fm: Lrc, + pos: BytePos, + ) -> Result { let line_info = self.lookup_line_with(fm, pos); match line_info { Ok(SourceFileAndLine { sf: f, line: a }) => { @@ -333,15 +352,15 @@ impl SourceMap { debug!("byte is on line: {}", line); } // assert!(chpos >= linechpos); - Loc { + Ok(Loc { file: f, line, col, col_display, - } + }) } Err(f) => { - let chpos = self.bytepos_to_file_charpos(pos); + let chpos = self.bytepos_to_file_charpos(pos)?; let col_display = { let end_width_idx = f @@ -354,19 +373,19 @@ impl SourceMap { .sum(); chpos.0 - end_width_idx + non_narrow }; - Loc { + Ok(Loc { file: f, line: 0, col: chpos, col_display, - } + }) } } } /// If the relevant source_file is empty, we don't return a line number. pub fn lookup_line(&self, pos: BytePos) -> Result> { - let f = self.lookup_source_file(pos); + let f = self.try_lookup_source_file(pos).unwrap(); self.lookup_line_with(f, pos) } @@ -559,8 +578,8 @@ impl SourceMap { return Err(Box::new(SpanSnippetError::DummyBytePos)); } - let local_begin = self.lookup_byte_offset(sp.lo()); - let local_end = self.lookup_byte_offset(sp.hi()); + let local_begin = self.try_lookup_byte_offset(sp.lo())?; + let local_end = self.try_lookup_byte_offset(sp.hi())?; if local_begin.sf.start_pos != local_end.sf.start_pos { Err(Box::new(SpanSnippetError::DistinctSources( @@ -947,16 +966,25 @@ impl SourceMap { /// For a global BytePos compute the local offset within the containing /// SourceFile pub fn lookup_byte_offset(&self, bpos: BytePos) -> SourceFileAndBytePos { - let sf = self.lookup_source_file(bpos); + self.try_lookup_byte_offset(bpos).unwrap() + } + + /// For a global BytePos compute the local offset within the containing + /// SourceFile + pub fn try_lookup_byte_offset( + &self, + bpos: BytePos, + ) -> Result { + let sf = self.try_lookup_source_file(bpos)?; let offset = bpos - sf.start_pos; - SourceFileAndBytePos { sf, pos: offset } + Ok(SourceFileAndBytePos { sf, pos: offset }) } /// Converts an absolute BytePos to a CharPos relative to the source_file. - fn bytepos_to_file_charpos(&self, bpos: BytePos) -> CharPos { - let map = self.lookup_source_file(bpos); + fn bytepos_to_file_charpos(&self, bpos: BytePos) -> Result { + let map = self.try_lookup_source_file(bpos)?; - self.bytepos_to_file_charpos_with(&map, bpos) + Ok(self.bytepos_to_file_charpos_with(&map, bpos)) } fn bytepos_to_file_charpos_with(&self, map: &SourceFile, bpos: BytePos) -> CharPos { @@ -1086,17 +1114,23 @@ impl SourceMap { /// This is not a public api. #[doc(hidden)] pub fn lookup_source_file(&self, pos: BytePos) -> Lrc { + self.try_lookup_source_file(pos).unwrap() + } + + /// Return the index of the source_file (in self.files) which contains pos. + /// + /// This is not a public api. + #[doc(hidden)] + pub fn try_lookup_source_file( + &self, + pos: BytePos, + ) -> Result, SourceMapLookupError> { let files = self.files.borrow(); let files = &files.source_files; let fm = Self::lookup_source_file_in(files, pos); match fm { - Some(fm) => fm, - None => { - panic!( - "position {} does not resolve to a source location", - pos.to_usize() - ); - } + Some(fm) => Ok(fm), + None => Err(SourceMapLookupError::NoFileFor(pos)), } } @@ -1263,7 +1297,7 @@ impl SourceMap { let f = match cur_file { Some(ref f) if f.start_pos <= pos && pos < f.end_pos => f, _ => { - f = self.lookup_source_file(pos); + f = self.try_lookup_source_file(pos).unwrap(); if config.skip(&f.name) { continue; } @@ -1543,10 +1577,10 @@ mod tests { // Test bytepos_to_file_charpos let sm = init_source_map(); - let cp1 = sm.bytepos_to_file_charpos(BytePos(23)); + let cp1 = sm.bytepos_to_file_charpos(BytePos(23)).unwrap(); assert_eq!(cp1, CharPos(22)); - let cp2 = sm.bytepos_to_file_charpos(BytePos(26)); + let cp2 = sm.bytepos_to_file_charpos(BytePos(26)).unwrap(); assert_eq!(cp2, CharPos(0)); } @@ -1585,16 +1619,16 @@ mod tests { // Test bytepos_to_file_charpos in the presence of multi-byte chars let sm = init_source_map_mbc(); - let cp1 = sm.bytepos_to_file_charpos(BytePos(4)); + let cp1 = sm.bytepos_to_file_charpos(BytePos(4)).unwrap(); assert_eq!(cp1, CharPos(3)); - let cp2 = sm.bytepos_to_file_charpos(BytePos(7)); + let cp2 = sm.bytepos_to_file_charpos(BytePos(7)).unwrap(); assert_eq!(cp2, CharPos(4)); - let cp3 = sm.bytepos_to_file_charpos(BytePos(57)); + let cp3 = sm.bytepos_to_file_charpos(BytePos(57)).unwrap(); assert_eq!(cp3, CharPos(12)); - let cp4 = sm.bytepos_to_file_charpos(BytePos(62)); + let cp4 = sm.bytepos_to_file_charpos(BytePos(62)).unwrap(); assert_eq!(cp4, CharPos(15)); } diff --git a/crates/swc_common/src/syntax_pos.rs b/crates/swc_common/src/syntax_pos.rs index 0e5bbbd5043c..dd18d915ebc8 100644 --- a/crates/swc_common/src/syntax_pos.rs +++ b/crates/swc_common/src/syntax_pos.rs @@ -1406,6 +1406,22 @@ pub enum SpanSnippetError { DistinctSources(DistinctSources), MalformedForSourcemap(MalformedSourceMapPositions), SourceNotAvailable { filename: FileName }, + LookupFailed(SourceMapLookupError), +} + +/// An error type for looking up source maps. +/// +/// +/// This type is small. +#[derive(Clone, PartialEq, Eq, Debug)] +#[cfg_attr( + any(feature = "rkyv-impl"), + derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize) +)] +#[cfg_attr(feature = "rkyv-impl", archive(check_bytes))] +#[cfg_attr(feature = "rkyv-impl", archive_attr(repr(u32)))] +pub enum SourceMapLookupError { + NoFileFor(BytePos), } #[derive(Clone, PartialEq, Eq, Debug)] @@ -1453,6 +1469,13 @@ fn lookup_line(lines: &[BytePos], pos: BytePos) -> isize { } } +impl From for Box { + #[cold] + fn from(err: SourceMapLookupError) -> Self { + Box::new(SpanSnippetError::LookupFailed(err)) + } +} + #[cfg(test)] mod tests { use super::{lookup_line, BytePos, Span};