| // Copyright 2012-2014 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. |
| |
| #![allow(non_camel_case_types)] |
| |
| // The crate store - a central repo for information collected about external |
| // crates and libraries |
| |
| pub use self::MetadataBlob::*; |
| |
| use creader; |
| use decoder; |
| use index; |
| use loader; |
| |
| use rustc::back::svh::Svh; |
| use rustc::front::map as ast_map; |
| use rustc::util::nodemap::{FnvHashMap, NodeMap, NodeSet}; |
| |
| use std::cell::{RefCell, Ref, Cell}; |
| use std::rc::Rc; |
| use std::path::PathBuf; |
| use flate::Bytes; |
| use syntax::ast; |
| use syntax::attr; |
| use syntax::codemap; |
| use syntax::parse::token; |
| use syntax::parse::token::IdentInterner; |
| use syntax::util::small_vector::SmallVector; |
| |
| pub use middle::cstore::{NativeLibraryKind, LinkagePreference}; |
| pub use middle::cstore::{NativeStatic, NativeFramework, NativeUnknown}; |
| pub use middle::cstore::{CrateSource, LinkMeta}; |
| |
| // A map from external crate numbers (as decoded from some crate file) to |
| // local crate numbers (as generated during this session). Each external |
| // crate may refer to types in other external crates, and each has their |
| // own crate numbers. |
| pub type cnum_map = FnvHashMap<ast::CrateNum, ast::CrateNum>; |
| |
| pub enum MetadataBlob { |
| MetadataVec(Bytes), |
| 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 name: String, |
| pub local_path: RefCell<SmallVector<ast_map::PathElem>>, |
| pub local_def_path: RefCell<ast_map::DefPath>, |
| pub data: MetadataBlob, |
| pub cnum_map: RefCell<cnum_map>, |
| pub cnum: ast::CrateNum, |
| pub codemap_import_info: RefCell<Vec<ImportedFileMap>>, |
| pub span: codemap::Span, |
| pub staged_api: bool, |
| |
| pub index: index::Index, |
| pub xref_index: index::DenseIndex, |
| |
| /// Flag if this crate is required by an rlib version of this crate, or in |
| /// other words whether it was explicitly linked to. An example of a crate |
| /// where this is false is when an allocator crate is injected into the |
| /// dependency list, and therefore isn't actually needed to link an rlib. |
| pub explicitly_linked: Cell<bool>, |
| } |
| |
| pub struct CStore { |
| metas: RefCell<FnvHashMap<ast::CrateNum, Rc<crate_metadata>>>, |
| /// Map from NodeId's of local extern crate statements to crate numbers |
| extern_mod_crate_map: RefCell<NodeMap<ast::CrateNum>>, |
| used_crate_sources: RefCell<Vec<CrateSource>>, |
| used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>, |
| used_link_args: RefCell<Vec<String>>, |
| statically_included_foreign_items: RefCell<NodeSet>, |
| pub intr: Rc<IdentInterner>, |
| } |
| |
| impl CStore { |
| pub fn new(intr: Rc<IdentInterner>) -> CStore { |
| CStore { |
| metas: RefCell::new(FnvHashMap()), |
| extern_mod_crate_map: RefCell::new(FnvHashMap()), |
| used_crate_sources: RefCell::new(Vec::new()), |
| used_libraries: RefCell::new(Vec::new()), |
| used_link_args: RefCell::new(Vec::new()), |
| intr: intr, |
| statically_included_foreign_items: RefCell::new(NodeSet()), |
| } |
| } |
| |
| pub fn next_crate_num(&self) -> ast::CrateNum { |
| self.metas.borrow().len() as ast::CrateNum + 1 |
| } |
| |
| pub fn get_crate_data(&self, cnum: ast::CrateNum) -> Rc<crate_metadata> { |
| self.metas.borrow().get(&cnum).unwrap().clone() |
| } |
| |
| pub fn get_crate_hash(&self, cnum: ast::CrateNum) -> Svh { |
| let cdata = self.get_crate_data(cnum); |
| decoder::get_crate_hash(cdata.data()) |
| } |
| |
| pub fn set_crate_data(&self, cnum: ast::CrateNum, data: Rc<crate_metadata>) { |
| self.metas.borrow_mut().insert(cnum, data); |
| } |
| |
| pub fn iter_crate_data<I>(&self, mut i: I) where |
| I: FnMut(ast::CrateNum, &Rc<crate_metadata>), |
| { |
| for (&k, v) in self.metas.borrow().iter() { |
| i(k, v); |
| } |
| } |
| |
| /// Like `iter_crate_data`, but passes source paths (if available) as well. |
| pub fn iter_crate_data_origins<I>(&self, mut i: I) where |
| I: FnMut(ast::CrateNum, &crate_metadata, Option<CrateSource>), |
| { |
| for (&k, v) in self.metas.borrow().iter() { |
| let origin = self.opt_used_crate_source(k); |
| origin.as_ref().map(|cs| { assert!(k == cs.cnum); }); |
| i(k, &**v, origin); |
| } |
| } |
| |
| pub fn add_used_crate_source(&self, src: CrateSource) { |
| let mut used_crate_sources = self.used_crate_sources.borrow_mut(); |
| if !used_crate_sources.contains(&src) { |
| used_crate_sources.push(src); |
| } |
| } |
| |
| pub fn opt_used_crate_source(&self, cnum: ast::CrateNum) |
| -> Option<CrateSource> { |
| self.used_crate_sources.borrow_mut() |
| .iter().find(|source| source.cnum == cnum).cloned() |
| } |
| |
| pub fn reset(&self) { |
| self.metas.borrow_mut().clear(); |
| self.extern_mod_crate_map.borrow_mut().clear(); |
| self.used_crate_sources.borrow_mut().clear(); |
| self.used_libraries.borrow_mut().clear(); |
| self.used_link_args.borrow_mut().clear(); |
| self.statically_included_foreign_items.borrow_mut().clear(); |
| } |
| |
| // This method is used when generating the command line to pass through to |
| // system linker. The linker expects undefined symbols on the left of the |
| // command line to be defined in libraries on the right, not the other way |
| // around. For more info, see some comments in the add_used_library function |
| // below. |
| // |
| // In order to get this left-to-right dependency ordering, we perform a |
| // topological sort of all crates putting the leaves at the right-most |
| // positions. |
| pub fn do_get_used_crates(&self, prefer: LinkagePreference) |
| -> Vec<(ast::CrateNum, Option<PathBuf>)> { |
| let mut ordering = Vec::new(); |
| fn visit(cstore: &CStore, cnum: ast::CrateNum, |
| ordering: &mut Vec<ast::CrateNum>) { |
| if ordering.contains(&cnum) { return } |
| let meta = cstore.get_crate_data(cnum); |
| for (_, &dep) in meta.cnum_map.borrow().iter() { |
| visit(cstore, dep, ordering); |
| } |
| ordering.push(cnum); |
| } |
| for (&num, _) in self.metas.borrow().iter() { |
| visit(self, num, &mut ordering); |
| } |
| info!("topological ordering: {:?}", ordering); |
| ordering.reverse(); |
| let mut libs = self.used_crate_sources.borrow() |
| .iter() |
| .map(|src| (src.cnum, match prefer { |
| LinkagePreference::RequireDynamic => src.dylib.clone().map(|p| p.0), |
| LinkagePreference::RequireStatic => src.rlib.clone().map(|p| p.0), |
| })) |
| .collect::<Vec<_>>(); |
| libs.sort_by(|&(a, _), &(b, _)| { |
| let a = ordering.iter().position(|x| *x == a); |
| let b = ordering.iter().position(|x| *x == b); |
| a.cmp(&b) |
| }); |
| libs |
| } |
| |
| pub fn add_used_library(&self, lib: String, kind: NativeLibraryKind) { |
| assert!(!lib.is_empty()); |
| self.used_libraries.borrow_mut().push((lib, kind)); |
| } |
| |
| pub fn get_used_libraries<'a>(&'a self) |
| -> &'a RefCell<Vec<(String, |
| NativeLibraryKind)>> { |
| &self.used_libraries |
| } |
| |
| pub fn add_used_link_args(&self, args: &str) { |
| for s in args.split(' ').filter(|s| !s.is_empty()) { |
| self.used_link_args.borrow_mut().push(s.to_string()); |
| } |
| } |
| |
| pub fn get_used_link_args<'a>(&'a self) -> &'a RefCell<Vec<String> > { |
| &self.used_link_args |
| } |
| |
| pub fn add_extern_mod_stmt_cnum(&self, |
| emod_id: ast::NodeId, |
| cnum: ast::CrateNum) { |
| self.extern_mod_crate_map.borrow_mut().insert(emod_id, cnum); |
| } |
| |
| pub fn add_statically_included_foreign_item(&self, id: ast::NodeId) { |
| self.statically_included_foreign_items.borrow_mut().insert(id); |
| } |
| |
| pub fn do_is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool { |
| self.statically_included_foreign_items.borrow().contains(&id) |
| } |
| |
| pub fn do_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> Option<ast::CrateNum> |
| { |
| self.extern_mod_crate_map.borrow().get(&emod_id).cloned() |
| } |
| } |
| |
| impl crate_metadata { |
| pub fn data<'a>(&'a self) -> &'a [u8] { self.data.as_slice() } |
| pub fn name(&self) -> String { decoder::get_crate_name(self.data()) } |
| pub fn hash(&self) -> Svh { decoder::get_crate_hash(self.data()) } |
| pub fn imported_filemaps<'a>(&'a self, codemap: &codemap::CodeMap) |
| -> Ref<'a, Vec<ImportedFileMap>> { |
| let filemaps = self.codemap_import_info.borrow(); |
| if filemaps.is_empty() { |
| drop(filemaps); |
| let filemaps = creader::import_codemap(codemap, &self.data); |
| |
| // This shouldn't borrow twice, but there is no way to downgrade RefMut to Ref. |
| *self.codemap_import_info.borrow_mut() = filemaps; |
| self.codemap_import_info.borrow() |
| } else { |
| filemaps |
| } |
| } |
| |
| pub fn with_local_path<T, F>(&self, f: F) -> T |
| where F: Fn(&[ast_map::PathElem]) -> T |
| { |
| let cpath = self.local_path.borrow(); |
| if cpath.is_empty() { |
| let name = ast_map::PathMod(token::intern(&self.name)); |
| f(&[name]) |
| } else { |
| f(cpath.as_slice()) |
| } |
| } |
| |
| pub fn update_local_path<'a, 'b>(&self, candidate: ast_map::PathElems<'a, 'b>) { |
| let mut cpath = self.local_path.borrow_mut(); |
| let cap = cpath.len(); |
| match cap { |
| 0 => *cpath = candidate.collect(), |
| 1 => (), |
| _ => { |
| let candidate: SmallVector<_> = candidate.collect(); |
| if candidate.len() < cap { |
| *cpath = candidate; |
| } |
| }, |
| } |
| } |
| |
| pub fn local_def_path(&self) -> ast_map::DefPath { |
| let local_def_path = self.local_def_path.borrow(); |
| if local_def_path.is_empty() { |
| let name = ast_map::DefPathData::DetachedCrate(token::intern(&self.name)); |
| vec![ast_map::DisambiguatedDefPathData { data: name, disambiguator: 0 }] |
| } else { |
| local_def_path.clone() |
| } |
| } |
| |
| pub fn update_local_def_path(&self, candidate: ast_map::DefPath) { |
| let mut local_def_path = self.local_def_path.borrow_mut(); |
| if local_def_path.is_empty() || candidate.len() < local_def_path.len() { |
| *local_def_path = candidate; |
| } |
| } |
| |
| pub fn is_allocator(&self) -> bool { |
| let attrs = decoder::get_crate_attributes(self.data()); |
| attr::contains_name(&attrs, "allocator") |
| } |
| |
| pub fn needs_allocator(&self) -> bool { |
| let attrs = decoder::get_crate_attributes(self.data()); |
| attr::contains_name(&attrs, "needs_allocator") |
| } |
| } |
| |
| impl MetadataBlob { |
| pub fn as_slice<'a>(&'a self) -> &'a [u8] { |
| let slice = match *self { |
| MetadataVec(ref vec) => &vec[..], |
| MetadataArchive(ref ar) => ar.as_slice(), |
| }; |
| if slice.len() < 4 { |
| &[] // corrupt metadata |
| } else { |
| let len = (((slice[0] as u32) << 24) | |
| ((slice[1] as u32) << 16) | |
| ((slice[2] as u32) << 8) | |
| ((slice[3] as u32) << 0)) as usize; |
| if len + 4 <= slice.len() { |
| &slice[4.. len + 4] |
| } else { |
| &[] // corrupt or old metadata |
| } |
| } |
| } |
| } |