cairo_lang_compiler/
db.rs1use anyhow::{Result, anyhow, bail};
2use cairo_lang_defs::db::{init_defs_group, init_external_files};
3use cairo_lang_diagnostics::Maybe;
4use cairo_lang_filesystem::cfg::CfgSet;
5use cairo_lang_filesystem::db::{CORELIB_VERSION, FilesGroup, init_dev_corelib, init_files_group};
6use cairo_lang_filesystem::detect::detect_corelib;
7use cairo_lang_filesystem::flag::{Flag, FlagsGroup};
8use cairo_lang_filesystem::ids::{CrateId, FlagLongId};
9use cairo_lang_lowering::db::init_lowering_group;
10use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
11use cairo_lang_lowering::optimizations::config::Optimizations;
12use cairo_lang_lowering::utils::InliningStrategy;
13use cairo_lang_project::ProjectConfig;
14use cairo_lang_runnable_utils::builder::RunnableBuilder;
15use cairo_lang_semantic::db::{PluginSuiteInput, init_semantic_group};
16use cairo_lang_semantic::inline_macros::get_default_plugin_suite;
17use cairo_lang_semantic::plugin::PluginSuite;
18use cairo_lang_sierra_generator::db::init_sierra_gen_group;
19use cairo_lang_sierra_generator::program_generator::get_dummy_program_for_size_estimation;
20use cairo_lang_utils::CloneableDatabase;
21use salsa::Database;
22
23use crate::project::update_crate_roots_from_project_config;
24
25fn estimate_code_size(
28 db: &dyn Database,
29 function_id: ConcreteFunctionWithBodyId<'_>,
30) -> Maybe<isize> {
31 let program = get_dummy_program_for_size_estimation(db, function_id)?;
32
33 let n_dummy_functions = program.funcs.len() - 1;
35
36 let builder = match RunnableBuilder::new(program, Default::default()) {
38 Ok(builder) => builder,
39 Err(err) => {
40 if err.is_ap_overflow_error() {
41 return Ok(isize::MAX);
45 }
46 if std::env::var("CAIRO_DEBUG_SIERRA_GEN").is_ok() {
47 return Ok(isize::MAX);
50 }
51
52 panic!(
53 "Internal compiler error when compiling function `{}` to CASM: `{err}`. You can \
54 set the CAIRO_DEBUG_SIERRA_GEN environment if you want to finish the compilation \
55 and debug the Sierra program.",
56 function_id.full_path(db)
57 );
58 }
59 };
60 let casm = builder.casm_program();
61 let total_size = casm.instructions.iter().map(|inst| inst.body.op_size()).sum::<usize>();
62
63 const DUMMY_FUNCTION_SIZE: usize = 3;
65 Ok((total_size - (n_dummy_functions * DUMMY_FUNCTION_SIZE)).try_into().unwrap_or(0))
66}
67
68#[salsa::db]
69#[derive(Clone)]
70pub struct RootDatabase {
71 storage: salsa::Storage<RootDatabase>,
72}
73#[salsa::db]
74impl salsa::Database for RootDatabase {}
75impl CloneableDatabase for RootDatabase {
76 fn dyn_clone(&self) -> Box<dyn CloneableDatabase> {
77 Box::new(self.clone())
78 }
79}
80
81impl RootDatabase {
82 fn new(default_plugin_suite: PluginSuite, optimizations: Optimizations) -> Self {
83 let mut res = Self { storage: Default::default() };
84 init_external_files(&mut res);
85 init_files_group(&mut res);
86 init_lowering_group(&mut res, optimizations, Some(estimate_code_size));
87 init_defs_group(&mut res);
88 init_semantic_group(&mut res);
89 init_sierra_gen_group(&mut res);
90
91 res.set_default_plugins_from_suite(default_plugin_suite);
92
93 res
94 }
95
96 pub fn empty() -> Self {
97 Self::builder().clear_plugins().build().unwrap()
98 }
99
100 pub fn builder() -> RootDatabaseBuilder {
101 RootDatabaseBuilder::new()
102 }
103
104 pub fn snapshot(&self) -> RootDatabase {
106 RootDatabase { storage: self.storage.clone() }
107 }
108}
109
110impl Default for RootDatabase {
111 fn default() -> Self {
112 Self::builder().build().unwrap()
113 }
114}
115
116#[derive(Clone, Debug)]
117pub struct RootDatabaseBuilder {
118 default_plugin_suite: PluginSuite,
119 detect_corelib: bool,
120 auto_withdraw_gas: bool,
121 panic_backtrace: bool,
122 unsafe_panic: bool,
123 project_config: Option<Box<ProjectConfig>>,
124 cfg_set: Option<CfgSet>,
125 optimizations: Optimizations,
126}
127
128impl RootDatabaseBuilder {
129 fn new() -> Self {
130 Self {
131 default_plugin_suite: get_default_plugin_suite(),
132 detect_corelib: false,
133 auto_withdraw_gas: true,
134 panic_backtrace: false,
135 unsafe_panic: false,
136 project_config: None,
137 cfg_set: None,
138 optimizations: Optimizations::enabled_with_default_movable_functions(
139 InliningStrategy::Default,
140 ),
141 }
142 }
143
144 pub fn with_default_plugin_suite(&mut self, suite: PluginSuite) -> &mut Self {
145 self.default_plugin_suite.add(suite);
146 self
147 }
148
149 pub fn clear_plugins(&mut self) -> &mut Self {
150 self.default_plugin_suite = get_default_plugin_suite();
151 self
152 }
153
154 pub fn with_optimizations(&mut self, optimizations: Optimizations) -> &mut Self {
155 self.optimizations = optimizations;
156 self
157 }
158
159 pub fn detect_corelib(&mut self) -> &mut Self {
160 self.detect_corelib = true;
161 self
162 }
163
164 pub fn with_project_config(&mut self, config: ProjectConfig) -> &mut Self {
165 self.project_config = Some(Box::new(config));
166 self
167 }
168
169 pub fn with_cfg(&mut self, cfg_set: impl Into<CfgSet>) -> &mut Self {
170 self.cfg_set = Some(cfg_set.into());
171 self
172 }
173
174 pub fn skip_auto_withdraw_gas(&mut self) -> &mut Self {
175 self.auto_withdraw_gas = false;
176 self
177 }
178
179 pub fn with_panic_backtrace(&mut self) -> &mut Self {
180 self.panic_backtrace = true;
181 self
182 }
183
184 pub fn with_unsafe_panic(&mut self) -> &mut Self {
185 self.unsafe_panic = true;
186 self
187 }
188
189 pub fn build(&mut self) -> Result<RootDatabase> {
190 let mut db =
195 RootDatabase::new(self.default_plugin_suite.clone(), self.optimizations.clone());
196
197 if let Some(cfg_set) = &self.cfg_set {
198 db.use_cfg(cfg_set);
199 }
200
201 if self.detect_corelib {
202 let path =
203 detect_corelib().ok_or_else(|| anyhow!("Failed to find development corelib."))?;
204 init_dev_corelib(&mut db, path)
205 }
206
207 let add_withdraw_gas_flag_id = FlagLongId(Flag::ADD_WITHDRAW_GAS.into());
208 db.set_flag(add_withdraw_gas_flag_id, Some(Flag::AddWithdrawGas(self.auto_withdraw_gas)));
209 let panic_backtrace_flag_id = FlagLongId(Flag::PANIC_BACKTRACE.into());
210 db.set_flag(panic_backtrace_flag_id, Some(Flag::PanicBacktrace(self.panic_backtrace)));
211 let unsafe_panic_flag_id = FlagLongId(Flag::UNSAFE_PANIC.into());
212 db.set_flag(unsafe_panic_flag_id, Some(Flag::UnsafePanic(self.unsafe_panic)));
213
214 if let Some(config) = &self.project_config {
215 update_crate_roots_from_project_config(&mut db, config.as_ref());
216 }
217 validate_corelib(&db)?;
218
219 Ok(db)
220 }
221}
222
223pub fn validate_corelib(db: &(dyn salsa::Database + 'static)) -> Result<()> {
225 let Some(config) = db.crate_config(CrateId::core(db)) else {
226 return Ok(());
227 };
228 let Some(found) = &config.settings.version else {
229 return Ok(());
230 };
231 let Ok(expected) = semver::Version::parse(CORELIB_VERSION) else {
232 return Ok(());
233 };
234 if found == &expected {
235 return Ok(());
236 }
237 let path_part = match &config.root {
238 cairo_lang_filesystem::ids::Directory::Real(path) => {
239 format!(" for `{}`", path.to_string_lossy())
240 }
241 cairo_lang_filesystem::ids::Directory::Virtual { .. } => "".to_string(),
242 };
243 bail!("Corelib version mismatch: expected `{expected}`, found `{found}`{path_part}.");
244}