diff --git a/Cargo.lock b/Cargo.lock index 5601dbae53d..21ed3beb2b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2684,6 +2684,7 @@ dependencies = [ "noirc_printable_type", "regex", "rustc-hash", + "serde", "serde_json", "small-ord-set", "smol_str", diff --git a/Cargo.toml b/Cargo.toml index b0bb24ddb03..db860aa9b44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ dirs = "4" lsp-types = "0.94" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0" -smol_str = "0.1.17" +smol_str = { version = "0.1.17", features = ["serde"] } thiserror = "1.0.21" toml = "0.7.2" tower = "0.4" diff --git a/compiler/noirc_frontend/Cargo.toml b/compiler/noirc_frontend/Cargo.toml index 1cb6e0c5c51..d30394159c6 100644 --- a/compiler/noirc_frontend/Cargo.toml +++ b/compiler/noirc_frontend/Cargo.toml @@ -18,6 +18,7 @@ chumsky.workspace = true thiserror.workspace = true smol_str.workspace = true serde_json.workspace = true +serde.workspace = true rustc-hash = "1.1.0" small-ord-set = "0.1.3" regex = "1.9.1" diff --git a/compiler/noirc_frontend/src/graph/mod.rs b/compiler/noirc_frontend/src/graph/mod.rs index 3a40c87d8e7..11024163199 100644 --- a/compiler/noirc_frontend/src/graph/mod.rs +++ b/compiler/noirc_frontend/src/graph/mod.rs @@ -8,6 +8,7 @@ use std::{fmt::Display, str::FromStr}; use fm::FileId; use rustc_hash::{FxHashMap, FxHashSet}; +use serde::Deserialize; use smol_str::SmolStr; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] @@ -32,7 +33,7 @@ impl CrateId { } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Deserialize)] pub struct CrateName(SmolStr); impl CrateName { diff --git a/compiler/wasm/src/compile.rs b/compiler/wasm/src/compile.rs index 5adaca9d356..b95063b6156 100644 --- a/compiler/wasm/src/compile.rs +++ b/compiler/wasm/src/compile.rs @@ -1,6 +1,6 @@ use fm::FileManager; use gloo_utils::format::JsValueSerdeExt; -use js_sys::Array; +use js_sys::Object; use nargo::artifacts::{ contract::{PreprocessedContract, PreprocessedContractFunction}, program::PreprocessedProgram, @@ -10,7 +10,7 @@ use noirc_driver::{ CompiledContract, CompiledProgram, }; use noirc_frontend::{ - graph::{CrateGraph, CrateId}, + graph::{CrateGraph, CrateId, CrateName}, hir::Context, }; use serde::Deserialize; @@ -21,32 +21,41 @@ use crate::errors::{CompileError, JsCompileError}; const BACKEND_IDENTIFIER: &str = "acvm-backend-barretenberg"; +#[wasm_bindgen(typescript_custom_section)] +const DEPENDENCY_GRAPH: &'static str = r#" +export type DependencyGraph = { + root_dependencies: readonly string[]; + library_dependencies: Readonly>; +} +"#; + #[wasm_bindgen] extern "C" { - #[wasm_bindgen(extends = Array, js_name = "StringArray", typescript_type = "string[]")] + #[wasm_bindgen(extends = Object, js_name = "DependencyGraph", typescript_type = "DependencyGraph")] #[derive(Clone, Debug, PartialEq, Eq)] - pub type StringArray; + pub type JsDependencyGraph; } #[derive(Deserialize)] struct DependencyGraph { - root_dependencies: Vec, - library_dependencies: HashMap>, + root_dependencies: Vec, + library_dependencies: HashMap>, } #[wasm_bindgen] pub fn compile( entry_point: String, contracts: Option, - dependency_graph: JsValue, + js_dependency_graph: Option, ) -> Result { console_error_panic_hook::set_once(); - let dependency_graph: DependencyGraph = - ::into_serde(&dependency_graph).unwrap_or(DependencyGraph { - root_dependencies: vec![], - library_dependencies: HashMap::new(), - }); + let dependency_graph: DependencyGraph = if let Some(js_dependency_graph) = js_dependency_graph { + ::into_serde(&JsValue::from(js_dependency_graph)) + .map_err(|err| err.to_string())? + } else { + DependencyGraph { root_dependencies: vec![], library_dependencies: HashMap::new() } + }; let root = Path::new("/"); let fm = FileManager::new(root, Box::new(get_non_stdlib_asset)); @@ -105,40 +114,36 @@ pub fn compile( } fn process_dependency_graph(context: &mut Context, dependency_graph: DependencyGraph) { - let mut crate_names: HashMap = HashMap::new(); + let mut crate_names: HashMap<&CrateName, CrateId> = HashMap::new(); - // register libraries for lib in &dependency_graph.root_dependencies { - let crate_id = add_noir_lib(context, &lib); - crate_names.insert(lib.to_string(), crate_id); - } + let crate_id = add_noir_lib(context, lib); + crate_names.insert(lib, crate_id); - // make root crate as depending on it - for lib in &dependency_graph.root_dependencies { - let crate_id = crate_names.get(lib.as_str()).unwrap().clone(); - let root_crate_id = context.root_crate_id().clone(); - add_dep(context, root_crate_id, crate_id, lib.parse().unwrap()); + add_dep(context, *context.root_crate_id(), crate_id, lib.clone()); } - for (lib_name, dependencies) in dependency_graph.library_dependencies { - let crate_id = crate_names.get(lib_name.as_str()).unwrap().clone(); + for (lib_name, dependencies) in &dependency_graph.library_dependencies { + // first create the library crate if needed + // this crate might not have been registered yet because of the order of the HashMap + // e.g. {root: [lib1], libs: { lib2 -> [lib3], lib1 -> [lib2] }} + let crate_id = crate_names + .entry(&lib_name) + .or_insert_with(|| add_noir_lib(context, &lib_name)) + .clone(); for dependency_name in dependencies { - let lib_crate_id = match crate_names.entry(dependency_name.clone()) { - std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), - std::collections::hash_map::Entry::Vacant(entry) => { - let crate_id = add_noir_lib(context, &dependency_name); - entry.insert(crate_id) - } - }; - - add_dep(context, crate_id, lib_crate_id.clone(), dependency_name.parse().unwrap()); + let dep_crate_id: &CrateId = crate_names + .entry(&dependency_name) + .or_insert_with(|| add_noir_lib(context, &dependency_name)); + + add_dep(context, crate_id, *dep_crate_id, dependency_name.clone()); } } } -fn add_noir_lib(context: &mut Context, library_name: &str) -> CrateId { - let path_to_lib = Path::new(&library_name).join("lib.nr"); +fn add_noir_lib(context: &mut Context, library_name: &CrateName) -> CrateId { + let path_to_lib = Path::new(&library_name.to_string()).join("lib.nr"); let library_crate_id = prepare_dependency(context, &path_to_lib); library_crate_id