cairo_lang_compiler/
db.rs1use std::sync::Arc;
2
3use anyhow::{Result, anyhow, bail};
4use cairo_lang_defs::db::{init_defs_group, init_external_files};
5use cairo_lang_diagnostics::Maybe;
6use cairo_lang_filesystem::cfg::CfgSet;
7use cairo_lang_filesystem::db::{CORELIB_VERSION, FilesGroup, init_dev_corelib, init_files_group};
8use cairo_lang_filesystem::detect::detect_corelib;
9use cairo_lang_filesystem::flag::Flag;
10use cairo_lang_filesystem::ids::{CrateId, FlagLongId};
11use cairo_lang_lowering::db::init_lowering_group;
12use cairo_lang_lowering::ids::ConcreteFunctionWithBodyId;
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 salsa::Database;
21
22use crate::InliningStrategy;
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 {}
75
76impl RootDatabase {
77 fn new(default_plugin_suite: PluginSuite, inlining_strategy: InliningStrategy) -> Self {
78 let mut res = Self { storage: Default::default() };
79 init_external_files(&mut res);
80 init_files_group(&mut res);
81 init_lowering_group(&mut res, inlining_strategy, Some(estimate_code_size));
82 init_defs_group(&mut res);
83 init_semantic_group(&mut res);
84 init_sierra_gen_group(&mut res);
85
86 res.set_default_plugins_from_suite(default_plugin_suite);
87
88 res
89 }
90
91 pub fn empty() -> Self {
92 Self::builder().clear_plugins().build().unwrap()
93 }
94
95 pub fn builder() -> RootDatabaseBuilder {
96 RootDatabaseBuilder::new()
97 }
98
99 pub fn snapshot(&self) -> RootDatabase {
101 RootDatabase { storage: self.storage.clone() }
102 }
103}
104
105impl Default for RootDatabase {
106 fn default() -> Self {
107 Self::builder().build().unwrap()
108 }
109}
110
111#[derive(Clone, Debug)]
112pub struct RootDatabaseBuilder {
113 default_plugin_suite: PluginSuite,
114 detect_corelib: bool,
115 auto_withdraw_gas: bool,
116 panic_backtrace: bool,
117 unsafe_panic: bool,
118 project_config: Option<Box<ProjectConfig>>,
119 cfg_set: Option<CfgSet>,
120 inlining_strategy: InliningStrategy,
121}
122
123impl RootDatabaseBuilder {
124 fn new() -> Self {
125 Self {
126 default_plugin_suite: get_default_plugin_suite(),
127 detect_corelib: false,
128 auto_withdraw_gas: true,
129 panic_backtrace: false,
130 unsafe_panic: false,
131 project_config: None,
132 cfg_set: None,
133 inlining_strategy: InliningStrategy::Default,
134 }
135 }
136
137 pub fn with_default_plugin_suite(&mut self, suite: PluginSuite) -> &mut Self {
138 self.default_plugin_suite.add(suite);
139 self
140 }
141
142 pub fn clear_plugins(&mut self) -> &mut Self {
143 self.default_plugin_suite = get_default_plugin_suite();
144 self
145 }
146
147 pub fn with_inlining_strategy(&mut self, inlining_strategy: InliningStrategy) -> &mut Self {
148 self.inlining_strategy = inlining_strategy;
149 self
150 }
151
152 pub fn detect_corelib(&mut self) -> &mut Self {
153 self.detect_corelib = true;
154 self
155 }
156
157 pub fn with_project_config(&mut self, config: ProjectConfig) -> &mut Self {
158 self.project_config = Some(Box::new(config));
159 self
160 }
161
162 pub fn with_cfg(&mut self, cfg_set: impl Into<CfgSet>) -> &mut Self {
163 self.cfg_set = Some(cfg_set.into());
164 self
165 }
166
167 pub fn skip_auto_withdraw_gas(&mut self) -> &mut Self {
168 self.auto_withdraw_gas = false;
169 self
170 }
171
172 pub fn with_panic_backtrace(&mut self) -> &mut Self {
173 self.panic_backtrace = true;
174 self
175 }
176
177 pub fn with_unsafe_panic(&mut self) -> &mut Self {
178 self.unsafe_panic = true;
179 self
180 }
181
182 pub fn build(&mut self) -> Result<RootDatabase> {
183 let mut db = RootDatabase::new(self.default_plugin_suite.clone(), self.inlining_strategy);
188
189 if let Some(cfg_set) = &self.cfg_set {
190 db.use_cfg(cfg_set);
191 }
192
193 if self.detect_corelib {
194 let path =
195 detect_corelib().ok_or_else(|| anyhow!("Failed to find development corelib."))?;
196 init_dev_corelib(&mut db, path)
197 }
198
199 let add_withdraw_gas_flag_id = FlagLongId("add_withdraw_gas".into());
200 db.set_flag(
201 add_withdraw_gas_flag_id,
202 Some(Arc::new(Flag::AddWithdrawGas(self.auto_withdraw_gas))),
203 );
204 let panic_backtrace_flag_id = FlagLongId("panic_backtrace".into());
205 db.set_flag(
206 panic_backtrace_flag_id,
207 Some(Arc::new(Flag::PanicBacktrace(self.panic_backtrace))),
208 );
209
210 let unsafe_panic_flag_id = FlagLongId("unsafe_panic".into());
211 db.set_flag(unsafe_panic_flag_id, Some(Arc::new(Flag::UnsafePanic(self.unsafe_panic))));
212
213 if let Some(config) = &self.project_config {
214 update_crate_roots_from_project_config(&mut db, config.as_ref());
215 }
216 validate_corelib(&db)?;
217
218 Ok(db)
219 }
220}
221
222pub fn validate_corelib(db: &(dyn salsa::Database + 'static)) -> Result<()> {
224 let Some(config) = db.crate_config(CrateId::core(db)) else {
225 return Ok(());
226 };
227 let Some(found) = &config.settings.version else {
228 return Ok(());
229 };
230 let Ok(expected) = semver::Version::parse(CORELIB_VERSION) else {
231 return Ok(());
232 };
233 if found == &expected {
234 return Ok(());
235 }
236 let path_part = match &config.root {
237 cairo_lang_filesystem::ids::Directory::Real(path) => {
238 format!(" for `{}`", path.to_string_lossy())
239 }
240 cairo_lang_filesystem::ids::Directory::Virtual { .. } => "".to_string(),
241 };
242 bail!("Corelib version mismatch: expected `{expected}`, found `{found}`{path_part}.");
243}