Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dojo-compiler): add sierra to cairo debug information #2392

Merged
merged 2 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 141 additions & 54 deletions crates/dojo-lang/src/compiler.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::{BTreeMap, HashMap};
use std::fs;
use std::io::Write;
use std::iter::zip;
use std::ops::DerefMut;
use std::rc::Rc;

use anyhow::{anyhow, Context, Result};
use cairo_lang_compiler::db::RootDatabase;
Expand All @@ -28,7 +28,7 @@
BASE_CONTRACT_TAG, BASE_DIR, BASE_QUALIFIED_PATH, CONTRACTS_DIR, MANIFESTS_DIR, MODELS_DIR,
WORLD_CONTRACT_TAG, WORLD_QUALIFIED_PATH,
};
use itertools::Itertools;
use itertools::{izip, Itertools};
use scarb::compiler::helpers::{build_compiler_config, collect_main_crate_ids};
use scarb::compiler::{CairoCompilationUnit, CompilationUnitAttributes, Compiler};
use scarb::core::{PackageName, TargetKind, Workspace};
Expand All @@ -41,6 +41,19 @@
use tracing::{debug, trace, trace_span};

use crate::plugin::{DojoAuxData, Model};
use crate::scarb_internal::debug::SierraToCairoDebugInfo;

#[derive(Debug, Clone)]
pub struct CompiledArtifact {
/// THe class hash of the Sierra contract.
class_hash: Felt,
/// The actual compiled Sierra contract class.
contract_class: Rc<ContractClass>,
debug_info: Option<Rc<SierraToCairoDebugInfo>>,
}

/// A type alias for a map of compiled artifacts by their path.
type CompiledArtifactByPath = HashMap<String, CompiledArtifact>;

const CAIRO_PATH_SEPARATOR: &str = "::";

Expand Down Expand Up @@ -88,6 +101,7 @@
TargetKind::new("dojo")
}

// TODO: refacto the main loop here, could be much more simpler and efficient.
fn compile(
&self,
unit: CairoCompilationUnit,
Expand Down Expand Up @@ -129,18 +143,32 @@
compile_prepared_db(db, &contracts, compiler_config)?
};

let mut compiled_classes: HashMap<String, (Felt, ContractClass)> = HashMap::new();
// TODO: get the debug flag from the `dojo_<profile>.toml` file.
let with_debug_info = true;
let debug_info_classes: Vec<Option<SierraToCairoDebugInfo>> = if with_debug_info {
let debug_classes =
crate::scarb_internal::debug::compile_prepared_db_to_debug_info(db, &contracts)?;

debug_classes
.into_iter()
.map(|d| Some(crate::scarb_internal::debug::get_sierra_to_cairo_debug_info(&d, db)))
.collect()
} else {
vec![None; contracts.len()]

Check warning on line 157 in crates/dojo-lang/src/compiler.rs

View check run for this annotation

Codecov / codecov/patch

crates/dojo-lang/src/compiler.rs#L157

Added line #L157 was not covered by tests
};

let mut compiled_classes: CompiledArtifactByPath = HashMap::new();
let list_selector = ListSelector::default();

for (decl, class) in zip(contracts, classes) {
for (decl, contract_class, debug_info) in izip!(contracts, classes, debug_info_classes) {
let contract_name = decl.submodule_id.name(db.upcast_mut());

// note that the qualified path is in snake case while
// the `full_path()` method of StructId uses the original struct name case.
// (see in `get_dojo_model_artifacts`)
let qualified_path = decl.module_id().full_path(db.upcast_mut());

match class.validate_version_compatible(list_selector.clone()) {
match contract_class.validate_version_compatible(list_selector.clone()) {
Ok(()) => {}
Err(AllowedLibfuncsError::UnsupportedLibfunc {
invalid_libfunc,
Expand All @@ -160,11 +188,22 @@
}
}

let class_hash = compute_class_hash_of_contract_class(&class).with_context(|| {
format!("problem computing class hash for contract `{}`", qualified_path.clone())
})?;

compiled_classes.insert(qualified_path, (class_hash, class));
let class_hash =
compute_class_hash_of_contract_class(&contract_class).with_context(|| {
format!(
"problem computing class hash for contract `{}`",
qualified_path.clone()
)

Check warning on line 196 in crates/dojo-lang/src/compiler.rs

View check run for this annotation

Codecov / codecov/patch

crates/dojo-lang/src/compiler.rs#L193-L196

Added lines #L193 - L196 were not covered by tests
})?;

compiled_classes.insert(
qualified_path,
CompiledArtifact {
class_hash,
contract_class: Rc::new(contract_class),
debug_info: debug_info.map(Rc::new),
},
);
}

update_files(
Expand Down Expand Up @@ -256,7 +295,7 @@
ws: &Workspace<'_>,
target_dir: &Filesystem,
crate_ids: &[CrateId],
compiled_artifacts: HashMap<String, (Felt, ContractClass)>,
compiled_artifacts: CompiledArtifactByPath,
external_contracts: Option<Vec<ContractSelector>>,
) -> anyhow::Result<()> {
let profile_name =
Expand All @@ -270,9 +309,9 @@
let manifest_dir = ws.manifest_path().parent().unwrap().to_path_buf();

fn get_compiled_artifact_from_map<'a>(
artifacts: &'a HashMap<String, (Felt, ContractClass)>,
artifacts: &'a CompiledArtifactByPath,
qualified_artifact_path: &str,
) -> anyhow::Result<&'a (Felt, ContractClass)> {
) -> anyhow::Result<&'a CompiledArtifact> {
artifacts.get(qualified_artifact_path).context(format!(
"Contract `{qualified_artifact_path}` not found. Did you include `dojo` as a \
dependency?",
Expand All @@ -285,7 +324,7 @@
for (qualified_path, tag) in
[(WORLD_QUALIFIED_PATH, WORLD_CONTRACT_TAG), (BASE_QUALIFIED_PATH, BASE_CONTRACT_TAG)]
{
let (hash, class) = get_compiled_artifact_from_map(&compiled_artifacts, qualified_path)?;
let artifact = get_compiled_artifact_from_map(&compiled_artifacts, qualified_path)?;
let filename = naming::get_filename_from_tag(tag);
write_manifest_and_abi(
&base_manifests_dir,
Expand All @@ -294,16 +333,20 @@
&mut Manifest::new(
// abi path will be written by `write_manifest`
Class {
class_hash: *hash,
class_hash: artifact.class_hash,
abi: None,
original_class_hash: *hash,
original_class_hash: artifact.class_hash,
tag: tag.to_string(),
},
filename.clone(),
),
&class.abi,
&artifact.contract_class.abi,
)?;
save_json_artifact_file(ws, target_dir, class, &filename, tag)?;
save_json_artifact_file(ws, target_dir, &artifact.contract_class, &filename, tag)?;

if let Some(debug_info) = &artifact.debug_info {
save_json_artifact_debug_file(ws, target_dir, debug_info, &filename, tag)?;
}

Check warning on line 349 in crates/dojo-lang/src/compiler.rs

View check run for this annotation

Codecov / codecov/patch

crates/dojo-lang/src/compiler.rs#L349

Added line #L349 was not covered by tests
}

let mut models = BTreeMap::new();
Expand Down Expand Up @@ -380,13 +423,13 @@
std::fs::create_dir_all(&base_contracts_abis_dir)?;
}

for (_, (manifest, class, module_id)) in contracts.iter_mut() {
for (_, (manifest, module_id, artifact)) in contracts.iter_mut() {
write_manifest_and_abi(
&base_contracts_dir,
&base_contracts_abis_dir,
&manifest_dir,
manifest,
&class.abi,
&artifact.contract_class.abi,
)?;

let filename = naming::get_filename_from_tag(&manifest.inner.tag);
Expand All @@ -398,7 +441,23 @@
&filename,
&manifest.inner.tag,
)?;
save_json_artifact_file(ws, &contracts_dir, class, &filename, &manifest.inner.tag)?;
save_json_artifact_file(
ws,
&contracts_dir,
&artifact.contract_class,
&filename,
&manifest.inner.tag,
)?;

if let Some(debug_info) = &artifact.debug_info {
save_json_artifact_debug_file(
ws,
&contracts_dir,
debug_info,
&filename,
&manifest.inner.tag,
)?;
}

Check warning on line 460 in crates/dojo-lang/src/compiler.rs

View check run for this annotation

Codecov / codecov/patch

crates/dojo-lang/src/compiler.rs#L460

Added line #L460 was not covered by tests
}

let models_dir = target_dir.child(MODELS_DIR);
Expand All @@ -417,18 +476,34 @@
std::fs::create_dir_all(&base_models_abis_dir)?;
}

for (_, (manifest, class, module_id)) in models.iter_mut() {
for (_, (manifest, module_id, artifact)) in models.iter_mut() {
write_manifest_and_abi(
&base_models_dir,
&base_models_abis_dir,
&manifest_dir,
manifest,
&class.abi,
&artifact.contract_class.abi,
)?;

let filename = naming::get_filename_from_tag(&manifest.inner.tag);
save_expanded_source_file(ws, *module_id, db, &models_dir, &filename, &manifest.inner.tag)?;
save_json_artifact_file(ws, &models_dir, class, &filename, &manifest.inner.tag)?;
save_json_artifact_file(
ws,
&models_dir,
&artifact.contract_class,
&filename,
&manifest.inner.tag,
)?;

if let Some(debug_info) = &artifact.debug_info {
save_json_artifact_debug_file(
ws,
&models_dir,
debug_info,
&filename,
&manifest.inner.tag,
)?;
}

Check warning on line 506 in crates/dojo-lang/src/compiler.rs

View check run for this annotation

Codecov / codecov/patch

crates/dojo-lang/src/compiler.rs#L506

Added line #L506 was not covered by tests
}

Ok(())
Expand All @@ -441,8 +516,8 @@
db: &RootDatabase,
aux_data: &Vec<Model>,
module_id: ModuleId,
compiled_classes: &HashMap<String, (Felt, ContractClass)>,
) -> anyhow::Result<HashMap<String, (Manifest<DojoModel>, ContractClass, ModuleId)>> {
compiled_classes: &CompiledArtifactByPath,
) -> anyhow::Result<HashMap<String, (Manifest<DojoModel>, ModuleId, CompiledArtifact)>> {
let mut models = HashMap::with_capacity(aux_data.len());

for model in aux_data {
Expand All @@ -456,25 +531,18 @@
let compiled_class = compiled_classes.get(&qualified_path).cloned();
let tag = naming::get_tag(&model.namespace, &model.name);

if let Some((class_hash, class)) = compiled_class {
models.insert(
qualified_path.clone(),
(
Manifest::new(
DojoModel {
tag: tag.clone(),
class_hash,
abi: None,
members: model.members.clone(),
original_class_hash: class_hash,
qualified_path,
},
naming::get_filename_from_tag(&tag),
),
class,
module_id,
),
);
if let Some(artifact) = compiled_class {
let dojo_model = DojoModel {
abi: None,
tag: tag.clone(),
members: model.members.clone(),
class_hash: artifact.class_hash,
qualified_path: qualified_path.clone(),
original_class_hash: artifact.class_hash,
};

let manifest = Manifest::new(dojo_model, naming::get_filename_from_tag(&tag));
models.insert(qualified_path, (manifest, module_id, artifact.clone()));
} else {
println!("Model {} not found in target.", tag.clone());
}
Expand All @@ -489,9 +557,9 @@
db: &RootDatabase,
module_id: &ModuleId,
tag: &str,
compiled_classes: &HashMap<String, (Felt, ContractClass)>,
compiled_classes: &CompiledArtifactByPath,
systems: &[String],
) -> anyhow::Result<HashMap<String, (Manifest<DojoContract>, ContractClass, ModuleId)>> {
) -> Result<HashMap<String, (Manifest<DojoContract>, ModuleId, CompiledArtifact)>> {
let mut result = HashMap::new();

if !matches!(naming::get_name_from_tag(tag).as_str(), "world" | "resource_metadata" | "base") {
Expand All @@ -501,24 +569,24 @@
let contract_qualified_path =
format!("{}{}{}", module_id.full_path(db), CAIRO_PATH_SEPARATOR, contract_name);

if let Some((class_hash, class)) =
compiled_classes.get(&contract_qualified_path.to_string())
{
if let Some(artifact) = compiled_classes.get(&contract_qualified_path.to_string()) {
let manifest = Manifest::new(
DojoContract {
tag: tag.to_string(),
writes: vec![],
reads: vec![],
class_hash: *class_hash,
original_class_hash: *class_hash,
class_hash: artifact.class_hash,
original_class_hash: artifact.class_hash,
systems: systems.to_vec(),
..Default::default()
},
naming::get_filename_from_tag(tag),
);

result
.insert(contract_qualified_path.to_string(), (manifest, class.clone(), *module_id));
result.insert(
contract_qualified_path.to_string(),
(manifest, *module_id, artifact.clone()),
);
}
}

Expand Down Expand Up @@ -631,3 +699,22 @@
.with_context(|| format!("failed to serialize contract artifact: {contract_tag}"))?;
Ok(())
}

fn save_json_artifact_debug_file(
ws: &Workspace<'_>,
contract_dir: &Filesystem,
debug_info: &SierraToCairoDebugInfo,
contract_basename: &str,
contract_tag: &str,
) -> anyhow::Result<()> {
let mut file = contract_dir.open_rw(
format!("{contract_basename}.debug.json"),
"class file",
ws.config(),
)?;

serde_json::to_writer_pretty(file.deref_mut(), debug_info)
.with_context(|| format!("failed to serialize contract debug artifact: {contract_tag}"))?;

Ok(())
}
Loading
Loading