1use std::{
2 fs,
3 io::{self, Read},
4 mem,
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) {
79 bug!("incompatible metadata version");
80 }
81
82 let footer = {
83 let mut decoder = MemDecoder::new(&buf, 0).unwrap();
84 let footer_pos = decoder
85 .with_position(decoder.len() - IntEncodedWithFixedSize::ENCODED_SIZE, |d| {
86 IntEncodedWithFixedSize::decode(d).0 as usize
87 });
88 decoder.with_position(footer_pos, Footer::decode)
89 };
90
91 let mut decoder = DecodeContext {
92 tcx,
93 opaque: MemDecoder::new(&buf, METADATA_HEADER.len()).unwrap(),
94 file_index_to_stable_id: footer.file_index_to_stable_id,
95 file_index_to_file: Default::default(),
96 syntax_contexts: &footer.syntax_contexts,
97 expn_data: footer.expn_data,
98 hygiene_context: &Default::default(),
99 };
100
101 Some(CrateMetadata::decode(&mut decoder))
102}
103
104implement_ty_decoder!(DecodeContext<'a, 'tcx>);
105
106impl SpanDecoder for DecodeContext<'_, '_> {
107 fn decode_attr_id(&mut self) -> rustc_ast::AttrId {
108 self.tcx.sess.psess.attr_id_generator.mk_attr_id()
109 }
110
111 fn decode_crate_num(&mut self) -> CrateNum {
112 let stable_id = StableCrateId::decode(self);
113 self.tcx.stable_crate_id_to_crate_num(stable_id)
114 }
115
116 fn decode_def_index(&mut self) -> DefIndex {
117 DefIndex::from_u32(self.read_u32())
118 }
119
120 fn decode_def_id(&mut self) -> DefId {
121 DefId { krate: Decodable::decode(self), index: Decodable::decode(self) }
122 }
123
124 fn decode_syntax_context(&mut self) -> SyntaxContext {
125 let syntax_contexts = self.syntax_contexts;
126 rustc_span::hygiene::decode_syntax_context(self, self.hygiene_context, |this, id| {
127 let pos = syntax_contexts.get(&id).unwrap();
130 this.with_position(pos.to_usize(), SyntaxContextKey::decode)
131 })
132 }
133
134 fn decode_expn_id(&mut self) -> rustc_span::ExpnId {
135 let stable_id = StableCrateId::decode(self);
136 let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id);
137 let index = u32::decode(self);
138
139 rustc_span::hygiene::decode_expn_id(cnum, index, |_| {
140 let pos = self.expn_data.get(&(stable_id, index)).unwrap();
141 self.with_position(pos.to_usize(), |decoder| {
142 let data = rustc_span::ExpnData::decode(decoder);
143 let hash = rustc_span::ExpnHash::decode(decoder);
144 (data, hash)
145 })
146 })
147 }
148
149 fn decode_span(&mut self) -> rustc_span::Span {
150 let ctxt = SyntaxContext::decode(self);
151 let tag: u8 = Decodable::decode(self);
152
153 if tag == TAG_PARTIAL_SPAN {
154 return DUMMY_SP.with_ctxt(ctxt);
155 }
156
157 debug_assert!(tag == TAG_FULL_SPAN);
158
159 let source_file_index = SourceFileIndex::decode(self);
160 let lo = BytePos::decode(self);
161 let len = BytePos::decode(self);
162 let file = self.file_index_to_file(source_file_index);
163 let lo = file.start_pos + lo;
164 let hi = lo + len;
165
166 Span::new(lo, hi, ctxt, None)
167 }
168
169 fn decode_symbol(&mut self) -> rustc_span::Symbol {
170 let tag = self.read_u8();
171
172 match tag {
173 SYMBOL_STR => {
174 let s = self.read_str();
175 Symbol::intern(s)
176 }
177 SYMBOL_OFFSET => {
178 let pos = self.read_usize();
180
181 self.opaque.with_position(pos, |d| {
183 let s = d.read_str();
184 Symbol::intern(s)
185 })
186 }
187 SYMBOL_PREDEFINED => {
188 let symbol_index = self.read_u32();
189 Symbol::new(symbol_index)
190 }
191 _ => unreachable!(),
192 }
193 }
194
195 fn decode_byte_symbol(&mut self) -> ByteSymbol {
196 ByteSymbol::intern(self.read_byte_str())
197 }
198}
199
200impl<'tcx> TyDecoder<'tcx> for DecodeContext<'_, 'tcx> {
201 const CLEAR_CROSS_CRATE: bool = true;
202
203 fn interner(&self) -> TyCtxt<'tcx> {
204 self.tcx
205 }
206
207 fn cached_ty_for_shorthand<F>(&mut self, shorthand: usize, or_insert_with: F) -> ty::Ty<'tcx>
208 where
209 F: FnOnce(&mut Self) -> ty::Ty<'tcx>,
210 {
211 let tcx = self.tcx;
212
213 let cache_key = ty::CReaderCacheKey { cnum: None, pos: shorthand };
214
215 if let Some(&ty) = tcx.ty_rcache.borrow().get(&cache_key) {
216 return ty;
217 }
218
219 let ty = or_insert_with(self);
220 tcx.ty_rcache.borrow_mut().insert_same(cache_key, ty);
222 ty
223 }
224
225 fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
226 where
227 F: FnOnce(&mut Self) -> R,
228 {
229 let new_opaque = self.opaque.split_at(pos);
230 let old_opaque = mem::replace(&mut self.opaque, new_opaque);
231 let r = f(self);
232 self.opaque = old_opaque;
233 r
234 }
235
236 fn decode_alloc_id(&mut self) -> rustc_middle::mir::interpret::AllocId {
237 bug!("Encoding `interpret::AllocId` is not supported")
238 }
239}
240
241mod errors {
242 use std::{io, path::Path};
243
244 use flux_errors::E0999;
245 use flux_macros::Diagnostic;
246
247 #[derive(Diagnostic)]
248 #[diag(metadata_decode_file_error, code = E0999)]
249 pub(super) struct DecodeFileError<'a> {
250 path: &'a Path,
251 err: io::Error,
252 }
253
254 impl<'a> DecodeFileError<'a> {
255 pub(super) fn new(path: &'a Path, err: io::Error) -> Self {
256 Self { path, err }
257 }
258 }
259}