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