]> git.lizzy.rs Git - rust.git/blob - src/librustc/metadata/creader.rs
auto merge of #13049 : alexcrichton/rust/io-fill, r=huonw
[rust.git] / src / librustc / metadata / creader.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #[allow(non_camel_case_types)];
12
13 //! Validates all used crates and extern libraries and loads their metadata
14
15 use back::link;
16 use back::svh::Svh;
17 use driver::{driver, session};
18 use driver::session::Session;
19 use metadata::csearch;
20 use metadata::cstore;
21 use metadata::decoder;
22 use metadata::loader;
23 use metadata::loader::Os;
24
25 use std::cell::RefCell;
26 use collections::HashMap;
27 use syntax::ast;
28 use syntax::abi;
29 use syntax::attr;
30 use syntax::attr::AttrMetaMethods;
31 use syntax::codemap::{Span};
32 use syntax::diagnostic::SpanHandler;
33 use syntax::ext::base::{CrateLoader, MacroCrate};
34 use syntax::parse::token::{IdentInterner, InternedString};
35 use syntax::parse::token;
36 use syntax::crateid::CrateId;
37 use syntax::visit;
38
39 // Traverses an AST, reading all the information about use'd crates and extern
40 // libraries necessary for later resolving, typechecking, linking, etc.
41 pub fn read_crates(sess: &Session,
42                    krate: &ast::Crate,
43                    os: loader::Os,
44                    intr: @IdentInterner) {
45     let mut e = Env {
46         sess: sess,
47         os: os,
48         crate_cache: @RefCell::new(Vec::new()),
49         next_crate_num: 1,
50         intr: intr
51     };
52     visit_crate(&e, krate);
53     visit::walk_crate(&mut e, krate, ());
54     dump_crates(e.crate_cache.borrow().as_slice());
55     warn_if_multiple_versions(&mut e,
56                               sess.diagnostic(),
57                               e.crate_cache.borrow().as_slice());
58 }
59
60 impl<'a> visit::Visitor<()> for Env<'a> {
61     fn visit_view_item(&mut self, a: &ast::ViewItem, _: ()) {
62         visit_view_item(self, a);
63         visit::walk_view_item(self, a, ());
64     }
65     fn visit_item(&mut self, a: &ast::Item, _: ()) {
66         visit_item(self, a);
67         visit::walk_item(self, a, ());
68     }
69 }
70
71 #[deriving(Clone)]
72 struct cache_entry {
73     cnum: ast::CrateNum,
74     span: Span,
75     hash: Svh,
76     crate_id: CrateId,
77 }
78
79 fn dump_crates(crate_cache: &[cache_entry]) {
80     debug!("resolved crates:");
81     for entry in crate_cache.iter() {
82         debug!("cnum: {:?}", entry.cnum);
83         debug!("span: {:?}", entry.span);
84         debug!("hash: {:?}", entry.hash);
85     }
86 }
87
88 fn warn_if_multiple_versions(e: &mut Env,
89                              diag: &SpanHandler,
90                              crate_cache: &[cache_entry]) {
91     if crate_cache.len() != 0u {
92         let name = crate_cache[crate_cache.len() - 1].crate_id.name.clone();
93
94         let (matches, non_matches) = crate_cache.partitioned(|entry|
95             name == entry.crate_id.name);
96
97         assert!(!matches.is_empty());
98
99         if matches.len() != 1u {
100             diag.handler().warn(
101                 format!("using multiple versions of crate `{}`", name));
102             for match_ in matches.iter() {
103                 diag.span_note(match_.span, "used here");
104                 loader::note_crateid_attr(diag, &match_.crate_id);
105             }
106         }
107
108         warn_if_multiple_versions(e, diag, non_matches);
109     }
110 }
111
112 struct Env<'a> {
113     sess: &'a Session,
114     os: loader::Os,
115     crate_cache: @RefCell<Vec<cache_entry>>,
116     next_crate_num: ast::CrateNum,
117     intr: @IdentInterner
118 }
119
120 fn visit_crate(e: &Env, c: &ast::Crate) {
121     for a in c.attrs.iter().filter(|m| m.name().equiv(&("link_args"))) {
122         match a.value_str() {
123             Some(ref linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
124             None => { /* fallthrough */ }
125         }
126     }
127 }
128
129 fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
130     let should_load = i.attrs.iter().all(|attr| {
131         attr.name().get() != "phase" ||
132             attr.meta_item_list().map_or(false, |phases| {
133                 attr::contains_name(phases.as_slice(), "link")
134             })
135     });
136
137     if !should_load {
138         return;
139     }
140
141     match extract_crate_info(e, i) {
142         Some(info) => {
143             let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
144                                      i.span);
145             e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
146         }
147         None => ()
148     }
149 }
150
151 struct CrateInfo {
152     ident: ~str,
153     crate_id: CrateId,
154     id: ast::NodeId,
155 }
156
157 fn extract_crate_info(e: &Env, i: &ast::ViewItem) -> Option<CrateInfo> {
158     match i.node {
159         ast::ViewItemExternCrate(ident, ref path_opt, id) => {
160             let ident = token::get_ident(ident);
161             debug!("resolving extern crate stmt. ident: {:?} path_opt: {:?}",
162                    ident, path_opt);
163             let crate_id = match *path_opt {
164                 Some((ref path_str, _)) => {
165                     let crateid: Option<CrateId> = from_str(path_str.get());
166                     match crateid {
167                         None => {
168                             e.sess.span_err(i.span, "malformed crate id");
169                             return None
170                         }
171                         Some(id) => id
172                     }
173                 }
174                 None => from_str(ident.get().to_str()).unwrap()
175             };
176             Some(CrateInfo {
177                 ident: ident.get().to_str(),
178                 crate_id: crate_id,
179                 id: id,
180             })
181         }
182         _ => None
183     }
184 }
185
186 fn visit_item(e: &Env, i: &ast::Item) {
187     match i.node {
188         ast::ItemForeignMod(ref fm) => {
189             if fm.abis.is_rust() || fm.abis.is_intrinsic() {
190                 return;
191             }
192
193             // First, add all of the custom link_args attributes
194             let link_args = i.attrs.iter()
195                 .filter_map(|at| if at.name().equiv(&("link_args")) {
196                     Some(at)
197                 } else {
198                     None
199                 })
200                 .collect::<~[&ast::Attribute]>();
201             for m in link_args.iter() {
202                 match m.value_str() {
203                     Some(linkarg) => e.sess.cstore.add_used_link_args(linkarg.get()),
204                     None => { /* fallthrough */ }
205                 }
206             }
207
208             // Next, process all of the #[link(..)]-style arguments
209             let link_args = i.attrs.iter()
210                 .filter_map(|at| if at.name().equiv(&("link")) {
211                     Some(at)
212                 } else {
213                     None
214                 })
215                 .collect::<~[&ast::Attribute]>();
216             for m in link_args.iter() {
217                 match m.meta_item_list() {
218                     Some(items) => {
219                         let kind = items.iter().find(|k| {
220                             k.name().equiv(&("kind"))
221                         }).and_then(|a| a.value_str());
222                         let kind = match kind {
223                             Some(k) => {
224                                 if k.equiv(&("static")) {
225                                     cstore::NativeStatic
226                                 } else if e.sess.targ_cfg.os == abi::OsMacos &&
227                                           k.equiv(&("framework")) {
228                                     cstore::NativeFramework
229                                 } else if k.equiv(&("framework")) {
230                                     e.sess.span_err(m.span,
231                                         "native frameworks are only available \
232                                          on OSX targets");
233                                     cstore::NativeUnknown
234                                 } else {
235                                     e.sess.span_err(m.span,
236                                         format!("unknown kind: `{}`", k));
237                                     cstore::NativeUnknown
238                                 }
239                             }
240                             None => cstore::NativeUnknown
241                         };
242                         let n = items.iter().find(|n| {
243                             n.name().equiv(&("name"))
244                         }).and_then(|a| a.value_str());
245                         let n = match n {
246                             Some(n) => n,
247                             None => {
248                                 e.sess.span_err(m.span,
249                                     "#[link(...)] specified without \
250                                      `name = \"foo\"`");
251                                 InternedString::new("foo")
252                             }
253                         };
254                         if n.get().is_empty() {
255                             e.sess.span_err(m.span, "#[link(name = \"\")] given with empty name");
256                         } else {
257                             e.sess.cstore.add_used_library(n.get().to_owned(), kind);
258                         }
259                     }
260                     None => {}
261                 }
262             }
263         }
264         _ => { }
265     }
266 }
267
268 fn existing_match(e: &Env, crate_id: &CrateId,
269                   hash: Option<&Svh>) -> Option<ast::CrateNum> {
270     for c in e.crate_cache.borrow().iter() {
271         if !crate_id.matches(&c.crate_id) { continue }
272         match hash {
273             Some(hash) if *hash != c.hash => {}
274             Some(..) | None => return Some(c.cnum)
275         }
276     }
277     None
278 }
279
280 fn resolve_crate(e: &mut Env,
281                  root_ident: Option<&str>,
282                  ident: &str,
283                  crate_id: &CrateId,
284                  hash: Option<&Svh>,
285                  span: Span)
286               -> ast::CrateNum {
287     match existing_match(e, crate_id, hash) {
288         None => {
289             let id_hash = link::crate_id_hash(crate_id);
290             let mut load_ctxt = loader::Context {
291                 sess: e.sess,
292                 span: span,
293                 ident: ident,
294                 crate_id: crate_id,
295                 id_hash: id_hash,
296                 hash: hash.map(|a| &*a),
297                 os: e.os,
298                 intr: e.intr,
299                 rejected_via_hash: false,
300             };
301             let loader::Library {
302                 dylib, rlib, metadata
303             } = load_ctxt.load_library_crate(root_ident);
304
305             let crate_id = decoder::get_crate_id(metadata.as_slice());
306             let hash = decoder::get_crate_hash(metadata.as_slice());
307
308             // Claim this crate number and cache it
309             let cnum = e.next_crate_num;
310             e.crate_cache.borrow_mut().push(cache_entry {
311                 cnum: cnum,
312                 span: span,
313                 hash: hash,
314                 crate_id: crate_id,
315             });
316             e.next_crate_num += 1;
317
318             // Maintain a reference to the top most crate.
319             let root_crate = match root_ident {
320                 Some(c) => c,
321                 None => load_ctxt.ident.clone()
322             };
323
324             // Now resolve the crates referenced by this crate
325             let cnum_map = resolve_crate_deps(e,
326             Some(root_crate),
327             metadata.as_slice(),
328             span);
329
330             let cmeta = @cstore::crate_metadata {
331                 name: load_ctxt.crate_id.name.to_owned(),
332                 data: metadata,
333                 cnum_map: cnum_map,
334                 cnum: cnum
335             };
336
337             e.sess.cstore.set_crate_data(cnum, cmeta);
338             e.sess.cstore.add_used_crate_source(cstore::CrateSource {
339                 dylib: dylib,
340                 rlib: rlib,
341                 cnum: cnum,
342             });
343             cnum
344         }
345         Some(cnum) => cnum
346     }
347 }
348
349 // Go through the crate metadata and load any crates that it references
350 fn resolve_crate_deps(e: &mut Env,
351                       root_ident: Option<&str>,
352                       cdata: &[u8], span : Span)
353                    -> cstore::cnum_map {
354     debug!("resolving deps of external crate");
355     // The map from crate numbers in the crate we're resolving to local crate
356     // numbers
357     let mut cnum_map = HashMap::new();
358     let r = decoder::get_crate_deps(cdata);
359     for dep in r.iter() {
360         let extrn_cnum = dep.cnum;
361         debug!("resolving dep crate {} hash: `{}`", dep.crate_id, dep.hash);
362         let local_cnum = resolve_crate(e, root_ident,
363                                        dep.crate_id.name.as_slice(),
364                                        &dep.crate_id,
365                                        Some(&dep.hash),
366                                        span);
367         cnum_map.insert(extrn_cnum, local_cnum);
368     }
369     return @RefCell::new(cnum_map);
370 }
371
372 pub struct Loader<'a> {
373     priv env: Env<'a>,
374 }
375
376 impl<'a> Loader<'a> {
377     pub fn new(sess: &'a Session) -> Loader<'a> {
378         let os = driver::get_os(driver::host_triple()).unwrap();
379         let os = session::sess_os_to_meta_os(os);
380         Loader {
381             env: Env {
382                 sess: sess,
383                 os: os,
384                 crate_cache: @RefCell::new(Vec::new()),
385                 next_crate_num: 1,
386                 intr: token::get_ident_interner(),
387             }
388         }
389     }
390 }
391
392 impl<'a> CrateLoader for Loader<'a> {
393     fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
394         let info = extract_crate_info(&self.env, krate).unwrap();
395         let cnum = resolve_crate(&mut self.env, None, info.ident,
396                                  &info.crate_id, None, krate.span);
397         let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
398         MacroCrate {
399             lib: library.dylib,
400             cnum: cnum
401         }
402     }
403
404     fn get_exported_macros(&mut self, cnum: ast::CrateNum) -> Vec<~str> {
405         csearch::get_exported_macros(&self.env.sess.cstore, cnum).move_iter()
406                                                                  .collect()
407     }
408
409     fn get_registrar_symbol(&mut self, cnum: ast::CrateNum) -> Option<~str> {
410         let cstore = &self.env.sess.cstore;
411         csearch::get_macro_registrar_fn(cstore, cnum)
412             .map(|did| csearch::get_symbol(cstore, did))
413     }
414 }