1use std::{
2 fs,
3 io::{self, Read},
4 mem, panic,
5 path::Path,
6 sync::Arc,
7};
8
9use flux_common::bug;
10use flux_errors::FluxSession;
11use rustc_data_structures::{fx::FxHashMap, sync::HashMapExt};
12use rustc_hir::def_id::DefId;
13use rustc_middle::{
14 implement_ty_decoder,
15 ty::{self, TyCtxt, codec::TyDecoder},
16};
17use rustc_serialize::{
18 Decodable, Decoder as _,
19 opaque::{IntEncodedWithFixedSize, MemDecoder},
20};
21use rustc_session::StableCrateId;
22use rustc_span::{
23 BlobDecoder, BytePos, ByteSymbol, DUMMY_SP, SourceFile, Span, SpanDecoder, Symbol,
24 SyntaxContext,
25 def_id::{CrateNum, DefIndex},
26 hygiene::{HygieneDecodeContext, SyntaxContextKey},
27};
28
29use crate::{
30 AbsoluteBytePos, CrateMetadata, EncodedSourceFileId, Footer, METADATA_HEADER, SYMBOL_OFFSET,
31 SYMBOL_PREDEFINED, SYMBOL_STR, SourceFileIndex, TAG_FULL_SPAN, TAG_PARTIAL_SPAN,
32};
33
34struct DecodeContext<'a, 'tcx> {
35 tcx: TyCtxt<'tcx>,
36 opaque: MemDecoder<'a>,
37 file_index_to_file: FxHashMap<SourceFileIndex, Arc<SourceFile>>,
38 file_index_to_stable_id: FxHashMap<SourceFileIndex, EncodedSourceFileId>,
39 syntax_contexts: &'a FxHashMap<u32, AbsoluteBytePos>,
40 expn_data: FxHashMap<(StableCrateId, u32), AbsoluteBytePos>,
41 hygiene_context: &'a HygieneDecodeContext,
42}
43
44impl<'a, 'tcx> DecodeContext<'a, 'tcx> {
45 fn file_index_to_file(&mut self, index: SourceFileIndex) -> Arc<SourceFile> {
46 self.file_index_to_file
47 .entry(index)
48 .or_insert_with(|| {
49 let source_file_id = &self.file_index_to_stable_id[&index];
50 let source_file_cnum = self
51 .tcx
52 .stable_crate_id_to_crate_num(source_file_id.stable_crate_id);
53
54 self.tcx.import_source_files(source_file_cnum);
55 self.tcx
56 .sess
57 .source_map()
58 .source_file_by_stable_id(source_file_id.stable_source_file_id)
59 .expect("failed to lookup `SourceFile` in new context")
60 })
61 .clone()
62 }
63}
64
65pub(super) fn decode_crate_metadata<'tcx>(
66 tcx: TyCtxt<'tcx>,
67 sess: &FluxSession,
68 path: &Path,
69) -> Option<CrateMetadata<'tcx>> {
70 let mut file = match fs::File::open(path) {
71 Ok(file) => file,
72 Err(err) if let io::ErrorKind::NotFound = err.kind() => return None,
73 Err(err) => sess.emit_fatal(errors::DecodeFileError::new(path, err)),
74 };
75 let mut buf = vec![];
76 file.read_to_end(&mut buf)
77 .unwrap_or_else(|err| sess.emit_fatal(errors::DecodeFileError::new(path, err)));
78
79 if !buf.starts_with(METADATA_HEADER) {
82 sess.emit_fatal(errors::IncompatibleMetadata::new(path));
83 }
84
85 let metadata = catch_decode(|| {
86 let footer = {
87 let mut decoder = MemDecoder::new(&buf, 0).unwrap();
88 let footer_pos = decoder
89 .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
90 IntEncodedWithFixedSize::decode(d).0 as usize
91 });
92 decoder.with_position(footer_pos, Footer::decode)
93 };
94
95 let mut decoder = DecodeContext {
96 tcx,
97 opaque: MemDecoder::new(&buf, METADATA_HEADER.len()).unwrap(),
98 file_index_to_stable_id: footer.file_index_to_stable_id,
99 file_index_to_file: Default::default(),
100 syntax_contexts: &footer.syntax_contexts,
101 expn_data: footer.expn_data,
102 hygiene_context: &Default::default(),
103 };
104
105 CrateMetadata::decode(&mut decoder)
106 });
107
108 match metadata {
109 Ok(metadata) => Some(metadata),
110 Err(()) => sess.emit_fatal(errors::IncompatibleMetadata::new(path)),
111 }
112}
113
114fn catch_decode<R>(decode: impl FnOnce() -> R) -> Result<R, ()> {
117 let prev_hook = panic::take_hook();
118 panic::set_hook(Box::new(|_| {}));
119 let result = panic::catch_unwind(panic::AssertUnwindSafe(decode));
120 panic::set_hook(prev_hook);
121 result.map_err(|_| ())
122}
123
124implement_ty_decoder!(DecodeContext<'a, 'tcx>);
125
126impl BlobDecoder for DecodeContext<'_, '_> {
127 fn decode_symbol(&mut self) -> Symbol {
128 let tag = self.read_u8();
129
130 match tag {
131 SYMBOL_STR => {
132 let s = self.read_str();
133 Symbol::intern(s)
134 }
135 SYMBOL_OFFSET => {
136 let pos = self.read_usize();
138
139 self.opaque.with_position(pos, |d| {
141 let s = d.read_str();
142 Symbol::intern(s)
143 })
144 }
145 SYMBOL_PREDEFINED => {
146 let symbol_index = self.read_u32();
147 Symbol::new(symbol_index)
148 }
149 _ => unreachable!(),
150 }
151 }
152
153 fn decode_byte_symbol(&mut self) -> ByteSymbol {
154 ByteSymbol::intern(self.read_byte_str())
155 }
156
157 fn decode_def_index(&mut self) -> DefIndex {
158 DefIndex::from_u32(self.read_u32())
159 }
160}
161
162impl SpanDecoder for DecodeContext<'_, '_> {
163 fn decode_attr_id(&mut self) -> rustc_ast::AttrId {
164 self.tcx.sess.psess.attr_id_generator.mk_attr_id()
165 }
166
167 fn decode_crate_num(&mut self) -> CrateNum {
168 let stable_id = StableCrateId::decode(self);
169 self.tcx.stable_crate_id_to_crate_num(stable_id)
170 }
171
172 fn decode_def_id(&mut self) -> DefId {
173 DefId { krate: Decodable::decode(self), index: Decodable::decode(self) }
174 }
175
176 fn decode_syntax_context(&mut self) -> SyntaxContext {
177 let syntax_contexts = self.syntax_contexts;
178 rustc_span::hygiene::decode_syntax_context(self, self.hygiene_context, |this, id| {
179 let pos = syntax_contexts.get(&id).unwrap();
182 this.with_position(pos.to_usize(), SyntaxContextKey::decode)
183 })
184 }
185
186 fn decode_expn_id(&mut self) -> rustc_span::ExpnId {
187 let stable_id = StableCrateId::decode(self);
188 let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id);
189 let index = u32::decode(self);
190
191 rustc_span::hygiene::decode_expn_id(cnum, index, |_| {
192 let pos = self.expn_data.get(&(stable_id, index)).unwrap();
193 self.with_position(pos.to_usize(), |decoder| {
194 let data = rustc_span::ExpnData::decode(decoder);
195 let hash = rustc_span::ExpnHash::decode(decoder);
196 (data, hash)
197 })
198 })
199 }
200
201 fn decode_span(&mut self) -> rustc_span::Span {
202 let ctxt = SyntaxContext::decode(self);
203 let tag: u8 = Decodable::decode(self);
204
205 if tag == TAG_PARTIAL_SPAN {
206 return DUMMY_SP.with_ctxt(ctxt);
207 }
208
209 debug_assert!(tag == TAG_FULL_SPAN);
210
211 let source_file_index = SourceFileIndex::decode(self);
212 let lo = BytePos::decode(self);
213 let len = BytePos::decode(self);
214 let file = self.file_index_to_file(source_file_index);
215 let lo = file.start_pos + lo;
216 let hi = lo + len;
217
218 Span::new(lo, hi, ctxt, None)
219 }
220}
221
222impl<'tcx> TyDecoder<'tcx> for DecodeContext<'_, 'tcx> {
223 const CLEAR_CROSS_CRATE: bool = true;
224
225 fn interner(&self) -> TyCtxt<'tcx> {
226 self.tcx
227 }
228
229 fn cached_ty_for_shorthand<F>(&mut self, shorthand: usize, or_insert_with: F) -> ty::Ty<'tcx>
230 where
231 F: FnOnce(&mut Self) -> ty::Ty<'tcx>,
232 {
233 let tcx = self.tcx;
234
235 let cache_key = ty::CReaderCacheKey { cnum: None, pos: shorthand };
236
237 if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) {
238 return ty;
239 }
240
241 let ty = or_insert_with(self);
242 tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty);
244 ty
245 }
246
247 fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
248 where
249 F: FnOnce(&mut Self) -> R,
250 {
251 let new_opaque = self.opaque.split_at(pos);
252 let old_opaque = mem::replace(&mut self.opaque, new_opaque);
253 let r = f(self);
254 self.opaque = old_opaque;
255 r
256 }
257
258 fn decode_alloc_id(&mut self) -> rustc_middle::mir::interpret::AllocId {
259 bug!("Encoding `interpret::AllocId` is not supported")
260 }
261}
262
263mod errors {
264 use std::{io, path::Path};
265
266 use flux_errors::E0999;
267 use flux_macros::Diagnostic;
268
269 #[derive(Diagnostic)]
270 #[diag(metadata_decode_file_error, code = E0999)]
271 pub(super) struct DecodeFileError<'a> {
272 path: &'a Path,
273 err: io::Error,
274 }
275
276 impl<'a> DecodeFileError<'a> {
277 pub(super) fn new(path: &'a Path, err: io::Error) -> Self {
278 Self { path, err }
279 }
280 }
281
282 #[derive(Diagnostic)]
283 #[diag(metadata_incompatible_metadata, code = E0999)]
284 pub(super) struct IncompatibleMetadata<'a> {
285 path: &'a Path,
286 }
287
288 impl<'a> IncompatibleMetadata<'a> {
289 pub(super) fn new(path: &'a Path) -> Self {
290 Self { path }
291 }
292 }
293}