1use std::collections::hash_map::Entry;
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};
12use rustc_session::config::CrateType;
13use rustc_span::{
14 ByteSymbol, ExpnId, FileName, SourceFile, Span, SpanEncoder, StableSourceFileId, Symbol,
15 SyntaxContext,
16 def_id::{CrateNum, DefIndex},
17 hygiene::{ExpnIndex, HygieneEncodeContext},
18};
19
20use crate::{CrateMetadata, METADATA_HEADER, SYMBOL_OFFSET, SYMBOL_PREDEFINED, SYMBOL_STR};
21
22struct EncodeContext<'a, 'tcx> {
23 tcx: TyCtxt<'tcx>,
24 opaque: opaque::FileEncoder,
25 type_shorthands: FxHashMap<ty::Ty<'tcx>, usize>,
26 predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
27 is_proc_macro: bool,
28 hygiene_ctxt: &'a HygieneEncodeContext,
29 symbol_index_table: FxHashMap<u32, usize>,
30}
31
32impl EncodeContext<'_, '_> {
33 fn encode_symbol_or_byte_symbol(
34 &mut self,
35 index: u32,
36 emit_str_or_byte_str: impl Fn(&mut Self),
37 ) {
38 if Symbol::is_predefined(index) {
41 self.opaque.emit_u8(SYMBOL_PREDEFINED);
42 self.opaque.emit_u32(index);
43 } else {
44 match self.symbol_index_table.entry(index) {
46 Entry::Vacant(o) => {
47 self.opaque.emit_u8(SYMBOL_STR);
48 let pos = self.opaque.position();
49 o.insert(pos);
50 emit_str_or_byte_str(self);
51 }
52 Entry::Occupied(o) => {
53 let x = *o.get();
54 self.emit_u8(SYMBOL_OFFSET);
55 self.emit_usize(x);
56 }
57 }
58 }
59 }
60}
61
62pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
63 let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
64 genv.tcx()
65 .sess
66 .dcx()
67 .emit_fatal(FailCreateFileEncoder { err })
68 });
69
70 encoder.emit_raw_bytes(METADATA_HEADER);
71
72 let crate_root = CrateMetadata::new(genv);
73
74 let hygiene_ctxt = HygieneEncodeContext::default();
75 let mut ecx = EncodeContext {
76 tcx: genv.tcx(),
77 opaque: encoder,
78 type_shorthands: Default::default(),
79 predicate_shorthands: Default::default(),
80 is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
81 hygiene_ctxt: &hygiene_ctxt,
82 symbol_index_table: Default::default(),
83 };
84
85 crate_root.encode(&mut ecx);
86
87 ecx.opaque.finish().unwrap();
88}
89
90impl SpanEncoder for EncodeContext<'_, '_> {
91 fn encode_crate_num(&mut self, crate_num: CrateNum) {
92 if crate_num != LOCAL_CRATE && self.is_proc_macro {
93 bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
94 }
95 self.tcx.stable_crate_id(crate_num).encode(self);
96 }
97
98 fn encode_def_index(&mut self, def_index: DefIndex) {
99 self.emit_u32(def_index.as_u32());
100 }
101
102 fn encode_def_id(&mut self, def_id: DefId) {
103 def_id.krate.encode(self);
104 def_id.index.encode(self);
105 }
106
107 fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
108 rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
109 }
110
111 fn encode_expn_id(&mut self, expn_id: ExpnId) {
112 if expn_id.krate == LOCAL_CRATE {
113 self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
118 }
119 expn_id.krate.encode(self);
120 expn_id.local_id.encode(self);
121 }
122
123 fn encode_span(&mut self, span: Span) {
125 let sm = self.tcx.sess.source_map();
126 for bp in [span.lo(), span.hi()] {
127 let sf = sm.lookup_source_file(bp);
128 let ssfi = stable_source_file_id_for_export(self.tcx, &sf);
129 ssfi.encode(self);
130 (bp - sf.start_pos).encode(self);
135 }
136 }
137
138 fn encode_symbol(&mut self, sym: Symbol) {
139 self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str()));
140 }
141
142 fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) {
143 self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| {
144 this.emit_byte_str(byte_sym.as_byte_str());
145 });
146 }
147}
148
149impl<'tcx> TyEncoder<'tcx> for EncodeContext<'_, 'tcx> {
150 const CLEAR_CROSS_CRATE: bool = true;
151
152 fn position(&self) -> usize {
153 self.opaque.position()
154 }
155
156 fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
157 &mut self.type_shorthands
158 }
159
160 fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
161 &mut self.predicate_shorthands
162 }
163
164 fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
165 bug!("Encoding `interpret::AllocId` is not supported");
166 }
169}
170
171impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
172 fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
173 s.emit_u32(self.as_u32());
174 }
175}
176
177macro_rules! encoder_methods {
178 ($($name:ident($ty:ty);)*) => {
179 $(fn $name(&mut self, value: $ty) {
180 self.opaque.$name(value)
181 })*
182 }
183}
184
185impl Encoder for EncodeContext<'_, '_> {
186 encoder_methods! {
187 emit_usize(usize);
188 emit_u128(u128);
189 emit_u64(u64);
190 emit_u32(u32);
191 emit_u16(u16);
192 emit_u8(u8);
193
194 emit_isize(isize);
195 emit_i128(i128);
196 emit_i64(i64);
197 emit_i32(i32);
198 emit_i16(i16);
199 emit_i8(i8);
200
201 emit_bool(bool);
202 emit_char(char);
203 emit_str(&str);
204 emit_raw_bytes(&[u8]);
205 }
206}
207
208fn stable_source_file_id_for_export(tcx: TyCtxt, sf: &SourceFile) -> StableSourceFileId {
209 let working_directory = &tcx.sess.opts.working_dir;
210 let crate_stable_id = tcx.stable_crate_id(sf.cnum);
211 let mut filename = sf.name.clone();
212 if let FileName::Real(original_file_name) = filename {
213 let adapted_file_name = tcx
214 .sess
215 .source_map()
216 .path_mapping()
217 .to_embeddable_absolute_path(original_file_name.clone(), working_directory);
218
219 filename = FileName::Real(adapted_file_name);
220 }
221 StableSourceFileId::from_filename_for_export(&filename, crate_stable_id)
222}