1 // Copyright 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.
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.
11 use rustc::dep_graph::DepNode;
12 use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX};
13 use rustc::hir::svh::Svh;
14 use rustc::ich::Fingerprint;
15 use rustc::ty::TyCtxt;
16 use rustc_data_structures::fx::FxHashMap;
17 use rustc_data_structures::flock;
18 use rustc_serialize::Decodable;
19 use rustc_serialize::opaque::Decoder;
21 use IncrementalHashesMap;
24 use super::file_format;
29 pub struct HashContext<'a, 'tcx: 'a> {
30 pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
31 incremental_hashes_map: &'a IncrementalHashesMap,
32 item_metadata_hashes: FxHashMap<DefId, Fingerprint>,
33 crate_hashes: FxHashMap<CrateNum, Svh>,
34 global_metadata_hashes: FxHashMap<DepNode<DefId>, Fingerprint>,
37 impl<'a, 'tcx> HashContext<'a, 'tcx> {
38 pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
39 incremental_hashes_map: &'a IncrementalHashesMap)
43 incremental_hashes_map: incremental_hashes_map,
44 item_metadata_hashes: FxHashMap(),
45 crate_hashes: FxHashMap(),
46 global_metadata_hashes: FxHashMap(),
50 pub fn is_hashable(dep_node: &DepNode<DefId>) -> bool {
55 DepNode::FileMap(..) =>
57 DepNode::MetaData(def_id) |
58 DepNode::GlobalMetaData(def_id, _) => !def_id.is_local(),
63 pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<Fingerprint> {
66 Some(self.incremental_hashes_map[dep_node])
69 // HIR nodes (which always come from our crate) are an input:
70 DepNode::Hir(def_id) |
71 DepNode::HirBody(def_id) => {
72 assert!(def_id.is_local(),
73 "cannot hash HIR for non-local def-id {:?} => {:?}",
75 self.tcx.item_path_str(def_id));
77 Some(self.incremental_hashes_map[dep_node])
80 DepNode::FileMap(def_id, ref name) => {
81 if def_id.is_local() {
82 Some(self.incremental_hashes_map[dep_node])
84 Some(self.metadata_hash(DepNode::FileMap(def_id, name.clone()),
86 |this| &mut this.global_metadata_hashes))
90 // MetaData from other crates is an *input* to us.
91 // MetaData nodes from *our* crates are an *output*; we
92 // don't hash them, but we do compute a hash for them and
93 // save it for others to use.
94 DepNode::MetaData(def_id) if !def_id.is_local() => {
95 Some(self.metadata_hash(def_id,
97 |this| &mut this.item_metadata_hashes))
100 DepNode::GlobalMetaData(def_id, kind) => {
101 Some(self.metadata_hash(DepNode::GlobalMetaData(def_id, kind),
103 |this| &mut this.global_metadata_hashes))
107 // Other kinds of nodes represent computed by-products
108 // that we don't hash directly; instead, they should
109 // have some transitive dependency on a Hir or
110 // MetaData node, so we'll just hash that
116 fn metadata_hash<K, C>(&mut self,
121 where K: Hash + Eq + Debug,
122 C: Fn(&mut Self) -> &mut FxHashMap<K, Fingerprint>,
124 debug!("metadata_hash(key={:?})", key);
126 debug_assert!(cnum != LOCAL_CRATE);
128 // check whether we have a result cached for this def-id
129 if let Some(&hash) = cache(self).get(&key) {
133 // check whether we did not find detailed metadata for this
134 // krate; in that case, we just use the krate's overall hash
135 if let Some(&svh) = self.crate_hashes.get(&cnum) {
136 // micro-"optimization": avoid a cache miss if we ask
137 // for metadata from this particular def-id again.
138 let fingerprint = svh_to_fingerprint(svh);
139 cache(self).insert(key, fingerprint);
144 // otherwise, load the data and repeat.
145 self.load_data(cnum);
146 assert!(self.crate_hashes.contains_key(&cnum));
150 fn load_data(&mut self, cnum: CrateNum) {
151 debug!("load_data(cnum={})", cnum);
153 let svh = self.tcx.sess.cstore.crate_hash(cnum);
154 let old = self.crate_hashes.insert(cnum, svh);
155 debug!("load_data: svh={}", svh);
156 assert!(old.is_none(), "loaded data for crate {:?} twice", cnum);
158 if let Some(session_dir) = find_metadata_hashes_for(self.tcx, cnum) {
159 debug!("load_data: session_dir={:?}", session_dir);
161 // Lock the directory we'll be reading the hashes from.
162 let lock_file_path = lock_file_path(&session_dir);
163 let _lock = match flock::Lock::new(&lock_file_path,
165 false, // don't create the lock-file
166 false) { // shared lock
169 debug!("Could not acquire lock on `{}` while trying to \
170 load metadata hashes: {}",
171 lock_file_path.display(),
174 // Could not acquire the lock. The directory is probably in
175 // in the process of being deleted. It's OK to just exit
176 // here. It's the same scenario as if the file had not
177 // existed in the first place.
182 let hashes_file_path = metadata_hash_import_path(&session_dir);
184 match file_format::read_file(self.tcx.sess, &hashes_file_path)
187 match self.load_from_data(cnum, &data, svh) {
190 bug!("decoding error in dep-graph from `{}`: {}",
191 &hashes_file_path.display(), err);
196 // If the file is not found, that's ok.
200 &format!("could not load dep information from `{}`: {}",
201 hashes_file_path.display(), err));
207 fn load_from_data(&mut self,
210 expected_svh: Svh) -> Result<(), String> {
211 debug!("load_from_data(cnum={})", cnum);
213 // Load up the hashes for the def-ids from this crate.
214 let mut decoder = Decoder::new(data, 0);
215 let svh_in_hashes_file = Svh::decode(&mut decoder)?;
217 if svh_in_hashes_file != expected_svh {
218 // We should not be able to get here. If we do, then
219 // `fs::find_metadata_hashes_for()` has messed up.
220 bug!("mismatch between SVH in crate and SVH in incr. comp. hashes")
223 let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder)?;
224 for serialized_hash in serialized_hashes.entry_hashes {
225 // the hashes are stored with just a def-index, which is
226 // always relative to the old crate; convert that to use
227 // our internal crate number
228 let def_id = DefId { krate: cnum, index: serialized_hash.def_index };
230 // record the hash for this dep-node
231 let old = self.item_metadata_hashes.insert(def_id, serialized_hash.hash);
232 debug!("load_from_data: def_id={:?} hash={}", def_id, serialized_hash.hash);
233 assert!(old.is_none(), "already have hash for {:?}", def_id);
236 for (dep_node, fingerprint) in serialized_hashes.global_hashes {
237 // Here we need to remap the CrateNum in the DepNode.
238 let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX };
239 let dep_node = match dep_node {
240 DepNode::GlobalMetaData(_, kind) => DepNode::GlobalMetaData(def_id, kind),
241 DepNode::FileMap(_, name) => DepNode::FileMap(def_id, name),
243 bug!("unexpected DepNode variant: {:?}", other)
247 // record the hash for this dep-node
248 debug!("load_from_data: def_node={:?} hash={}", dep_node, fingerprint);
249 let old = self.global_metadata_hashes.insert(dep_node.clone(), fingerprint);
250 assert!(old.is_none(), "already have hash for {:?}", dep_node);
257 fn svh_to_fingerprint(svh: Svh) -> Fingerprint {
258 Fingerprint::from_smaller_hash(svh.as_u64())