Skip to main content

flux_metadata/
encoder.rs

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/byte symbol is predefined, emit tag and symbol index
47        // TODO: we could also encode flux predefined symbols like this
48        if Symbol::is_predefined(index) {
49            self.opaque.emit_u8(SYMBOL_PREDEFINED);
50            self.opaque.emit_u32(index);
51        } else {
52            // otherwise write it as string or as offset to it
53            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 local_crate_stable_id = tcx.stable_crate_id(LOCAL_CRATE);
81
82    // This portion of the code is adapted from the rustc metadata encoder, while the rest of
83    // the code in this file is based off the rustc incremental cache encoder.
84    //
85    // Probably we should refactor the code to be exclusively based on the metadata encoder
86    for (index, file) in files.iter().enumerate() {
87        let index = SourceFileIndex(index as u32);
88        let file_ptr: *const SourceFile = &**file as *const _;
89        file_to_file_index.insert(file_ptr, index);
90
91        let mut adapted_source_file = (**file).clone();
92        if adapted_source_file.cnum == LOCAL_CRATE {
93            use rustc_span::FileName;
94            match file.name {
95                FileName::Real(ref original_file_name) => {
96                    let mut adapted_file_name = original_file_name.clone();
97                    adapted_file_name.update_for_crate_metadata();
98
99                    adapted_source_file.name = FileName::Real(adapted_file_name);
100                }
101                _ => {
102                    // expanded code, not from a file
103                }
104            };
105            use rustc_span::StableSourceFileId;
106            adapted_source_file.stable_id = StableSourceFileId::from_filename_for_export(
107                &adapted_source_file.name,
108                local_crate_stable_id,
109            );
110        }
111
112        let source_file_id = EncodedSourceFileId::new(tcx, &adapted_source_file);
113        file_index_to_stable_id.insert(index, source_file_id);
114    }
115
116    (file_to_file_index, file_index_to_stable_id)
117}
118
119pub fn encode_metadata(genv: GlobalEnv, path: &std::path::Path) {
120    let (file_to_file_index, file_index_to_stable_id) = file_indices(genv.tcx());
121
122    let mut encoder = opaque::FileEncoder::new(path).unwrap_or_else(|err| {
123        genv.tcx()
124            .sess
125            .dcx()
126            .emit_fatal(FailCreateFileEncoder { err })
127    });
128
129    encoder.emit_raw_bytes(METADATA_HEADER);
130
131    let crate_root = CrateMetadata::new(genv);
132
133    let hygiene_ctxt = HygieneEncodeContext::default();
134    let tcx = genv.tcx();
135    let mut ecx = EncodeContext {
136        tcx,
137        opaque: encoder,
138        type_shorthands: Default::default(),
139        predicate_shorthands: Default::default(),
140        file_to_file_index,
141        is_proc_macro: genv.tcx().crate_types().contains(&CrateType::ProcMacro),
142        hygiene_ctxt: &hygiene_ctxt,
143        symbol_index_table: Default::default(),
144    };
145
146    crate_root.encode(&mut ecx);
147
148    // BEGIN: CREUSOT-footer
149    let mut syntax_contexts = FxHashMap::default();
150    let mut expn_data = FxHashMap::default();
151
152    // Encode all hygiene data (`SyntaxContextData` and `ExpnData`) from the current session.
153    ecx.hygiene_ctxt.encode(
154        &mut ecx,
155        |encoder, index, ctxt_data| {
156            let pos = AbsoluteBytePos::new(encoder.position());
157            ctxt_data.encode(encoder);
158            syntax_contexts.insert(index, pos);
159        },
160        |encoder, expn_id, data, hash| {
161            let pos = AbsoluteBytePos::new(encoder.position());
162            data.encode(encoder);
163            hash.encode(encoder);
164            expn_data.insert((tcx.stable_crate_id(expn_id.krate), expn_id.local_id.as_u32()), pos);
165        },
166    );
167
168    // Encode the file footer.
169    let footer_pos = ecx.position() as u64;
170    let footer = Footer { file_index_to_stable_id, syntax_contexts, expn_data };
171    footer.encode(&mut ecx);
172
173    // Encode the position of the footer as the last 8 bytes of the
174    // file so we know where to look for it.
175    IntEncodedWithFixedSize(footer_pos).encode(&mut ecx.opaque);
176
177    // DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address
178    // of the footer must be the last thing in the data stream.
179    // END: CREUSOT-footer
180
181    ecx.opaque.finish().unwrap();
182}
183
184impl SpanEncoder for EncodeContext<'_, '_> {
185    fn encode_crate_num(&mut self, crate_num: CrateNum) {
186        if crate_num != LOCAL_CRATE && self.is_proc_macro {
187            bug!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
188        }
189        self.tcx.stable_crate_id(crate_num).encode(self);
190    }
191
192    fn encode_def_index(&mut self, def_index: DefIndex) {
193        self.emit_u32(def_index.as_u32());
194    }
195
196    fn encode_def_id(&mut self, def_id: DefId) {
197        def_id.krate.encode(self);
198        def_id.index.encode(self);
199    }
200
201    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
202        rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
203    }
204
205    fn encode_expn_id(&mut self, expn_id: ExpnId) {
206        if expn_id.krate == LOCAL_CRATE {
207            // We will only write details for local expansions. Non-local expansions will fetch
208            // data from the corresponding crate's metadata.
209            // FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
210            // metadata from proc-macro crates.
211            self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
212        }
213        expn_id.krate.encode(self);
214        expn_id.local_id.encode(self);
215    }
216
217    // Code adapted from creusot
218    fn encode_span(&mut self, span: Span) {
219        let span = span.data();
220        span.ctxt.encode(self);
221
222        if span.is_dummy() {
223            return TAG_PARTIAL_SPAN.encode(self);
224        }
225
226        let source_file = self.tcx.sess().source_map().lookup_source_file(span.lo);
227        if !source_file.contains(span.hi) {
228            // Unfortunately, macro expansion still sometimes generates Spans
229            // that malformed in this way.
230            return TAG_PARTIAL_SPAN.encode(self);
231        }
232
233        let lo = span.lo - source_file.start_pos;
234        let len = span.hi - span.lo;
235        let source_file_index = self.source_file_index(source_file);
236
237        TAG_FULL_SPAN.encode(self);
238        source_file_index.encode(self);
239        lo.encode(self);
240        len.encode(self);
241    }
242
243    fn encode_symbol(&mut self, sym: Symbol) {
244        self.encode_symbol_or_byte_symbol(sym.as_u32(), |this| this.emit_str(sym.as_str()));
245    }
246
247    fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol) {
248        self.encode_symbol_or_byte_symbol(byte_sym.as_u32(), |this| {
249            this.emit_byte_str(byte_sym.as_byte_str());
250        });
251    }
252}
253
254impl<'tcx> TyEncoder<'tcx> for EncodeContext<'_, 'tcx> {
255    const CLEAR_CROSS_CRATE: bool = true;
256
257    fn position(&self) -> usize {
258        self.opaque.position()
259    }
260
261    fn type_shorthands(&mut self) -> &mut FxHashMap<ty::Ty<'tcx>, usize> {
262        &mut self.type_shorthands
263    }
264
265    fn predicate_shorthands(&mut self) -> &mut FxHashMap<ty::PredicateKind<'tcx>, usize> {
266        &mut self.predicate_shorthands
267    }
268
269    fn encode_alloc_id(&mut self, _alloc_id: &rustc_middle::mir::interpret::AllocId) {
270        bug!("Encoding `interpret::AllocId` is not supported");
271        // let (index, _) = self.interpret_allocs.insert_full(*alloc_id);
272        // index.encode(self);
273    }
274}
275
276impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
277    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
278        s.emit_u32(self.as_u32());
279    }
280}
281
282macro_rules! encoder_methods {
283    ($($name:ident($ty:ty);)*) => {
284        $(fn $name(&mut self, value: $ty) {
285            self.opaque.$name(value)
286        })*
287    }
288}
289
290impl Encoder for EncodeContext<'_, '_> {
291    encoder_methods! {
292        emit_usize(usize);
293        emit_u128(u128);
294        emit_u64(u64);
295        emit_u32(u32);
296        emit_u16(u16);
297        emit_u8(u8);
298
299        emit_isize(isize);
300        emit_i128(i128);
301        emit_i64(i64);
302        emit_i32(i32);
303        emit_i16(i16);
304        emit_i8(i8);
305
306        emit_bool(bool);
307        emit_char(char);
308        emit_str(&str);
309        emit_raw_bytes(&[u8]);
310    }
311}