Skip to content

Commit c1d12d6

Browse files
authored
perf(es/plugin): Make analyze_source_file lazy, again (#9732)
**Description:** This reverts commit 772f023. **Related issue:** - Reverts #9717
1 parent 9b74ccd commit c1d12d6

File tree

7 files changed

+173
-40
lines changed

7 files changed

+173
-40
lines changed

Diff for: .changeset/calm-crabs-lie.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
swc_common: major
3+
---
4+
5+
perf(es/plugin): Make `analyze_source_file` lazy, again

Diff for: crates/swc_common/src/cache.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use std::ops::Deref;
2+
3+
use once_cell::sync::OnceCell;
4+
5+
/// Wrapper for [OnceCell] with support for [rkyv].
6+
#[derive(Clone, Debug)]
7+
pub struct CacheCell<T>(OnceCell<T>);
8+
9+
impl<T> Deref for CacheCell<T> {
10+
type Target = OnceCell<T>;
11+
12+
fn deref(&self) -> &Self::Target {
13+
&self.0
14+
}
15+
}
16+
17+
impl<T> CacheCell<T> {
18+
pub fn new() -> Self {
19+
Self(OnceCell::new())
20+
}
21+
}
22+
23+
impl<T> From<T> for CacheCell<T> {
24+
fn from(value: T) -> Self {
25+
Self(OnceCell::from(value))
26+
}
27+
}
28+
29+
impl<T> Default for CacheCell<T> {
30+
fn default() -> Self {
31+
Self::new()
32+
}
33+
}
34+
35+
#[cfg(feature = "rkyv-impl")]
36+
mod rkyv_impl {
37+
use std::hint::unreachable_unchecked;
38+
39+
use rancor::Fallible;
40+
use rkyv::{
41+
munge::munge, option::ArchivedOption, traits::NoUndef, Archive, Deserialize, Place,
42+
Serialize,
43+
};
44+
45+
use super::*;
46+
47+
#[allow(dead_code)]
48+
#[repr(u8)]
49+
enum ArchivedOptionTag {
50+
None,
51+
Some,
52+
}
53+
54+
// SAFETY: `ArchivedOptionTag` is `repr(u8)` and so always consists of a single
55+
// well-defined byte.
56+
unsafe impl NoUndef for ArchivedOptionTag {}
57+
58+
#[repr(C)]
59+
struct ArchivedOptionVariantNone(ArchivedOptionTag);
60+
61+
#[repr(C)]
62+
struct ArchivedOptionVariantSome<T>(ArchivedOptionTag, T);
63+
64+
impl<T: Archive> Archive for CacheCell<T> {
65+
type Archived = ArchivedOption<T::Archived>;
66+
type Resolver = Option<T::Resolver>;
67+
68+
fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>) {
69+
match resolver {
70+
None => {
71+
let out = unsafe { out.cast_unchecked::<ArchivedOptionVariantNone>() };
72+
munge!(let ArchivedOptionVariantNone(tag) = out);
73+
tag.write(ArchivedOptionTag::None);
74+
}
75+
Some(resolver) => {
76+
let out =
77+
unsafe { out.cast_unchecked::<ArchivedOptionVariantSome<T::Archived>>() };
78+
munge!(let ArchivedOptionVariantSome(tag, out_value) = out);
79+
tag.write(ArchivedOptionTag::Some);
80+
81+
let value = if let Some(value) = self.get() {
82+
value
83+
} else {
84+
unsafe {
85+
unreachable_unchecked();
86+
}
87+
};
88+
89+
value.resolve(resolver, out_value);
90+
}
91+
}
92+
}
93+
}
94+
95+
impl<T: Serialize<S>, S: Fallible + ?Sized> Serialize<S> for CacheCell<T> {
96+
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
97+
self.get()
98+
.map(|value| value.serialize(serializer))
99+
.transpose()
100+
}
101+
}
102+
103+
impl<T, D> Deserialize<CacheCell<T>, D> for ArchivedOption<T::Archived>
104+
where
105+
T: Archive,
106+
T::Archived: Deserialize<T, D>,
107+
D: Fallible + ?Sized,
108+
{
109+
fn deserialize(&self, deserializer: &mut D) -> Result<CacheCell<T>, D::Error> {
110+
Ok(match self {
111+
ArchivedOption::Some(value) => CacheCell::from(value.deserialize(deserializer)?),
112+
ArchivedOption::None => CacheCell::new(),
113+
})
114+
}
115+
}
116+
}

Diff for: crates/swc_common/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,13 @@ pub use self::{
5151
source_map::{FileLines, FileLoader, FilePathMapping, SourceMap, SpanSnippetError},
5252
syntax_pos::LineCol,
5353
};
54-
#[doc(hidden)]
55-
pub mod private;
5654

5755
/// A trait for ast nodes.
5856
pub trait AstNode: Debug + PartialEq + Clone + Spanned {
5957
const TYPE: &'static str;
6058
}
6159

60+
pub mod cache;
6261
pub mod collections;
6362
pub mod comments;
6463
mod eq;
@@ -68,6 +67,8 @@ pub mod iter;
6867
pub mod pass;
6968
pub mod plugin;
7069
mod pos;
70+
#[doc(hidden)]
71+
pub mod private;
7172
mod rustc_data_structures;
7273
pub mod serializer;
7374
pub mod source_map;

Diff for: crates/swc_common/src/source_map.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,11 @@ impl SourceMap {
316316
let line_info = self.lookup_line_with(fm, pos);
317317
match line_info {
318318
Ok(SourceFileAndLine { sf: f, line: a }) => {
319+
let analysis = f.analyze();
319320
let chpos = self.bytepos_to_file_charpos_with(&f, pos);
320321

321322
let line = a + 1; // Line numbers start at 1
322-
let linebpos = f.lines[a];
323+
let linebpos = f.analyze().lines[a];
323324
assert!(
324325
pos >= linebpos,
325326
"{}: bpos = {:?}; linebpos = {:?};",
@@ -332,16 +333,17 @@ impl SourceMap {
332333
let col = chpos - linechpos;
333334

334335
let col_display = {
335-
let start_width_idx = f
336+
let start_width_idx = analysis
336337
.non_narrow_chars
337338
.binary_search_by_key(&linebpos, |x| x.pos())
338339
.unwrap_or_else(|x| x);
339-
let end_width_idx = f
340+
let end_width_idx = analysis
340341
.non_narrow_chars
341342
.binary_search_by_key(&pos, |x| x.pos())
342343
.unwrap_or_else(|x| x);
343344
let special_chars = end_width_idx - start_width_idx;
344-
let non_narrow: usize = f.non_narrow_chars[start_width_idx..end_width_idx]
345+
let non_narrow: usize = analysis.non_narrow_chars
346+
[start_width_idx..end_width_idx]
345347
.iter()
346348
.map(|x| x.width())
347349
.sum();
@@ -367,14 +369,15 @@ impl SourceMap {
367369
})
368370
}
369371
Err(f) => {
372+
let analysis = f.analyze();
370373
let chpos = self.bytepos_to_file_charpos(pos)?;
371374

372375
let col_display = {
373-
let end_width_idx = f
376+
let end_width_idx = analysis
374377
.non_narrow_chars
375378
.binary_search_by_key(&pos, |x| x.pos())
376379
.unwrap_or_else(|x| x);
377-
let non_narrow: usize = f.non_narrow_chars[0..end_width_idx]
380+
let non_narrow: usize = analysis.non_narrow_chars[0..end_width_idx]
378381
.iter()
379382
.map(|x| x.width())
380383
.sum();
@@ -1028,11 +1031,11 @@ impl SourceMap {
10281031
) -> u32 {
10291032
let mut total_extra_bytes = state.total_extra_bytes;
10301033
let mut index = state.mbc_index;
1031-
1034+
let analysis = file.analyze();
10321035
if bpos >= state.pos {
1033-
let range = index..file.multibyte_chars.len();
1036+
let range = index..analysis.multibyte_chars.len();
10341037
for i in range {
1035-
let mbc = &file.multibyte_chars[i];
1038+
let mbc = &analysis.multibyte_chars[i];
10361039
debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
10371040
if mbc.pos >= bpos {
10381041
break;
@@ -1052,7 +1055,7 @@ impl SourceMap {
10521055
} else {
10531056
let range = 0..index;
10541057
for i in range.rev() {
1055-
let mbc = &file.multibyte_chars[i];
1058+
let mbc = &analysis.multibyte_chars[i];
10561059
debug!("{}-byte char at {:?}", mbc.bytes, mbc.pos);
10571060
if mbc.pos < bpos {
10581061
break;
@@ -1322,7 +1325,8 @@ impl SourceMap {
13221325
None => continue,
13231326
};
13241327

1325-
let linebpos = f.lines[line as usize];
1328+
let analysis = f.analyze();
1329+
let linebpos = analysis.lines[line as usize];
13261330
debug_assert!(
13271331
pos >= linebpos,
13281332
"{}: bpos = {:?}; linebpos = {:?};",

Diff for: crates/swc_common/src/syntax_pos.rs

+32-25
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use url::Url;
1616

1717
use self::hygiene::MarkData;
1818
pub use self::hygiene::{Mark, SyntaxContext};
19-
use crate::{rustc_data_structures::stable_hasher::StableHasher, sync::Lrc};
19+
use crate::{cache::CacheCell, rustc_data_structures::stable_hasher::StableHasher, sync::Lrc};
2020

2121
mod analyze_source_file;
2222
pub mod hygiene;
@@ -825,14 +825,10 @@ pub struct SourceFile {
825825
pub start_pos: BytePos,
826826
/// The end position of this source in the `SourceMap`
827827
pub end_pos: BytePos,
828-
/// Locations of lines beginnings in the source code
829-
pub lines: Vec<BytePos>,
830-
/// Locations of multi-byte characters in the source code
831-
pub multibyte_chars: Vec<MultiByteChar>,
832-
/// Width of characters that are not narrow in the source code
833-
pub non_narrow_chars: Vec<NonNarrowChar>,
834828
/// A hash of the filename, used for speeding up the incr. comp. hashing.
835829
pub name_hash: u128,
830+
831+
lazy: CacheCell<SourceFileAnalysis>,
836832
}
837833

838834
#[cfg_attr(
@@ -849,8 +845,6 @@ pub struct SourceFileAnalysis {
849845
pub multibyte_chars: Vec<MultiByteChar>,
850846
/// Width of characters that are not narrow in the source code
851847
pub non_narrow_chars: Vec<NonNarrowChar>,
852-
/// A hash of the filename, used for speeding up the incr. comp. hashing.
853-
pub name_hash: u128,
854848
}
855849

856850
impl fmt::Debug for SourceFile {
@@ -904,9 +898,6 @@ impl SourceFile {
904898
};
905899
let end_pos = start_pos.to_usize() + src.len();
906900

907-
let (lines, multibyte_chars, non_narrow_chars) =
908-
analyze_source_file::analyze_source_file(&src[..], start_pos);
909-
910901
SourceFile {
911902
name,
912903
name_was_remapped,
@@ -916,17 +907,16 @@ impl SourceFile {
916907
src_hash,
917908
start_pos,
918909
end_pos: SmallPos::from_usize(end_pos),
919-
lines,
920-
multibyte_chars,
921-
non_narrow_chars,
922910
name_hash,
911+
lazy: CacheCell::new(),
923912
}
924913
}
925914

926915
/// Return the BytePos of the beginning of the current line.
927916
pub fn line_begin_pos(&self, pos: BytePos) -> BytePos {
928917
let line_index = self.lookup_line(pos).unwrap();
929-
self.lines[line_index]
918+
let analysis = self.analyze();
919+
analysis.lines[line_index]
930920
}
931921

932922
/// Get a line from the list of pre-computed line-beginnings.
@@ -944,7 +934,8 @@ impl SourceFile {
944934
}
945935

946936
let begin = {
947-
let line = self.lines.get(line_number)?;
937+
let analysis = self.analyze();
938+
let line = analysis.lines.get(line_number)?;
948939
let begin: BytePos = *line - self.start_pos;
949940
begin.to_usize()
950941
};
@@ -961,20 +952,22 @@ impl SourceFile {
961952
}
962953

963954
pub fn count_lines(&self) -> usize {
964-
self.lines.len()
955+
let analysis = self.analyze();
956+
analysis.lines.len()
965957
}
966958

967959
/// Find the line containing the given position. The return value is the
968960
/// index into the `lines` array of this SourceFile, not the 1-based line
969961
/// number. If the `source_file` is empty or the position is located before
970962
/// the first line, `None` is returned.
971963
pub fn lookup_line(&self, pos: BytePos) -> Option<usize> {
972-
if self.lines.is_empty() {
964+
let analysis = self.analyze();
965+
if analysis.lines.is_empty() {
973966
return None;
974967
}
975968

976-
let line_index = lookup_line(&self.lines[..], pos);
977-
assert!(line_index < self.lines.len() as isize);
969+
let line_index = lookup_line(&analysis.lines, pos);
970+
assert!(line_index < analysis.lines.len() as isize);
978971
if line_index >= 0 {
979972
Some(line_index as usize)
980973
} else {
@@ -987,18 +980,32 @@ impl SourceFile {
987980
return (self.start_pos, self.end_pos);
988981
}
989982

990-
assert!(line_index < self.lines.len());
991-
if line_index == (self.lines.len() - 1) {
992-
(self.lines[line_index], self.end_pos)
983+
let analysis = self.analyze();
984+
985+
assert!(line_index < analysis.lines.len());
986+
if line_index == (analysis.lines.len() - 1) {
987+
(analysis.lines[line_index], self.end_pos)
993988
} else {
994-
(self.lines[line_index], self.lines[line_index + 1])
989+
(analysis.lines[line_index], analysis.lines[line_index + 1])
995990
}
996991
}
997992

998993
#[inline]
999994
pub fn contains(&self, byte_pos: BytePos) -> bool {
1000995
byte_pos >= self.start_pos && byte_pos <= self.end_pos
1001996
}
997+
998+
pub fn analyze(&self) -> &SourceFileAnalysis {
999+
self.lazy.get_or_init(|| {
1000+
let (lines, multibyte_chars, non_narrow_chars) =
1001+
analyze_source_file::analyze_source_file(&self.src[..], self.start_pos);
1002+
SourceFileAnalysis {
1003+
lines,
1004+
multibyte_chars,
1005+
non_narrow_chars,
1006+
}
1007+
})
1008+
}
10021009
}
10031010

10041011
/// Remove utf-8 BOM if any.

Diff for: crates/swc_error_reporters/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl SourceCode for MietteSourceCode<'_> {
131131
}
132132

133133
let loc = self.0.lookup_char_pos(span.lo());
134-
let line_count = loc.file.lines.len();
134+
let line_count = loc.file.analyze().lines.len();
135135

136136
let name = if self.1.skip_filename {
137137
None

Diff for: crates/swc_estree_compat/src/swcify/ctx.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct Context {
1212

1313
impl Context {
1414
fn locate_line_col(&self, loc: LineCol) -> BytePos {
15-
if let Some(&line_start) = self.fm.lines.get(loc.line) {
15+
if let Some(&line_start) = self.fm.analyze().lines.get(loc.line) {
1616
line_start + BytePos(loc.column as _)
1717
} else {
1818
BytePos(0)

0 commit comments

Comments
 (0)