@@ -26,7 +26,7 @@ use syntax::ast;
2626use syntax:: abi;
2727use syntax:: attr;
2828use syntax:: attr:: AttrMetaMethods ;
29- use syntax:: codemap:: { Span , mk_sp} ;
29+ use syntax:: codemap:: { self , Span , mk_sp, Pos } ;
3030use syntax:: parse;
3131use syntax:: parse:: token:: InternedString ;
3232use syntax:: parse:: token;
@@ -373,15 +373,17 @@ impl<'a> CrateReader<'a> {
373373 // Maintain a reference to the top most crate.
374374 let root = if root. is_some ( ) { root } else { & crate_paths } ;
375375
376- let cnum_map = self . resolve_crate_deps ( root , lib . metadata . as_slice ( ) , span ) ;
376+ let loader :: Library { dylib , rlib , metadata } = lib ;
377377
378- let loader:: Library { dylib, rlib, metadata } = lib;
378+ let cnum_map = self . resolve_crate_deps ( root, metadata. as_slice ( ) , span) ;
379+ let codemap_import_info = import_codemap ( self . sess . codemap ( ) , & metadata) ;
379380
380381 let cmeta = Rc :: new ( cstore:: crate_metadata {
381382 name : name. to_string ( ) ,
382383 data : metadata,
383384 cnum_map : cnum_map,
384385 cnum : cnum,
386+ codemap_import_info : codemap_import_info,
385387 span : span,
386388 } ) ;
387389
@@ -586,3 +588,131 @@ impl<'a> CrateReader<'a> {
586588 }
587589 }
588590}
591+
592+ /// Imports the codemap from an external crate into the codemap of the crate
593+ /// currently being compiled (the "local crate").
594+ ///
595+ /// The import algorithm works analogous to how AST items are inlined from an
596+ /// external crate's metadata:
597+ /// For every FileMap in the external codemap an 'inline' copy is created in the
598+ /// local codemap. The correspondence relation between external and local
599+ /// FileMaps is recorded in the `ImportedFileMap` objects returned from this
600+ /// function. When an item from an external crate is later inlined into this
601+ /// crate, this correspondence information is used to translate the span
602+ /// information of the inlined item so that it refers the correct positions in
603+ /// the local codemap (see `astencode::DecodeContext::tr_span()`).
604+ ///
605+ /// The import algorithm in the function below will reuse FileMaps already
606+ /// existing in the local codemap. For example, even if the FileMap of some
607+ /// source file of libstd gets imported many times, there will only ever be
608+ /// one FileMap object for the corresponding file in the local codemap.
609+ ///
610+ /// Note that imported FileMaps do not actually contain the source code of the
611+ /// file they represent, just information about length, line breaks, and
612+ /// multibyte characters. This information is enough to generate valid debuginfo
613+ /// for items inlined from other crates.
614+ fn import_codemap ( local_codemap : & codemap:: CodeMap ,
615+ metadata : & MetadataBlob )
616+ -> Vec < cstore:: ImportedFileMap > {
617+ let external_codemap = decoder:: get_imported_filemaps ( metadata. as_slice ( ) ) ;
618+
619+ let imported_filemaps = external_codemap. into_iter ( ) . map ( |filemap_to_import| {
620+ // Try to find an existing FileMap that can be reused for the filemap to
621+ // be imported. A FileMap is reusable if it is exactly the same, just
622+ // positioned at a different offset within the codemap.
623+ let reusable_filemap = {
624+ local_codemap. files
625+ . borrow ( )
626+ . iter ( )
627+ . find ( |fm| are_equal_modulo_startpos ( & fm, & filemap_to_import) )
628+ . map ( |rc| rc. clone ( ) )
629+ } ;
630+
631+ match reusable_filemap {
632+ Some ( fm) => {
633+ cstore:: ImportedFileMap {
634+ original_start_pos : filemap_to_import. start_pos ,
635+ original_end_pos : filemap_to_import. end_pos ,
636+ translated_filemap : fm
637+ }
638+ }
639+ None => {
640+ // We can't reuse an existing FileMap, so allocate a new one
641+ // containing the information we need.
642+ let codemap:: FileMap {
643+ name,
644+ start_pos,
645+ end_pos,
646+ lines,
647+ multibyte_chars,
648+ ..
649+ } = filemap_to_import;
650+
651+ let source_length = ( end_pos - start_pos) . to_usize ( ) ;
652+
653+ // Translate line-start positions and multibyte character
654+ // position into frame of reference local to file.
655+ // `CodeMap::new_imported_filemap()` will then translate those
656+ // coordinates to their new global frame of reference when the
657+ // offset of the FileMap is known.
658+ let lines = lines. into_inner ( ) . map_in_place ( |pos| pos - start_pos) ;
659+ let multibyte_chars = multibyte_chars
660+ . into_inner ( )
661+ . map_in_place ( |mbc|
662+ codemap:: MultiByteChar {
663+ pos : mbc. pos + start_pos,
664+ bytes : mbc. bytes
665+ } ) ;
666+
667+ let local_version = local_codemap. new_imported_filemap ( name,
668+ source_length,
669+ lines,
670+ multibyte_chars) ;
671+ cstore:: ImportedFileMap {
672+ original_start_pos : start_pos,
673+ original_end_pos : end_pos,
674+ translated_filemap : local_version
675+ }
676+ }
677+ }
678+ } ) . collect ( ) ;
679+
680+ return imported_filemaps;
681+
682+ fn are_equal_modulo_startpos ( fm1 : & codemap:: FileMap ,
683+ fm2 : & codemap:: FileMap )
684+ -> bool {
685+ if fm1. name != fm2. name {
686+ return false ;
687+ }
688+
689+ let lines1 = fm1. lines . borrow ( ) ;
690+ let lines2 = fm2. lines . borrow ( ) ;
691+
692+ if lines1. len ( ) != lines2. len ( ) {
693+ return false ;
694+ }
695+
696+ for ( & line1, & line2) in lines1. iter ( ) . zip ( lines2. iter ( ) ) {
697+ if ( line1 - fm1. start_pos ) != ( line2 - fm2. start_pos ) {
698+ return false ;
699+ }
700+ }
701+
702+ let multibytes1 = fm1. multibyte_chars . borrow ( ) ;
703+ let multibytes2 = fm2. multibyte_chars . borrow ( ) ;
704+
705+ if multibytes1. len ( ) != multibytes2. len ( ) {
706+ return false ;
707+ }
708+
709+ for ( mb1, mb2) in multibytes1. iter ( ) . zip ( multibytes2. iter ( ) ) {
710+ if ( mb1. bytes != mb2. bytes ) ||
711+ ( ( mb1. pos - fm1. start_pos ) != ( mb2. pos - fm2. start_pos ) ) {
712+ return false ;
713+ }
714+ }
715+
716+ true
717+ }
718+ }
0 commit comments