summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_span/src/source_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_span/src/source_map.rs')
-rw-r--r--compiler/rustc_span/src/source_map.rs217
1 files changed, 100 insertions, 117 deletions
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 983b2ab04..0b575c13a 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -14,16 +14,15 @@ pub use crate::*;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{Hash128, Hash64, StableHasher};
-use rustc_data_structures::sync::{
- AtomicU32, IntoDynSyncSend, Lrc, MappedReadGuard, ReadGuard, RwLock,
-};
+use rustc_data_structures::sync::{IntoDynSyncSend, Lrc, MappedReadGuard, ReadGuard, RwLock};
use std::cmp;
use std::hash::Hash;
use std::path::{self, Path, PathBuf};
-use std::sync::atomic::Ordering;
use std::fs;
use std::io;
+use std::io::BorrowedBuf;
+use std::io::Read;
#[cfg(test)]
mod tests;
@@ -101,10 +100,13 @@ pub trait FileLoader {
fn file_exists(&self, path: &Path) -> bool;
/// Read the contents of a UTF-8 file into memory.
+ /// This function must return a String because we normalize
+ /// source files, which may require resizing.
fn read_file(&self, path: &Path) -> io::Result<String>;
/// Read the contents of a potentially non-UTF-8 file into memory.
- fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>>;
+ /// We don't normalize binary files, so we can start in an Lrc.
+ fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>>;
}
/// A FileLoader that uses std::fs to load real files.
@@ -119,8 +121,45 @@ impl FileLoader for RealFileLoader {
fs::read_to_string(path)
}
- fn read_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
- fs::read(path)
+ fn read_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
+ let mut file = fs::File::open(path)?;
+ let len = file.metadata()?.len();
+
+ let mut bytes = Lrc::new_uninit_slice(len as usize);
+ let mut buf = BorrowedBuf::from(Lrc::get_mut(&mut bytes).unwrap());
+ match file.read_buf_exact(buf.unfilled()) {
+ Ok(()) => {}
+ Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
+ drop(bytes);
+ return fs::read(path).map(Vec::into);
+ }
+ Err(e) => return Err(e),
+ }
+ // SAFETY: If the read_buf_exact call returns Ok(()), then we have
+ // read len bytes and initialized the buffer.
+ let bytes = unsafe { bytes.assume_init() };
+
+ // At this point, we've read all the bytes that filesystem metadata reported exist.
+ // But we are not guaranteed to be at the end of the file, because we did not attempt to do
+ // a read with a non-zero-sized buffer and get Ok(0).
+ // So we do small read to a fixed-size buffer. If the read returns no bytes then we're
+ // already done, and we just return the Lrc we built above.
+ // If the read returns bytes however, we just fall back to reading into a Vec then turning
+ // that into an Lrc, losing our nice peak memory behavior. This fallback code path should
+ // be rarely exercised.
+
+ let mut probe = [0u8; 32];
+ let n = loop {
+ match file.read(&mut probe) {
+ Ok(0) => return Ok(bytes),
+ Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
+ Err(e) => return Err(e),
+ Ok(n) => break n,
+ }
+ };
+ let mut bytes: Vec<u8> = bytes.iter().copied().chain(probe[..n].iter().copied()).collect();
+ file.read_to_end(&mut bytes)?;
+ Ok(bytes.into())
}
}
@@ -174,9 +213,6 @@ pub(super) struct SourceMapFiles {
}
pub struct SourceMap {
- /// The address space below this value is currently used by the files in the source map.
- used_address_space: AtomicU32,
-
files: RwLock<SourceMapFiles>,
file_loader: IntoDynSyncSend<Box<dyn FileLoader + Sync + Send>>,
// This is used to apply the file path remapping as specified via
@@ -202,7 +238,6 @@ impl SourceMap {
hash_kind: SourceFileHashAlgorithm,
) -> SourceMap {
SourceMap {
- used_address_space: AtomicU32::new(0),
files: Default::default(),
file_loader: IntoDynSyncSend(file_loader),
path_mapping,
@@ -228,7 +263,7 @@ impl SourceMap {
///
/// Unlike `load_file`, guarantees that no normalization like BOM-removal
/// takes place.
- pub fn load_binary_file(&self, path: &Path) -> io::Result<Vec<u8>> {
+ pub fn load_binary_file(&self, path: &Path) -> io::Result<Lrc<[u8]>> {
let bytes = self.file_loader.read_binary_file(path)?;
// We need to add file to the `SourceMap`, so that it is present
@@ -254,26 +289,26 @@ impl SourceMap {
self.files.borrow().stable_id_to_source_file.get(&stable_id).cloned()
}
- fn allocate_address_space(&self, size: usize) -> Result<usize, OffsetOverflowError> {
- let size = u32::try_from(size).map_err(|_| OffsetOverflowError)?;
-
- loop {
- let current = self.used_address_space.load(Ordering::Relaxed);
- let next = current
- .checked_add(size)
- // Add one so there is some space between files. This lets us distinguish
- // positions in the `SourceMap`, even in the presence of zero-length files.
- .and_then(|next| next.checked_add(1))
- .ok_or(OffsetOverflowError)?;
-
- if self
- .used_address_space
- .compare_exchange(current, next, Ordering::Relaxed, Ordering::Relaxed)
- .is_ok()
- {
- return Ok(usize::try_from(current).unwrap());
- }
- }
+ fn register_source_file(
+ &self,
+ file_id: StableSourceFileId,
+ mut file: SourceFile,
+ ) -> Result<Lrc<SourceFile>, OffsetOverflowError> {
+ let mut files = self.files.borrow_mut();
+
+ file.start_pos = BytePos(if let Some(last_file) = files.source_files.last() {
+ // Add one so there is some space between files. This lets us distinguish
+ // positions in the `SourceMap`, even in the presence of zero-length files.
+ last_file.end_position().0.checked_add(1).ok_or(OffsetOverflowError)?
+ } else {
+ 0
+ });
+
+ let file = Lrc::new(file);
+ files.source_files.push(file.clone());
+ files.stable_id_to_source_file.insert(file_id, file.clone());
+
+ Ok(file)
}
/// Creates a new `SourceFile`.
@@ -297,32 +332,18 @@ impl SourceMap {
let (filename, _) = self.path_mapping.map_filename_prefix(&filename);
let file_id = StableSourceFileId::new_from_name(&filename, LOCAL_CRATE);
-
- let lrc_sf = match self.source_file_by_stable_id(file_id) {
- Some(lrc_sf) => lrc_sf,
+ match self.source_file_by_stable_id(file_id) {
+ Some(lrc_sf) => Ok(lrc_sf),
None => {
- let start_pos = self.allocate_address_space(src.len())?;
-
- let source_file = Lrc::new(SourceFile::new(
- filename,
- src,
- Pos::from_usize(start_pos),
- self.hash_kind,
- ));
+ let source_file = SourceFile::new(filename, src, self.hash_kind)?;
// Let's make sure the file_id we generated above actually matches
// the ID we generate for the SourceFile we just created.
debug_assert_eq!(StableSourceFileId::new(&source_file), file_id);
- let mut files = self.files.borrow_mut();
-
- files.source_files.push(source_file.clone());
- files.stable_id_to_source_file.insert(file_id, source_file.clone());
-
- source_file
+ self.register_source_file(file_id, source_file)
}
- };
- Ok(lrc_sf)
+ }
}
/// Allocates a new `SourceFile` representing a source file from an external
@@ -334,78 +355,37 @@ impl SourceMap {
filename: FileName,
src_hash: SourceFileHash,
name_hash: Hash128,
- source_len: usize,
+ source_len: u32,
cnum: CrateNum,
- file_local_lines: Lock<SourceFileLines>,
- mut file_local_multibyte_chars: Vec<MultiByteChar>,
- mut file_local_non_narrow_chars: Vec<NonNarrowChar>,
- mut file_local_normalized_pos: Vec<NormalizedPos>,
- original_start_pos: BytePos,
+ file_local_lines: FreezeLock<SourceFileLines>,
+ multibyte_chars: Vec<MultiByteChar>,
+ non_narrow_chars: Vec<NonNarrowChar>,
+ normalized_pos: Vec<NormalizedPos>,
metadata_index: u32,
) -> Lrc<SourceFile> {
- let start_pos = self
- .allocate_address_space(source_len)
- .expect("not enough address space for imported source file");
-
- let end_pos = Pos::from_usize(start_pos + source_len);
- let start_pos = Pos::from_usize(start_pos);
-
- // Translate these positions into the new global frame of reference,
- // now that the offset of the SourceFile is known.
- //
- // These are all unsigned values. `original_start_pos` may be larger or
- // smaller than `start_pos`, but `pos` is always larger than both.
- // Therefore, `(pos - original_start_pos) + start_pos` won't overflow
- // but `start_pos - original_start_pos` might. So we use the former
- // form rather than pre-computing the offset into a local variable. The
- // compiler backend can optimize away the repeated computations in a
- // way that won't trigger overflow checks.
- match &mut *file_local_lines.borrow_mut() {
- SourceFileLines::Lines(lines) => {
- for pos in lines {
- *pos = (*pos - original_start_pos) + start_pos;
- }
- }
- SourceFileLines::Diffs(SourceFileDiffs { line_start, .. }) => {
- *line_start = (*line_start - original_start_pos) + start_pos;
- }
- }
- for mbc in &mut file_local_multibyte_chars {
- mbc.pos = (mbc.pos - original_start_pos) + start_pos;
- }
- for swc in &mut file_local_non_narrow_chars {
- *swc = (*swc - original_start_pos) + start_pos;
- }
- for nc in &mut file_local_normalized_pos {
- nc.pos = (nc.pos - original_start_pos) + start_pos;
- }
+ let source_len = RelativeBytePos::from_u32(source_len);
- let source_file = Lrc::new(SourceFile {
+ let source_file = SourceFile {
name: filename,
src: None,
src_hash,
- external_src: Lock::new(ExternalSource::Foreign {
+ external_src: FreezeLock::new(ExternalSource::Foreign {
kind: ExternalSourceKind::AbsentOk,
metadata_index,
}),
- start_pos,
- end_pos,
+ start_pos: BytePos(0),
+ source_len,
lines: file_local_lines,
- multibyte_chars: file_local_multibyte_chars,
- non_narrow_chars: file_local_non_narrow_chars,
- normalized_pos: file_local_normalized_pos,
+ multibyte_chars,
+ non_narrow_chars,
+ normalized_pos,
name_hash,
cnum,
- });
-
- let mut files = self.files.borrow_mut();
-
- files.source_files.push(source_file.clone());
- files
- .stable_id_to_source_file
- .insert(StableSourceFileId::new(&source_file), source_file.clone());
+ };
- source_file
+ let file_id = StableSourceFileId::new(&source_file);
+ self.register_source_file(file_id, source_file)
+ .expect("not enough address space for imported source file")
}
/// If there is a doctest offset, applies it to the line.
@@ -439,6 +419,7 @@ impl SourceMap {
pub fn lookup_line(&self, pos: BytePos) -> Result<SourceFileAndLine, Lrc<SourceFile>> {
let f = self.lookup_source_file(pos);
+ let pos = f.relative_position(pos);
match f.lookup_line(pos) {
Some(line) => Ok(SourceFileAndLine { sf: f, line }),
None => Err(f),
@@ -534,7 +515,9 @@ impl SourceMap {
return true;
}
let f = (*self.files.borrow().source_files)[lo].clone();
- f.lookup_line(sp.lo()) != f.lookup_line(sp.hi())
+ let lo = f.relative_position(sp.lo());
+ let hi = f.relative_position(sp.hi());
+ f.lookup_line(lo) != f.lookup_line(hi)
}
#[instrument(skip(self), level = "trace")]
@@ -610,11 +593,11 @@ impl SourceMap {
end: (local_end.sf.name.clone(), local_end.sf.start_pos),
})))
} else {
- self.ensure_source_file_source_present(local_begin.sf.clone());
+ self.ensure_source_file_source_present(&local_begin.sf);
let start_index = local_begin.pos.to_usize();
let end_index = local_end.pos.to_usize();
- let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize();
+ let source_len = local_begin.sf.source_len.to_usize();
if start_index > end_index || end_index > source_len {
return Err(SpanSnippetError::MalformedForSourcemap(MalformedSourceMapPositions {
@@ -627,7 +610,7 @@ impl SourceMap {
if let Some(ref src) = local_begin.sf.src {
extract_source(src, start_index, end_index)
- } else if let Some(src) = local_begin.sf.external_src.borrow().get_source() {
+ } else if let Some(src) = local_begin.sf.external_src.read().get_source() {
extract_source(src, start_index, end_index)
} else {
Err(SpanSnippetError::SourceNotAvailable { filename: local_begin.sf.name.clone() })
@@ -919,7 +902,7 @@ impl SourceMap {
let sp = sp.data();
let local_begin = self.lookup_byte_offset(sp.lo);
let start_index = local_begin.pos.to_usize();
- let src = local_begin.sf.external_src.borrow();
+ let src = local_begin.sf.external_src.read();
let snippet = if let Some(ref src) = local_begin.sf.src {
Some(&src[start_index..])
@@ -1021,7 +1004,7 @@ impl SourceMap {
return 1;
}
- let source_len = (local_begin.sf.end_pos - local_begin.sf.start_pos).to_usize();
+ let source_len = local_begin.sf.source_len.to_usize();
debug!("source_len=`{:?}`", source_len);
// Ensure indexes are also not malformed.
if start_index > end_index || end_index > source_len - 1 {
@@ -1029,7 +1012,7 @@ impl SourceMap {
return 1;
}
- let src = local_begin.sf.external_src.borrow();
+ let src = local_begin.sf.external_src.read();
let snippet = if let Some(src) = &local_begin.sf.src {
src
@@ -1076,7 +1059,7 @@ impl SourceMap {
self.files().iter().fold(0, |a, f| a + f.count_lines())
}
- pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
+ pub fn ensure_source_file_source_present(&self, source_file: &SourceFile) -> bool {
source_file.add_external_src(|| {
let FileName::Real(ref name) = source_file.name else {
return None;