diff --git a/Cargo.lock b/Cargo.lock index 3bebcb08c8..3dea0759c6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2174,6 +2174,7 @@ dependencies = [ "console_error_panic_hook", "gloo-utils", "noirc_driver", + "noirc_frontend", "serde", "wasm-bindgen", "wasm-bindgen-test", @@ -2213,6 +2214,7 @@ dependencies = [ "codespan", "codespan-reporting 0.9.5", "fm", + "serde", ] [[package]] diff --git a/crates/noirc_driver/src/lib.rs b/crates/noirc_driver/src/lib.rs index 990da29b1a..e49e769e3a 100644 --- a/crates/noirc_driver/src/lib.rs +++ b/crates/noirc_driver/src/lib.rs @@ -13,6 +13,7 @@ use noirc_frontend::hir::def_map::{Contract, CrateDefMap}; use noirc_frontend::hir::Context; use noirc_frontend::monomorphization::monomorphize; use noirc_frontend::node_interner::FuncId; +use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; mod program; @@ -23,7 +24,7 @@ pub struct Driver { language: Language, } -#[derive(Args, Clone, Debug)] +#[derive(Args, Clone, Debug, Serialize, Deserialize)] pub struct CompileOptions { /// Emit debug information for the intermediate SSA IR #[arg(short, long)] diff --git a/crates/noirc_errors/Cargo.toml b/crates/noirc_errors/Cargo.toml index ed4c18fd0a..3079f2c583 100644 --- a/crates/noirc_errors/Cargo.toml +++ b/crates/noirc_errors/Cargo.toml @@ -11,3 +11,4 @@ codespan-reporting.workspace = true codespan.workspace = true fm.workspace = true chumsky.workspace = true +serde.workspace = true diff --git a/crates/noirc_errors/src/lib.rs b/crates/noirc_errors/src/lib.rs index 0e4fc51e2c..9bb1ebaef8 100644 --- a/crates/noirc_errors/src/lib.rs +++ b/crates/noirc_errors/src/lib.rs @@ -6,9 +6,10 @@ mod position; pub mod reporter; pub use position::{Location, Position, Span, Spanned}; pub use reporter::{CustomDiagnostic, DiagnosticKind}; +use serde::{Deserialize, Serialize}; /// Returned when the Reporter finishes after reporting errors -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Serialize, Deserialize)] pub struct ReportedError; #[derive(Debug, PartialEq, Eq)] diff --git a/crates/wasm/Cargo.toml b/crates/wasm/Cargo.toml index 16094b0b98..32bd9537d6 100644 --- a/crates/wasm/Cargo.toml +++ b/crates/wasm/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["cdylib"] acvm.workspace = true noirc_driver.workspace = true +noirc_frontend.workspace = true wasm-bindgen.workspace = true serde.workspace = true diff --git a/crates/wasm/src/lib.rs b/crates/wasm/src/lib.rs index b4c8e9b388..f34103cc3d 100644 --- a/crates/wasm/src/lib.rs +++ b/crates/wasm/src/lib.rs @@ -4,6 +4,8 @@ use acvm::acir::circuit::Circuit; use gloo_utils::format::JsValueSerdeExt; +use noirc_driver::{CompileOptions, Driver}; +use noirc_frontend::graph::{CrateName, CrateType}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use wasm_bindgen::prelude::*; @@ -15,24 +17,73 @@ pub struct BuildInfo { dirty: &'static str, } +#[derive(Serialize, Deserialize)] +pub struct WASMCompileOptions { + // Compile each contract function used within the program + #[serde(default = "bool::default")] + contracts: bool, + + #[serde(default)] + compile_options: CompileOptions, + + #[serde(default)] + optional_dependencies_set: Vec, +} + const BUILD_INFO: BuildInfo = BuildInfo { git_hash: env!("GIT_COMMIT"), version: env!("CARGO_PKG_VERSION"), dirty: env!("GIT_DIRTY"), }; -// Returns a compiled program which is the ACIR circuit along with the ABI +pub fn add_noir_lib(driver: &mut Driver, crate_name: String) { + let path_to_lib = PathBuf::from(&crate_name).join("lib.nr"); + let library_crate = driver.create_non_local_crate(path_to_lib, CrateType::Library); + + driver.propagate_dep(library_crate, &CrateName::new(crate_name.as_str()).unwrap()); +} + #[wasm_bindgen] -pub fn compile(src: String) -> JsValue { +pub fn compile(args: JsValue) -> JsValue { console_error_panic_hook::set_once(); + let options: WASMCompileOptions = JsValueSerdeExt::into_serde(&args).unwrap(); // For now we default to plonk width = 3, though we can add it as a parameter let language = acvm::Language::PLONKCSat { width: 3 }; - let path = PathBuf::from(src); - let compiled_program = match noirc_driver::Driver::compile_file(path, language) { - Ok(compiled_program) => compiled_program, - Err(_) => panic!("Compilation Error: Failed to compile circuit"), - }; - ::from_serde(&compiled_program).unwrap() + let path = PathBuf::from("main.nr"); + let mut driver = noirc_driver::Driver::new(&language); + + driver.create_local_crate(path, CrateType::Binary); + + for dependency in options.optional_dependencies_set { + add_noir_lib(&mut driver, dependency); + } + + driver.check_crate(&options.compile_options).unwrap_or_else(|_| panic!("Crate check failed")); + + if options.contracts { + let mut collected_compiled_programs = vec![]; + + for contract in driver.get_all_contracts() { + contract.functions.into_iter().for_each(|function| { + let name = driver.function_name(function); + let key = format!("{}-{name}", &contract.name); + let compiled_program = driver + .compile_no_check(&options.compile_options, function) + .unwrap_or_else(|_| panic!("Compilation of `{key}` failed")); + collected_compiled_programs.push((key, compiled_program)); + }); + } + + ::from_serde(&collected_compiled_programs).unwrap() + } else { + let main = + driver.main_function().unwrap_or_else(|_| panic!("Could not find main function!")); + let compiled_program = driver + .compile_no_check(&options.compile_options, main) + .unwrap_or_else(|_| panic!("Compilation failed")); + + ::from_serde(&compiled_program).unwrap() + } } // Deserializes bytes into ACIR structure