Skip to main content

charon_lib/
export.rs

1pub mod multi_target;
2
3use crate::ast::*;
4use crate::transform::TransformCtx;
5use serde::{Deserialize, Deserializer, Serialize};
6use serde_state::{DeserializeState, SerializeState};
7use std::fs::File;
8use std::path::Path;
9
10/// The data of a generic crate. We serialize this to pass it to `charon-ml`, so this must be as
11/// stable as possible. This is used for both ULLBC and LLBC.
12#[derive(SerializeState, DeserializeState)]
13pub struct CrateData {
14    /// The version of charon currently being used. `charon-ml` inspects this and errors if it is
15    /// trying to read an incompatible version (for now we compare versions for equality).
16    #[serde_state(stateless)]
17    pub charon_version: CharonVersion,
18    pub translated: TranslatedCrate,
19    #[serde_state(stateless)]
20    #[charon::opaque] // Don't change, this would break version detection for old charon-ml
21    /// If there were errors, this contains only a partial description of the input crate.
22    pub has_errors: bool,
23}
24
25impl Serialize for CrateData {
26    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
27    where
28        S: serde::Serializer,
29    {
30        if self.translated.options.no_dedup_serialized_ast {
31            self.serialize_state(&(), serializer)
32        } else {
33            let state = HashConsDedupSerializer::default();
34            self.serialize_state(&state, serializer)
35        }
36    }
37}
38
39impl<'de> Deserialize<'de> for CrateData {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: Deserializer<'de>,
43    {
44        // Always provide the state, just in case.
45        let state = HashConsDedupSerializer::default();
46        Self::deserialize_state(&state, deserializer)
47    }
48}
49
50#[derive(Serialize)]
51pub struct CharonVersion(pub String);
52
53impl<'de> Deserialize<'de> for CharonVersion {
54    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55    where
56        D: Deserializer<'de>,
57    {
58        use serde::de::Error;
59        let version = String::deserialize(deserializer)?;
60        if version != crate::VERSION {
61            return Err(D::Error::custom(format!(
62                "Incompatible version of charon: \
63                this program supports llbc emitted by charon v{} \
64                but attempted to read a file emitted by charon v{}",
65                crate::VERSION,
66                version,
67            )));
68        }
69        Ok(CharonVersion(version))
70    }
71}
72
73impl CrateData {
74    pub fn new(ctx: TransformCtx) -> Self {
75        CrateData {
76            charon_version: CharonVersion(crate::VERSION.to_owned()),
77            has_errors: ctx.has_errors(),
78            translated: ctx.translated,
79        }
80    }
81
82    /// Export the translated definitions to a JSON file.
83    #[allow(clippy::result_unit_err)]
84    pub fn serialize_to_file(&self, target_filename: &Path) -> Result<(), ()> {
85        // Create the directory, if necessary (note that if the target directory
86        // is not specified, there is no need to create it: otherwise we
87        // couldn't have read the input file in the first place).
88        let target_dir = target_filename.parent().unwrap();
89        match std::fs::create_dir_all(target_dir) {
90            Ok(()) => (),
91            Err(_) => {
92                error!("Could not create the directory: {:?}", target_dir);
93                return Err(());
94            }
95        };
96
97        // Create the file.
98        let std::io::Result::Ok(outfile) = File::create(target_filename) else {
99            error!("Could not open: {:?}", target_filename);
100            return Err(());
101        };
102        // Write to the file.
103        let res = serde_json::to_writer(&outfile, self);
104        match res {
105            Ok(()) => {}
106            Err(err) => {
107                error!("Could not serialize to `{target_filename:?}`: {err:?}");
108                return Err(());
109            }
110        }
111
112        // We canonicalize (i.e., make absolute) the path before printing it; this makes it clearer
113        // to the user where to find the file.
114        let target_filename = std::fs::canonicalize(target_filename).unwrap();
115        if self.has_errors {
116            info!(
117                "Generated the partial (because we encountered errors) file: {}",
118                target_filename.to_str().unwrap()
119            );
120        } else {
121            info!("Generated the file: {}", target_filename.to_str().unwrap());
122        }
123        Ok(())
124    }
125
126    pub fn deserialize_from_file(path: &std::path::Path) -> anyhow::Result<Self> {
127        use crate::export::CrateData;
128        use anyhow::Context;
129        use serde::Deserialize;
130        use std::fs::File;
131        use std::io::BufReader;
132        let file = File::open(path)
133            .with_context(|| format!("Failed to read llbc file {}", path.display()))?;
134        let reader = BufReader::new(file);
135        let mut deserializer = serde_json::Deserializer::from_reader(reader);
136        // Deserialize without recursion limit.
137        deserializer.disable_recursion_limit();
138        // Grow stack space as needed.
139        let deserializer = serde_stacker::Deserializer::new(&mut deserializer);
140        Ok(CrateData::deserialize(deserializer)?)
141    }
142}