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