1use std::{collections::hash_map::Entry, sync::Arc};
2
3use flux_middle::global_env::GlobalEnv;
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6use rustc_metadata::errors::FailCreateFileEncoder;
7use rustc_middle::{
8 bug,
9 ty::{self, TyCtxt, codec::TyEncoder},
10};
11use rustc_serialize::{Encodable, Encoder, opaque, opaque::IntEncodedWithFixedSize};
12use rustc_session::config::CrateType;
13use rustc_span::{
14 ByteSymbol, ExpnId, SourceFile, Span, SpanEncoder, Symbol, SyntaxContext,
15 def_id::{CrateNum, DefIndex},
16 hygiene::{ExpnIndex, HygieneEncodeContext},
17};
18
19use crate::{
20 AbsoluteBytePos, CrateMetadata, EncodedSourceFileId, Footer, METADATA_HEADER, SYMBOL_OFFSET,
21 SYMBOL_PREDEFINED, SYMBOL_STR, SourceFileIndex, TAG_FULL_SPAN, TAG_PARTIAL_SPAN,
22 rustc_middle::dep_graph::DepContext,
23};
24
25struct EncodeContext<'a, 'tcx> {
26 tcx: TyCtxt<'tcx>,
27 opaque: opaque::FileEncoder,
28 type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>,
29 predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
30 file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>,
31 is_proc_macro: bool,
32 hygiene_ctxt: &'a HygieneEncodeContext,
33 symbol_index_table: FxHashMap<u32, usize>,
34}
35
36impl EncodeContext<'_, '_> {
37 fn source_file_index(&mut self, source_file: Arc<SourceFile>) -> SourceFileIndex {
38 self.file_to_file_index[&(&*source_file as *const SourceFile)]
39 }
40
41 fn encode_symbol_or_byte_symbol(
42 &mut self,
43 index: u32,
44 emit_str_or_byte_str: impl Fn(&mut Self),
45 ) {
46 if Symbol::is_predefined(index) {
49 self.opaque.emit_u8(SYMBOL_PREDEFINED);
50 self.opaque.emit_u32(index);
51 } else {
52 match self.symbol_index_table.entry(index) {
54 Entry::Vacant(o) => {
55 self.opaque.emit_u8(SYMBOL_STR);
56 let pos = self.opaque.position();
57 o.insert(pos);
58 emit_str_or_byte_str(self);
59 }
60 Entry::Occupied(o) => {
61 let x = *o.get();
62 self.emit_u8(SYMBOL_OFFSET);
63 self.emit_usize(x);
64 }
65 }
66 }
67 }
68}
69
70fn file_indices(
71 tcx: TyCtxt,
72) -> (FxHashMap<*const SourceFile, SourceFileIndex>, FxHashMap<SourceFileIndex, EncodedSourceFileId>)
73{
74 let files = tcx.sess.source_map().files();
75 let mut file_to_file_index =
76 FxHashMap::with_capacity_and_hasher(files.len(), Default::default());
77 let mut file_index_to_stable_id =
78 FxHashMap::with_capacity_and_hasher(files.len(), Default::default());
79 use rustc_span::def_id::LOCAL_CRATE;
80 let source_map = tcx.sess.source_map();
81 let working_directory = &tcx.sess.opts.working_dir;
82 let local_crate_stable_id = tcx.stable_crate_id(LOCAL_CRATE);
83
84 for (index, file) in files.iter().enumerate() {
89 let index = SourceFileIndex(index as u32);
90 let file_ptr: *const SourceFile = &**file as *const _;
91 file_to_file_index.insert(file_ptr, index);
92
93 let mut adapted_source_file = (**file).clone();
94 if adapted_source_file.cnum == LOCAL_CRATE {
95 use rustc_span::FileName;
96 match file.name {
97 FileName::Real(ref original_file_name) => {
98 let adapted_file_name = source_map
99 .path_mapping()
100 .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
101
102 adapted_source_file.name = FileName::Real(adapted_file_name);
103 }
104 _ => {
105 }
107 };
108 use rustc_span::StableSourceFileId;
109 adapted_source_file.stable_id = StableSourceFileId::from_filename_for_export(
110 &adapted_source_file.name,
111 local_crate_stable_id,
112 );
113 }
114
115 let source_file_id = EncodedSourceFileId::new(tcx, &adapted_source_file);
116 file_index_to_stable_id.insert(index, source_file_id);
117 }
118
119 (file_to_file_index, file_index_to_stable_id)
120}
121
122pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
123 let (file_to_file_index, file_index_to_stable_id) = file_indices(genv.tcx());
124
125 let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
126 genv.tcx()
127 .sess
128 .dcx()
129 .emit_fatal(FailCreateFileEncoder { err })
130 });
131
132 encoder.emit_raw_bytes(METADATA_HEADER);
133
134 let crate_root = CrateMetadata::new(genv);
135
136 let hygiene_ctxt = HygieneEncodeContext::default();
137 let tcx = genv.tcx();
138 let mut ecx = EncodeContext {
139 tcx,
140 opaque: encoder,
141 type_shorthands: Default::default(),
142 predicate_shorthands: Default::default(),
143 file_to_file_index,
144 is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
145 hygiene_ctxt: &hygiene_ctxt,
146 symbol_index_table: Default::default(),
147 };
148
149 crate_root.encode(&mut ecx);
150
151 let mut syntax_contexts = FxHashMap::default();
153 let mut expn_data = FxHashMap::default();
154
155 ecx.hygiene_ctxt.encode(
157 &mut ecx,
158 |encoder, index, ctxt_data| {
159 let pos = AbsoluteBytePos::new(encoder.position());
160 ctxt_data.encode(encoder);
161 syntax_contexts.insert(index, pos);
162 },
163 |encoder, expn_id, data, hash| {
164 let pos = AbsoluteBytePos::new(encoder.position());
165 data.encode(encoder);
166 hash.encode(encoder);
167 expn_data.insert((tcx.stable_crate_id(expn_id.krate), expn_id.local_id.as_u32()), pos);
168 },
169 );
170
171 let footer_pos = ecx.position() as u64;
173 let footer = Footer { file_index_to_stable_id, syntax_contexts, expn_data };
174 footer.encode(&mut ecx);
175
176 IntEncodedWithFixedSize(footer_pos).encode(&mut ecx.opaque);
179
180 ecx.opaque.finish().unwrap();
185}
186
187impl SpanEncoder for EncodeContext<'_, '_> {
188 fn encode_crate_num(&mut self, crate_num: CrateNum) {
189 if crate_num != LOCAL_CRATE && self.is_proc_macro {
190 bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
191 }
192 self.tcx.stable_crate_id(crate_num).encode(self);
193 }
194
195 fn encode_def_index(&mut self, def_index: DefIndex) {
196 self.emit_u32(def_index.as_u32());
197 }
198
199 fn encode_def_id(&mut self, def_id: DefId) {
200 def_id.krate.encode(self);
201 def_id.index.encode(self);
202 }
203
204 fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
205 rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
206 }
207
208 fn encode_expn_id(&mut self, expn_id: ExpnId) {
209 if expn_id.krate == LOCAL_CRATE {
210 self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
215 }
216 expn_id.krate.encode(self);
217 expn_id.local_id.encode(self);
218 }
219
220 fn encode_span(&mut self, span: Span) {
222 let span = span.data();
223 span.ctxt.encode(self);
224
225 if span.is_dummy() {
226 return TAG_PARTIAL_SPAN.encode(self);
227 }
228
229 let source_file = self.tcx.sess().source_map().lookup_source_file(span.lo);
230 if !source_file.contains(span.hi) {
231 return TAG_PARTIAL_SPAN.encode(self);
234 }
235
236 let lo = span.lo - source_file.start_pos;
237 let len = span.hi - span.lo;
238 let source_file_index = self.source_file_index(source_file);
239
240 TAG_FULL_SPAN.encode(self);
241 source_file_index.encode(self);
242 lo.encode(self);
243 len.encode(self);
244 }
245
246 fn encode_symbol(&mut self, sym: Symbol) {
247 self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str()));
248 }
249
250 fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) {
251 self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| {
252 this.emit_byte_str(byte_sym.as_byte_str());
253 });
254 }
255}
256
257impl<'tcx> TyEncoder<'tcx> for EncodeContext<'_, 'tcx> {
258 const CLEAR_CROSS_CRATE: bool = true;
259
260 fn position(&self) -> usize {
261 self.opaque.position()
262 }
263
264 fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
265 &mut self.type_shorthands
266 }
267
268 fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
269 &mut self.predicate_shorthands
270 }
271
272 fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
273 bug!("Encoding `interpret::AllocId` is not supported");
274 }
277}
278
279impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
280 fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
281 s.emit_u32(self.as_u32());
282 }
283}
284
285macro_rules! encoder_methods {
286 ($($name:ident($ty:ty);)*) => {
287 $(fn $name(&mut self, value: $ty) {
288 self.opaque.$name(value)
289 })*
290 }
291}
292
293impl Encoder for EncodeContext<'_, '_> {
294 encoder_methods! {
295 emit_usize(usize);
296 emit_u128(u128);
297 emit_u64(u64);
298 emit_u32(u32);
299 emit_u16(u16);
300 emit_u8(u8);
301
302 emit_isize(isize);
303 emit_i128(i128);
304 emit_i64(i64);
305 emit_i32(i32);
306 emit_i16(i16);
307 emit_i8(i8);
308
309 emit_bool(bool);
310 emit_char(char);
311 emit_str(&str);
312 emit_raw_bytes(&[u8]);
313 }
314}