Auto merge of #22235 - michaelwoerister:cross-crate-spans, r=michaelwoerister

This allows to create proper debuginfo line information for items inlined from other crates (e.g. instantiations of generics). Only the codemap's 'metadata' is stored in a crate's metadata. That is, just filename, positions of line-beginnings, etc. but not the actual source code itself.

Crate metadata size is increased by this change because spans in the encoded ASTs take up space now:
```
                BEFORE    AFTER
libcore         36 MiB    39.6 MiB    +10%
libsyntax       51.1 MiB  60.5 MiB    +18.4%
libcollections  11.2 MiB  12.8 MiB    +14.3%
```
This only affects binaries containing metadata (rlibs and dylibs), executables should not be affected in size. 

Fixes #19228 and probably #22226.
This commit is contained in:
bors 2015-03-04 14:47:51 +00:00
commit 3b3bb0e682
12 changed files with 693 additions and 172 deletions

View File

@ -252,3 +252,6 @@ pub const tag_macro_def: uint = 0x9e;
pub const tag_macro_def_body: uint = 0x9f; pub const tag_macro_def_body: uint = 0x9f;
pub const tag_paren_sugar: uint = 0xa0; pub const tag_paren_sugar: uint = 0xa0;
pub const tag_codemap: uint = 0xa1;
pub const tag_codemap_filemap: uint = 0xa2;

View File

@ -26,7 +26,7 @@ use syntax::ast;
use syntax::abi; use syntax::abi;
use syntax::attr; use syntax::attr;
use syntax::attr::AttrMetaMethods; use syntax::attr::AttrMetaMethods;
use syntax::codemap::{Span, mk_sp}; use syntax::codemap::{self, Span, mk_sp, Pos};
use syntax::parse; use syntax::parse;
use syntax::parse::token::InternedString; use syntax::parse::token::InternedString;
use syntax::parse::token; use syntax::parse::token;
@ -373,15 +373,17 @@ impl<'a> CrateReader<'a> {
// Maintain a reference to the top most crate. // Maintain a reference to the top most crate.
let root = if root.is_some() { root } else { &crate_paths }; let root = if root.is_some() { root } else { &crate_paths };
let cnum_map = self.resolve_crate_deps(root, lib.metadata.as_slice(), span); let loader::Library { dylib, rlib, metadata } = lib;
let loader::Library{ dylib, rlib, metadata } = lib; let cnum_map = self.resolve_crate_deps(root, metadata.as_slice(), span);
let codemap_import_info = import_codemap(self.sess.codemap(), &metadata);
let cmeta = Rc::new( cstore::crate_metadata { let cmeta = Rc::new( cstore::crate_metadata {
name: name.to_string(), name: name.to_string(),
data: metadata, data: metadata,
cnum_map: cnum_map, cnum_map: cnum_map,
cnum: cnum, cnum: cnum,
codemap_import_info: codemap_import_info,
span: span, span: span,
}); });
@ -586,3 +588,131 @@ impl<'a> CrateReader<'a> {
} }
} }
} }
/// Imports the codemap from an external crate into the codemap of the crate
/// currently being compiled (the "local crate").
///
/// The import algorithm works analogous to how AST items are inlined from an
/// external crate's metadata:
/// For every FileMap in the external codemap an 'inline' copy is created in the
/// local codemap. The correspondence relation between external and local
/// FileMaps is recorded in the `ImportedFileMap` objects returned from this
/// function. When an item from an external crate is later inlined into this
/// crate, this correspondence information is used to translate the span
/// information of the inlined item so that it refers the correct positions in
/// the local codemap (see `astencode::DecodeContext::tr_span()`).
///
/// The import algorithm in the function below will reuse FileMaps already
/// existing in the local codemap. For example, even if the FileMap of some
/// source file of libstd gets imported many times, there will only ever be
/// one FileMap object for the corresponding file in the local codemap.
///
/// Note that imported FileMaps do not actually contain the source code of the
/// file they represent, just information about length, line breaks, and
/// multibyte characters. This information is enough to generate valid debuginfo
/// for items inlined from other crates.
fn import_codemap(local_codemap: &codemap::CodeMap,
metadata: &MetadataBlob)
-> Vec<cstore::ImportedFileMap> {
let external_codemap = decoder::get_imported_filemaps(metadata.as_slice());
let imported_filemaps = external_codemap.into_iter().map(|filemap_to_import| {
// Try to find an existing FileMap that can be reused for the filemap to
// be imported. A FileMap is reusable if it is exactly the same, just
// positioned at a different offset within the codemap.
let reusable_filemap = {
local_codemap.files
.borrow()
.iter()
.find(|fm| are_equal_modulo_startpos(&fm, &filemap_to_import))
.map(|rc| rc.clone())
};
match reusable_filemap {
Some(fm) => {
cstore::ImportedFileMap {
original_start_pos: filemap_to_import.start_pos,
original_end_pos: filemap_to_import.end_pos,
translated_filemap: fm
}
}
None => {
// We can't reuse an existing FileMap, so allocate a new one
// containing the information we need.
let codemap::FileMap {
name,
start_pos,
end_pos,
lines,
multibyte_chars,
..
} = filemap_to_import;
let source_length = (end_pos - start_pos).to_usize();
// Translate line-start positions and multibyte character
// position into frame of reference local to file.
// `CodeMap::new_imported_filemap()` will then translate those
// coordinates to their new global frame of reference when the
// offset of the FileMap is known.
let lines = lines.into_inner().map_in_place(|pos| pos - start_pos);
let multibyte_chars = multibyte_chars
.into_inner()
.map_in_place(|mbc|
codemap::MultiByteChar {
pos: mbc.pos + start_pos,
bytes: mbc.bytes
});
let local_version = local_codemap.new_imported_filemap(name,
source_length,
lines,
multibyte_chars);
cstore::ImportedFileMap {
original_start_pos: start_pos,
original_end_pos: end_pos,
translated_filemap: local_version
}
}
}
}).collect();
return imported_filemaps;
fn are_equal_modulo_startpos(fm1: &codemap::FileMap,
fm2: &codemap::FileMap)
-> bool {
if fm1.name != fm2.name {
return false;
}
let lines1 = fm1.lines.borrow();
let lines2 = fm2.lines.borrow();
if lines1.len() != lines2.len() {
return false;
}
for (&line1, &line2) in lines1.iter().zip(lines2.iter()) {
if (line1 - fm1.start_pos) != (line2 - fm2.start_pos) {
return false;
}
}
let multibytes1 = fm1.multibyte_chars.borrow();
let multibytes2 = fm2.multibyte_chars.borrow();
if multibytes1.len() != multibytes2.len() {
return false;
}
for (mb1, mb2) in multibytes1.iter().zip(multibytes2.iter()) {
if (mb1.bytes != mb2.bytes) ||
((mb1.pos - fm1.start_pos) != (mb2.pos - fm2.start_pos)) {
return false;
}
}
true
}
}

View File

@ -27,7 +27,7 @@ use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use flate::Bytes; use flate::Bytes;
use syntax::ast; use syntax::ast;
use syntax::codemap::Span; use syntax::codemap;
use syntax::parse::token::IdentInterner; use syntax::parse::token::IdentInterner;
// A map from external crate numbers (as decoded from some crate file) to // A map from external crate numbers (as decoded from some crate file) to
@ -41,12 +41,24 @@ pub enum MetadataBlob {
MetadataArchive(loader::ArchiveMetadata), MetadataArchive(loader::ArchiveMetadata),
} }
/// Holds information about a codemap::FileMap imported from another crate.
/// See creader::import_codemap() for more information.
pub struct ImportedFileMap {
/// This FileMap's byte-offset within the codemap of its original crate
pub original_start_pos: codemap::BytePos,
/// The end of this FileMap within the codemap of its original crate
pub original_end_pos: codemap::BytePos,
/// The imported FileMap's representation within the local codemap
pub translated_filemap: Rc<codemap::FileMap>
}
pub struct crate_metadata { pub struct crate_metadata {
pub name: String, pub name: String,
pub data: MetadataBlob, pub data: MetadataBlob,
pub cnum_map: cnum_map, pub cnum_map: cnum_map,
pub cnum: ast::CrateNum, pub cnum: ast::CrateNum,
pub span: Span, pub codemap_import_info: Vec<ImportedFileMap>,
pub span: codemap::Span,
} }
#[derive(Copy, Debug, PartialEq, Clone)] #[derive(Copy, Debug, PartialEq, Clone)]

View File

@ -1561,7 +1561,6 @@ pub fn is_associated_type(cdata: Cmd, id: ast::NodeId) -> bool {
} }
} }
pub fn is_default_trait<'tcx>(cdata: Cmd, id: ast::NodeId) -> bool { pub fn is_default_trait<'tcx>(cdata: Cmd, id: ast::NodeId) -> bool {
let item_doc = lookup_item(id, cdata.data()); let item_doc = lookup_item(id, cdata.data());
match item_family(item_doc) { match item_family(item_doc) {
@ -1569,3 +1568,19 @@ pub fn is_default_trait<'tcx>(cdata: Cmd, id: ast::NodeId) -> bool {
_ => false _ => false
} }
} }
pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
let crate_doc = rbml::Doc::new(metadata);
let cm_doc = reader::get_doc(crate_doc, tag_codemap);
let mut filemaps = vec![];
reader::tagged_docs(cm_doc, tag_codemap_filemap, |filemap_doc| {
let mut decoder = reader::Decoder::new(filemap_doc);
let filemap: codemap::FileMap = Decodable::decode(&mut decoder).unwrap();
filemaps.push(filemap);
true
});
return filemaps;
}

View File

@ -1751,6 +1751,28 @@ fn encode_plugin_registrar_fn(ecx: &EncodeContext, rbml_w: &mut Encoder) {
} }
} }
fn encode_codemap(ecx: &EncodeContext, rbml_w: &mut Encoder) {
rbml_w.start_tag(tag_codemap);
let codemap = ecx.tcx.sess.codemap();
for filemap in &codemap.files.borrow()[..] {
if filemap.lines.borrow().len() == 0 || filemap.is_imported() {
// No need to export empty filemaps, as they can't contain spans
// that need translation.
// Also no need to re-export imported filemaps, as any downstream
// crate will import them from their original source.
continue;
}
rbml_w.start_tag(tag_codemap_filemap);
filemap.encode(rbml_w);
rbml_w.end_tag();
}
rbml_w.end_tag();
}
/// Serialize the text of the exported macros /// Serialize the text of the exported macros
fn encode_macro_defs(rbml_w: &mut Encoder, fn encode_macro_defs(rbml_w: &mut Encoder,
krate: &ast::Crate) { krate: &ast::Crate) {
@ -1968,6 +1990,7 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
lang_item_bytes: u64, lang_item_bytes: u64,
native_lib_bytes: u64, native_lib_bytes: u64,
plugin_registrar_fn_bytes: u64, plugin_registrar_fn_bytes: u64,
codemap_bytes: u64,
macro_defs_bytes: u64, macro_defs_bytes: u64,
impl_bytes: u64, impl_bytes: u64,
misc_bytes: u64, misc_bytes: u64,
@ -1982,6 +2005,7 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
lang_item_bytes: 0, lang_item_bytes: 0,
native_lib_bytes: 0, native_lib_bytes: 0,
plugin_registrar_fn_bytes: 0, plugin_registrar_fn_bytes: 0,
codemap_bytes: 0,
macro_defs_bytes: 0, macro_defs_bytes: 0,
impl_bytes: 0, impl_bytes: 0,
misc_bytes: 0, misc_bytes: 0,
@ -2047,6 +2071,11 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
encode_plugin_registrar_fn(&ecx, &mut rbml_w); encode_plugin_registrar_fn(&ecx, &mut rbml_w);
stats.plugin_registrar_fn_bytes = rbml_w.writer.tell().unwrap() - i; stats.plugin_registrar_fn_bytes = rbml_w.writer.tell().unwrap() - i;
// Encode codemap
i = rbml_w.writer.tell().unwrap();
encode_codemap(&ecx, &mut rbml_w);
stats.codemap_bytes = rbml_w.writer.tell().unwrap() - i;
// Encode macro definitions // Encode macro definitions
i = rbml_w.writer.tell().unwrap(); i = rbml_w.writer.tell().unwrap();
encode_macro_defs(&mut rbml_w, krate); encode_macro_defs(&mut rbml_w, krate);
@ -2091,6 +2120,7 @@ fn encode_metadata_inner(wr: &mut SeekableMemWriter,
println!(" lang item bytes: {}", stats.lang_item_bytes); println!(" lang item bytes: {}", stats.lang_item_bytes);
println!(" native bytes: {}", stats.native_lib_bytes); println!(" native bytes: {}", stats.native_lib_bytes);
println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes); println!("plugin registrar bytes: {}", stats.plugin_registrar_fn_bytes);
println!(" codemap bytes: {}", stats.codemap_bytes);
println!(" macro def bytes: {}", stats.macro_defs_bytes); println!(" macro def bytes: {}", stats.macro_defs_bytes);
println!(" impl bytes: {}", stats.impl_bytes); println!(" impl bytes: {}", stats.impl_bytes);
println!(" misc bytes: {}", stats.misc_bytes); println!(" misc bytes: {}", stats.misc_bytes);

View File

@ -42,6 +42,7 @@ use syntax;
use std::old_io::Seek; use std::old_io::Seek;
use std::num::FromPrimitive; use std::num::FromPrimitive;
use std::rc::Rc; use std::rc::Rc;
use std::cell::Cell;
use rbml::reader; use rbml::reader;
use rbml::writer::Encoder; use rbml::writer::Encoder;
@ -58,7 +59,9 @@ struct DecodeContext<'a, 'b, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>, tcx: &'a ty::ctxt<'tcx>,
cdata: &'b cstore::crate_metadata, cdata: &'b cstore::crate_metadata,
from_id_range: ast_util::IdRange, from_id_range: ast_util::IdRange,
to_id_range: ast_util::IdRange to_id_range: ast_util::IdRange,
// Cache the last used filemap for translating spans as an optimization.
last_filemap_index: Cell<usize>,
} }
trait tr { trait tr {
@ -120,6 +123,8 @@ impl<'a, 'b, 'c, 'tcx> ast_map::FoldOps for &'a DecodeContext<'b, 'c, 'tcx> {
} }
} }
/// Decodes an item from its AST in the cdata's metadata and adds it to the
/// ast-map.
pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata, pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
tcx: &ty::ctxt<'tcx>, tcx: &ty::ctxt<'tcx>,
path: Vec<ast_map::PathElem>, path: Vec<ast_map::PathElem>,
@ -143,7 +148,8 @@ pub fn decode_inlined_item<'tcx>(cdata: &cstore::crate_metadata,
cdata: cdata, cdata: cdata,
tcx: tcx, tcx: tcx,
from_id_range: from_id_range, from_id_range: from_id_range,
to_id_range: to_id_range to_id_range: to_id_range,
last_filemap_index: Cell::new(0)
}; };
let raw_ii = decode_ast(ast_doc); let raw_ii = decode_ast(ast_doc);
let ii = ast_map::map_decoded_item(&dcx.tcx.map, path, raw_ii, dcx); let ii = ast_map::map_decoded_item(&dcx.tcx.map, path, raw_ii, dcx);
@ -234,8 +240,47 @@ impl<'a, 'b, 'tcx> DecodeContext<'a, 'b, 'tcx> {
assert_eq!(did.krate, ast::LOCAL_CRATE); assert_eq!(did.krate, ast::LOCAL_CRATE);
ast::DefId { krate: ast::LOCAL_CRATE, node: self.tr_id(did.node) } ast::DefId { krate: ast::LOCAL_CRATE, node: self.tr_id(did.node) }
} }
pub fn tr_span(&self, _span: Span) -> Span {
codemap::DUMMY_SP // FIXME (#1972): handle span properly /// Translates a `Span` from an extern crate to the corresponding `Span`
/// within the local crate's codemap. `creader::import_codemap()` will
/// already have allocated any additionally needed FileMaps in the local
/// codemap as a side-effect of creating the crate_metadata's
/// `codemap_import_info`.
pub fn tr_span(&self, span: Span) -> Span {
let imported_filemaps = &self.cdata.codemap_import_info[..];
let filemap_index = {
// Optimize for the case that most spans within a translated item
// originate from the same filemap.
let last_filemap_index = self.last_filemap_index.get();
if span.lo >= imported_filemaps[last_filemap_index].original_start_pos &&
span.hi <= imported_filemaps[last_filemap_index].original_end_pos {
last_filemap_index
} else {
let mut a = 0;
let mut b = imported_filemaps.len();
while b - a > 1 {
let m = (a + b) / 2;
if imported_filemaps[m].original_start_pos > span.lo {
b = m;
} else {
a = m;
}
}
self.last_filemap_index.set(a);
a
}
};
let lo = (span.lo - imported_filemaps[filemap_index].original_start_pos) +
imported_filemaps[filemap_index].translated_filemap.start_pos;
let hi = (span.hi - imported_filemaps[filemap_index].original_start_pos) +
imported_filemaps[filemap_index].translated_filemap.start_pos;
codemap::mk_sp(lo, hi)
} }
} }

View File

@ -542,7 +542,11 @@ pub fn pretty_print_input(sess: Session,
let src_name = driver::source_name(input); let src_name = driver::source_name(input);
let src = sess.codemap().get_filemap(&src_name[..]) let src = sess.codemap().get_filemap(&src_name[..])
.src.as_bytes().to_vec(); .src
.as_ref()
.unwrap()
.as_bytes()
.to_vec();
let mut rdr = MemReader::new(src); let mut rdr = MemReader::new(src);
let out = match ofile { let out = match ofile {

View File

@ -29,6 +29,11 @@ use std::rc::Rc;
use libc::c_uint; use libc::c_uint;
use serialize::{Encodable, Decodable, Encoder, Decoder}; use serialize::{Encodable, Decodable, Encoder, Decoder};
// _____________________________________________________________________________
// Pos, BytePos, CharPos
//
pub trait Pos { pub trait Pos {
fn from_usize(n: usize) -> Self; fn from_usize(n: usize) -> Self;
fn to_usize(&self) -> usize; fn to_usize(&self) -> usize;
@ -69,6 +74,18 @@ impl Sub for BytePos {
} }
} }
impl Encodable for BytePos {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_u32(self.0)
}
}
impl Decodable for BytePos {
fn decode<D: Decoder>(d: &mut D) -> Result<BytePos, D::Error> {
Ok(BytePos(try!{ d.read_u32() }))
}
}
impl Pos for CharPos { impl Pos for CharPos {
fn from_usize(n: usize) -> CharPos { CharPos(n) } fn from_usize(n: usize) -> CharPos { CharPos(n) }
fn to_usize(&self) -> usize { let CharPos(n) = *self; n } fn to_usize(&self) -> usize { let CharPos(n) = *self; n }
@ -90,6 +107,10 @@ impl Sub for CharPos {
} }
} }
// _____________________________________________________________________________
// Span, Spanned
//
/// Spans represent a region of code, used for error reporting. Positions in spans /// Spans represent a region of code, used for error reporting. Positions in spans
/// are *absolute* positions from the beginning of the codemap, not positions /// are *absolute* positions from the beginning of the codemap, not positions
/// relative to FileMaps. Methods on the CodeMap can be used to relate spans back /// relative to FileMaps. Methods on the CodeMap can be used to relate spans back
@ -126,15 +147,20 @@ impl PartialEq for Span {
impl Eq for Span {} impl Eq for Span {}
impl Encodable for Span { impl Encodable for Span {
/* Note #1972 -- spans are encoded but not decoded */
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> { fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_nil() // Encode spans as a single u64 in order to cut down on tagging overhead
// added by the RBML metadata encoding. The should be solved differently
// altogether some time (FIXME #21482)
s.emit_u64( (self.lo.0 as u64) | ((self.hi.0 as u64) << 32) )
} }
} }
impl Decodable for Span { impl Decodable for Span {
fn decode<D: Decoder>(_d: &mut D) -> Result<Span, D::Error> { fn decode<D: Decoder>(d: &mut D) -> Result<Span, D::Error> {
Ok(DUMMY_SP) let lo_hi: u64 = try! { d.read_u64() };
let lo = BytePos(lo_hi as u32);
let hi = BytePos((lo_hi >> 32) as u32);
Ok(mk_sp(lo, hi))
} }
} }
@ -168,6 +194,10 @@ pub fn original_sp(cm: &CodeMap, sp: Span, enclosing_sp: Span) -> Span {
} }
} }
// _____________________________________________________________________________
// Loc, LocWithOpt, FileMapAndLine, FileMapAndBytePos
//
/// A source code location used for error reporting /// A source code location used for error reporting
pub struct Loc { pub struct Loc {
/// Information about the original source /// Information about the original source
@ -192,6 +222,11 @@ pub struct LocWithOpt {
pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize } pub struct FileMapAndLine { pub fm: Rc<FileMap>, pub line: usize }
pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos } pub struct FileMapAndBytePos { pub fm: Rc<FileMap>, pub pos: BytePos }
// _____________________________________________________________________________
// MacroFormat, NameAndSpan, ExpnInfo, ExpnId
//
/// The syntax with which a macro was invoked. /// The syntax with which a macro was invoked.
#[derive(Clone, Copy, Hash, Debug)] #[derive(Clone, Copy, Hash, Debug)]
pub enum MacroFormat { pub enum MacroFormat {
@ -254,6 +289,10 @@ impl ExpnId {
} }
} }
// _____________________________________________________________________________
// FileMap, MultiByteChar, FileName, FileLines
//
pub type FileName = String; pub type FileName = String;
pub struct FileLines { pub struct FileLines {
@ -262,7 +301,7 @@ pub struct FileLines {
} }
/// Identifies an offset of a multi-byte character in a FileMap /// Identifies an offset of a multi-byte character in a FileMap
#[derive(Copy)] #[derive(Copy, RustcEncodable, RustcDecodable, Eq, PartialEq)]
pub struct MultiByteChar { pub struct MultiByteChar {
/// The absolute offset of the character in the CodeMap /// The absolute offset of the character in the CodeMap
pub pos: BytePos, pub pos: BytePos,
@ -277,13 +316,134 @@ pub struct FileMap {
/// e.g. `<anon>` /// e.g. `<anon>`
pub name: FileName, pub name: FileName,
/// The complete source code /// The complete source code
pub src: String, pub src: Option<Rc<String>>,
/// The start position of this source in the CodeMap /// The start position of this source in the CodeMap
pub start_pos: BytePos, pub start_pos: BytePos,
/// The end position of this source in the CodeMap
pub end_pos: BytePos,
/// Locations of lines beginnings in the source code /// Locations of lines beginnings in the source code
pub lines: RefCell<Vec<BytePos> >, pub lines: RefCell<Vec<BytePos>>,
/// Locations of multi-byte characters in the source code /// Locations of multi-byte characters in the source code
pub multibyte_chars: RefCell<Vec<MultiByteChar> >, pub multibyte_chars: RefCell<Vec<MultiByteChar>>,
}
impl Encodable for FileMap {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_struct("FileMap", 5, |s| {
try! { s.emit_struct_field("name", 0, |s| self.name.encode(s)) };
try! { s.emit_struct_field("start_pos", 1, |s| self.start_pos.encode(s)) };
try! { s.emit_struct_field("end_pos", 2, |s| self.end_pos.encode(s)) };
try! { s.emit_struct_field("lines", 3, |s| {
let lines = self.lines.borrow();
// store the length
try! { s.emit_u32(lines.len() as u32) };
if lines.len() > 0 {
// In order to preserve some space, we exploit the fact that
// the lines list is sorted and individual lines are
// probably not that long. Because of that we can store lines
// as a difference list, using as little space as possible
// for the differences.
let max_line_length = if lines.len() == 1 {
0
} else {
lines.as_slice()
.windows(2)
.map(|w| w[1] - w[0])
.map(|bp| bp.to_usize())
.max()
.unwrap()
};
let bytes_per_diff: u8 = match max_line_length {
0 ... 0xFF => 1,
0x100 ... 0xFFFF => 2,
_ => 4
};
// Encode the number of bytes used per diff.
try! { bytes_per_diff.encode(s) };
// Encode the first element.
try! { lines[0].encode(s) };
let diff_iter = (&lines[..]).windows(2)
.map(|w| (w[1] - w[0]));
match bytes_per_diff {
1 => for diff in diff_iter { try! { (diff.0 as u8).encode(s) } },
2 => for diff in diff_iter { try! { (diff.0 as u16).encode(s) } },
4 => for diff in diff_iter { try! { (diff.0 as u32).encode(s) } },
_ => unreachable!()
}
}
Ok(())
})
};
s.emit_struct_field("multibyte_chars", 4, |s| {
(*self.multibyte_chars.borrow()).encode(s)
})
})
}
}
impl Decodable for FileMap {
fn decode<D: Decoder>(d: &mut D) -> Result<FileMap, D::Error> {
d.read_struct("FileMap", 5, |d| {
let name: String = try! {
d.read_struct_field("name", 0, |d| Decodable::decode(d))
};
let start_pos: BytePos = try! {
d.read_struct_field("start_pos", 1, |d| Decodable::decode(d))
};
let end_pos: BytePos = try! {
d.read_struct_field("end_pos", 2, |d| Decodable::decode(d))
};
let lines: Vec<BytePos> = try! {
d.read_struct_field("lines", 3, |d| {
let num_lines: u32 = try! { Decodable::decode(d) };
let mut lines = Vec::with_capacity(num_lines as usize);
if num_lines > 0 {
// Read the number of bytes used per diff.
let bytes_per_diff: u8 = try! { Decodable::decode(d) };
// Read the first element.
let mut line_start: BytePos = try! { Decodable::decode(d) };
lines.push(line_start);
for _ in 1..num_lines {
let diff = match bytes_per_diff {
1 => try! { d.read_u8() } as u32,
2 => try! { d.read_u16() } as u32,
4 => try! { d.read_u32() },
_ => unreachable!()
};
line_start = line_start + BytePos(diff);
lines.push(line_start);
}
}
Ok(lines)
})
};
let multibyte_chars: Vec<MultiByteChar> = try! {
d.read_struct_field("multibyte_chars", 4, |d| Decodable::decode(d))
};
Ok(FileMap {
name: name,
start_pos: start_pos,
end_pos: end_pos,
src: None,
lines: RefCell::new(lines),
multibyte_chars: RefCell::new(multibyte_chars)
})
})
}
} }
impl FileMap { impl FileMap {
@ -307,16 +467,21 @@ impl FileMap {
/// get a line from the list of pre-computed line-beginnings /// get a line from the list of pre-computed line-beginnings
/// ///
pub fn get_line(&self, line_number: usize) -> Option<String> { pub fn get_line(&self, line_number: usize) -> Option<String> {
let lines = self.lines.borrow(); match self.src {
lines.get(line_number).map(|&line| { Some(ref src) => {
let begin: BytePos = line - self.start_pos; let lines = self.lines.borrow();
let begin = begin.to_usize(); lines.get(line_number).map(|&line| {
let slice = &self.src[begin..]; let begin: BytePos = line - self.start_pos;
match slice.find('\n') { let begin = begin.to_usize();
Some(e) => &slice[..e], let slice = &src[begin..];
None => slice match slice.find('\n') {
}.to_string() Some(e) => &slice[..e],
}) None => slice
}.to_string()
})
}
None => None
}
} }
pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) { pub fn record_multibyte_char(&self, pos: BytePos, bytes: usize) {
@ -332,8 +497,17 @@ impl FileMap {
!(self.name.starts_with("<") && !(self.name.starts_with("<") &&
self.name.ends_with(">")) self.name.ends_with(">"))
} }
pub fn is_imported(&self) -> bool {
self.src.is_none()
}
} }
// _____________________________________________________________________________
// CodeMap
//
pub struct CodeMap { pub struct CodeMap {
pub files: RefCell<Vec<Rc<FileMap>>>, pub files: RefCell<Vec<Rc<FileMap>>>,
expansions: RefCell<Vec<ExpnInfo>> expansions: RefCell<Vec<ExpnInfo>>
@ -351,7 +525,7 @@ impl CodeMap {
let mut files = self.files.borrow_mut(); let mut files = self.files.borrow_mut();
let start_pos = match files.last() { let start_pos = match files.last() {
None => 0, None => 0,
Some(last) => last.start_pos.to_usize() + last.src.len(), Some(last) => last.end_pos.to_usize(),
}; };
// Remove utf-8 BOM if any. // Remove utf-8 BOM if any.
@ -372,10 +546,13 @@ impl CodeMap {
src.push('\n'); src.push('\n');
} }
let end_pos = start_pos + src.len();
let filemap = Rc::new(FileMap { let filemap = Rc::new(FileMap {
name: filename, name: filename,
src: src.to_string(), src: Some(Rc::new(src)),
start_pos: Pos::from_usize(start_pos), start_pos: Pos::from_usize(start_pos),
end_pos: Pos::from_usize(end_pos),
lines: RefCell::new(Vec::new()), lines: RefCell::new(Vec::new()),
multibyte_chars: RefCell::new(Vec::new()), multibyte_chars: RefCell::new(Vec::new()),
}); });
@ -385,6 +562,45 @@ impl CodeMap {
filemap filemap
} }
/// Allocates a new FileMap representing a source file from an external
/// crate. The source code of such an "imported filemap" is not available,
/// but we still know enough to generate accurate debuginfo location
/// information for things inlined from other crates.
pub fn new_imported_filemap(&self,
filename: FileName,
source_len: usize,
file_local_lines: Vec<BytePos>,
file_local_multibyte_chars: Vec<MultiByteChar>)
-> Rc<FileMap> {
let mut files = self.files.borrow_mut();
let start_pos = match files.last() {
None => 0,
Some(last) => last.end_pos.to_usize(),
};
let end_pos = Pos::from_usize(start_pos + source_len);
let start_pos = Pos::from_usize(start_pos);
let lines = file_local_lines.map_in_place(|pos| pos + start_pos);
let multibyte_chars = file_local_multibyte_chars.map_in_place(|mbc| MultiByteChar {
pos: mbc.pos + start_pos,
bytes: mbc.bytes
});
let filemap = Rc::new(FileMap {
name: filename,
src: None,
start_pos: start_pos,
end_pos: end_pos,
lines: RefCell::new(lines),
multibyte_chars: RefCell::new(multibyte_chars),
});
files.push(filemap.clone());
filemap
}
pub fn mk_substr_filename(&self, sp: Span) -> String { pub fn mk_substr_filename(&self, sp: Span) -> String {
let pos = self.lookup_char_pos(sp.lo); let pos = self.lookup_char_pos(sp.lo);
(format!("<{}:{}:{}>", (format!("<{}:{}:{}>",
@ -442,30 +658,42 @@ impl CodeMap {
return Err(SpanSnippetError::IllFormedSpan(sp)); return Err(SpanSnippetError::IllFormedSpan(sp));
} }
let begin = self.lookup_byte_offset(sp.lo); let local_begin = self.lookup_byte_offset(sp.lo);
let end = self.lookup_byte_offset(sp.hi); let local_end = self.lookup_byte_offset(sp.hi);
if begin.fm.start_pos != end.fm.start_pos { if local_begin.fm.start_pos != local_end.fm.start_pos {
return Err(SpanSnippetError::DistinctSources(DistinctSources { return Err(SpanSnippetError::DistinctSources(DistinctSources {
begin: (begin.fm.name.clone(), begin: (local_begin.fm.name.clone(),
begin.fm.start_pos), local_begin.fm.start_pos),
end: (end.fm.name.clone(), end: (local_end.fm.name.clone(),
end.fm.start_pos) local_end.fm.start_pos)
})); }));
} else { } else {
let start = begin.pos.to_usize(); match local_begin.fm.src {
let limit = end.pos.to_usize(); Some(ref src) => {
if start > limit || limit > begin.fm.src.len() { let start_index = local_begin.pos.to_usize();
return Err(SpanSnippetError::MalformedForCodemap( let end_index = local_end.pos.to_usize();
MalformedCodemapPositions { let source_len = (local_begin.fm.end_pos -
name: begin.fm.name.clone(), local_begin.fm.start_pos).to_usize();
source_len: begin.fm.src.len(),
begin_pos: begin.pos,
end_pos: end.pos,
}));
}
return Ok((&begin.fm.src[start..limit]).to_string()) if start_index > end_index || end_index > source_len {
return Err(SpanSnippetError::MalformedForCodemap(
MalformedCodemapPositions {
name: local_begin.fm.name.clone(),
source_len: source_len,
begin_pos: local_begin.pos,
end_pos: local_end.pos,
}));
}
return Ok((&src[start_index..end_index]).to_string())
}
None => {
return Err(SpanSnippetError::SourceNotAvailable {
filename: local_begin.fm.name.clone()
});
}
}
} }
} }
@ -478,6 +706,7 @@ impl CodeMap {
panic!("asking for {} which we don't know about", filename); panic!("asking for {} which we don't know about", filename);
} }
/// For a global BytePos compute the local offset within the containing FileMap
pub fn lookup_byte_offset(&self, bpos: BytePos) -> FileMapAndBytePos { pub fn lookup_byte_offset(&self, bpos: BytePos) -> FileMapAndBytePos {
let idx = self.lookup_filemap_idx(bpos); let idx = self.lookup_filemap_idx(bpos);
let fm = (*self.files.borrow())[idx].clone(); let fm = (*self.files.borrow())[idx].clone();
@ -639,11 +868,16 @@ impl CodeMap {
} }
} }
// _____________________________________________________________________________
// SpanSnippetError, DistinctSources, MalformedCodemapPositions
//
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum SpanSnippetError { pub enum SpanSnippetError {
IllFormedSpan(Span), IllFormedSpan(Span),
DistinctSources(DistinctSources), DistinctSources(DistinctSources),
MalformedForCodemap(MalformedCodemapPositions), MalformedForCodemap(MalformedCodemapPositions),
SourceNotAvailable { filename: String }
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
@ -660,6 +894,11 @@ pub struct MalformedCodemapPositions {
end_pos: BytePos end_pos: BytePos
} }
// _____________________________________________________________________________
// Tests
//
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -76,6 +76,10 @@ pub struct StringReader<'a> {
// are revised to go directly to token-trees. // are revised to go directly to token-trees.
/// Is \x00<name>,<ctxt>\x00 is interpreted as encoded ast::Ident? /// Is \x00<name>,<ctxt>\x00 is interpreted as encoded ast::Ident?
read_embedded_ident: bool, read_embedded_ident: bool,
// cache a direct reference to the source text, so that we don't have to
// retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
source_text: Rc<String>
} }
impl<'a> Reader for StringReader<'a> { impl<'a> Reader for StringReader<'a> {
@ -141,7 +145,14 @@ pub fn make_reader_with_embedded_idents<'b>(span_diagnostic: &'b SpanHandler,
impl<'a> StringReader<'a> { impl<'a> StringReader<'a> {
/// For comments.rs, which hackily pokes into pos and curr /// For comments.rs, which hackily pokes into pos and curr
pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler, pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler,
filemap: Rc<codemap::FileMap>) -> StringReader<'b> { filemap: Rc<codemap::FileMap>) -> StringReader<'b> {
if filemap.src.is_none() {
span_diagnostic.handler.bug(&format!("Cannot lex filemap without source: {}",
filemap.name)[..]);
}
let source_text = (*filemap.src.as_ref().unwrap()).clone();
let mut sr = StringReader { let mut sr = StringReader {
span_diagnostic: span_diagnostic, span_diagnostic: span_diagnostic,
pos: filemap.start_pos, pos: filemap.start_pos,
@ -153,6 +164,7 @@ impl<'a> StringReader<'a> {
peek_tok: token::Eof, peek_tok: token::Eof,
peek_span: codemap::DUMMY_SP, peek_span: codemap::DUMMY_SP,
read_embedded_ident: false, read_embedded_ident: false,
source_text: source_text
}; };
sr.bump(); sr.bump();
sr sr
@ -213,7 +225,7 @@ impl<'a> StringReader<'a> {
m.push_str(": "); m.push_str(": ");
let from = self.byte_offset(from_pos).to_usize(); let from = self.byte_offset(from_pos).to_usize();
let to = self.byte_offset(to_pos).to_usize(); let to = self.byte_offset(to_pos).to_usize();
m.push_str(&self.filemap.src[from..to]); m.push_str(&self.source_text[from..to]);
self.fatal_span_(from_pos, to_pos, &m[..]); self.fatal_span_(from_pos, to_pos, &m[..]);
} }
@ -270,9 +282,8 @@ impl<'a> StringReader<'a> {
fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where fn with_str_from_to<T, F>(&self, start: BytePos, end: BytePos, f: F) -> T where
F: FnOnce(&str) -> T, F: FnOnce(&str) -> T,
{ {
f(&self.filemap.src[ f(&self.source_text[self.byte_offset(start).to_usize()..
self.byte_offset(start).to_usize().. self.byte_offset(end).to_usize()])
self.byte_offset(end).to_usize()])
} }
/// Converts CRLF to LF in the given string, raising an error on bare CR. /// Converts CRLF to LF in the given string, raising an error on bare CR.
@ -321,12 +332,10 @@ impl<'a> StringReader<'a> {
pub fn bump(&mut self) { pub fn bump(&mut self) {
self.last_pos = self.pos; self.last_pos = self.pos;
let current_byte_offset = self.byte_offset(self.pos).to_usize(); let current_byte_offset = self.byte_offset(self.pos).to_usize();
if current_byte_offset < self.filemap.src.len() { if current_byte_offset < self.source_text.len() {
assert!(self.curr.is_some()); assert!(self.curr.is_some());
let last_char = self.curr.unwrap(); let last_char = self.curr.unwrap();
let next = self.filemap let next = self.source_text.char_range_at(current_byte_offset);
.src
.char_range_at(current_byte_offset);
let byte_offset_diff = next.next - current_byte_offset; let byte_offset_diff = next.next - current_byte_offset;
self.pos = self.pos + Pos::from_usize(byte_offset_diff); self.pos = self.pos + Pos::from_usize(byte_offset_diff);
self.curr = Some(next.ch); self.curr = Some(next.ch);
@ -346,8 +355,8 @@ impl<'a> StringReader<'a> {
pub fn nextch(&self) -> Option<char> { pub fn nextch(&self) -> Option<char> {
let offset = self.byte_offset(self.pos).to_usize(); let offset = self.byte_offset(self.pos).to_usize();
if offset < self.filemap.src.len() { if offset < self.source_text.len() {
Some(self.filemap.src.char_at(offset)) Some(self.source_text.char_at(offset))
} else { } else {
None None
} }
@ -359,7 +368,7 @@ impl<'a> StringReader<'a> {
pub fn nextnextch(&self) -> Option<char> { pub fn nextnextch(&self) -> Option<char> {
let offset = self.byte_offset(self.pos).to_usize(); let offset = self.byte_offset(self.pos).to_usize();
let s = &*self.filemap.src; let s = &self.source_text[..];
if offset >= s.len() { return None } if offset >= s.len() { return None }
let str::CharRange { next, .. } = s.char_range_at(offset); let str::CharRange { next, .. } = s.char_range_at(offset);
if next < s.len() { if next < s.len() {

View File

@ -751,6 +751,7 @@ pub fn integer_lit(s: &str, suffix: Option<&str>, sd: &SpanHandler, sp: Span) ->
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use std::rc::Rc;
use serialize::json; use serialize::json;
use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION}; use codemap::{Span, BytePos, Pos, Spanned, NO_EXPANSION};
use owned_slice::OwnedSlice; use owned_slice::OwnedSlice;
@ -855,117 +856,50 @@ mod test {
} }
#[test] #[test]
fn string_to_tts_1 () { fn string_to_tts_1() {
let tts = string_to_tts("fn a (b : i32) { b; }".to_string()); let tts = string_to_tts("fn a (b : i32) { b; }".to_string());
assert_eq!(json::encode(&tts).unwrap(),
"[\ let expected = vec![
{\ ast::TtToken(sp(0, 2),
\"variant\":\"TtToken\",\ token::Ident(str_to_ident("fn"),
\"fields\":[\ token::IdentStyle::Plain)),
null,\ ast::TtToken(sp(3, 4),
{\ token::Ident(str_to_ident("a"),
\"variant\":\"Ident\",\ token::IdentStyle::Plain)),
\"fields\":[\ ast::TtDelimited(
\"fn\",\ sp(5, 14),
\"Plain\"\ Rc::new(ast::Delimited {
]\ delim: token::DelimToken::Paren,
}\ open_span: sp(5, 6),
]\ tts: vec![
},\ ast::TtToken(sp(6, 7),
{\ token::Ident(str_to_ident("b"),
\"variant\":\"TtToken\",\ token::IdentStyle::Plain)),
\"fields\":[\ ast::TtToken(sp(8, 9),
null,\ token::Colon),
{\ ast::TtToken(sp(10, 13),
\"variant\":\"Ident\",\ token::Ident(str_to_ident("i32"),
\"fields\":[\ token::IdentStyle::Plain)),
\"a\",\ ],
\"Plain\"\ close_span: sp(13, 14),
]\ })),
}\ ast::TtDelimited(
]\ sp(15, 21),
},\ Rc::new(ast::Delimited {
{\ delim: token::DelimToken::Brace,
\"variant\":\"TtDelimited\",\ open_span: sp(15, 16),
\"fields\":[\ tts: vec![
null,\ ast::TtToken(sp(17, 18),
{\ token::Ident(str_to_ident("b"),
\"delim\":\"Paren\",\ token::IdentStyle::Plain)),
\"open_span\":null,\ ast::TtToken(sp(18, 19),
\"tts\":[\ token::Semi)
{\ ],
\"variant\":\"TtToken\",\ close_span: sp(20, 21),
\"fields\":[\ }))
null,\ ];
{\
\"variant\":\"Ident\",\ assert_eq!(tts, expected);
\"fields\":[\
\"b\",\
\"Plain\"\
]\
}\
]\
},\
{\
\"variant\":\"TtToken\",\
\"fields\":[\
null,\
\"Colon\"\
]\
},\
{\
\"variant\":\"TtToken\",\
\"fields\":[\
null,\
{\
\"variant\":\"Ident\",\
\"fields\":[\
\"i32\",\
\"Plain\"\
]\
}\
]\
}\
],\
\"close_span\":null\
}\
]\
},\
{\
\"variant\":\"TtDelimited\",\
\"fields\":[\
null,\
{\
\"delim\":\"Brace\",\
\"open_span\":null,\
\"tts\":[\
{\
\"variant\":\"TtToken\",\
\"fields\":[\
null,\
{\
\"variant\":\"Ident\",\
\"fields\":[\
\"b\",\
\"Plain\"\
]\
}\
]\
},\
{\
\"variant\":\"TtToken\",\
\"fields\":[\
null,\
\"Semi\"\
]\
}\
],\
\"close_span\":null\
}\
]\
}\
]"
);
} }
#[test] fn ret_expr() { #[test] fn ret_expr() {

View File

@ -0,0 +1,26 @@
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![crate_type = "rlib"]
#![omit_gdb_pretty_printer_section]
// no-prefer-dynamic
// compile-flags:-g
pub fn generic_function<T: Clone>(val: T) -> (T, T) {
let result = (val.clone(), val.clone());
let a_variable: u32 = 123456789;
let another_variable: f64 = 123456789.5;
zzz();
result
}
#[inline(never)]
fn zzz() {()}

View File

@ -0,0 +1,74 @@
// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![omit_gdb_pretty_printer_section]
// ignore-android: FIXME(#10381)
// min-lldb-version: 310
// aux-build:cross_crate_spans.rs
extern crate cross_crate_spans;
// compile-flags:-g
// === GDB TESTS ===================================================================================
// gdb-command:break cross_crate_spans.rs:21
// gdb-command:run
// gdb-command:print result
// gdb-check:$1 = {17, 17}
// gdb-command:print a_variable
// gdb-check:$2 = 123456789
// gdb-command:print another_variable
// gdb-check:$3 = 123456789.5
// gdb-command:continue
// gdb-command:print result
// gdb-check:$4 = {1212, 1212}
// gdb-command:print a_variable
// gdb-check:$5 = 123456789
// gdb-command:print another_variable
// gdb-check:$6 = 123456789.5
// gdb-command:continue
// === LLDB TESTS ==================================================================================
// lldb-command:b cross_crate_spans.rs:21
// lldb-command:run
// lldb-command:print result
// lldb-check:[...]$0 = (17, 17)
// lldb-command:print a_variable
// lldb-check:[...]$1 = 123456789
// lldb-command:print another_variable
// lldb-check:[...]$2 = 123456789.5
// lldb-command:continue
// lldb-command:print result
// lldb-check:[...]$3 = (1212, 1212)
// lldb-command:print a_variable
// lldb-check:[...]$4 = 123456789
// lldb-command:print another_variable
// lldb-check:[...]$5 = 123456789.5
// lldb-command:continue
// This test makes sure that we can break in functions inlined from other crates.
fn main() {
let _ = cross_crate_spans::generic_function(17u32);
let _ = cross_crate_spans::generic_function(1212i16);
}