Skip to content

Commit

Permalink
feat: return compilation errors from noir_wasm (#3091)
Browse files Browse the repository at this point in the history
Co-authored-by: kevaundray <[email protected]>
  • Loading branch information
alexghr and kevaundray authored Oct 12, 2023
1 parent 88682da commit 55f63c9
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 21 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions compiler/wasm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fm.workspace = true
nargo.workspace = true
noirc_driver.workspace = true
noirc_frontend.workspace = true
noirc_errors.workspace = true
wasm-bindgen.workspace = true
serde.workspace = true
js-sys.workspace = true
Expand Down
8 changes: 6 additions & 2 deletions compiler/wasm/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ pub fn compile(

if contracts.unwrap_or_default() {
let compiled_contract = compile_contract(&mut context, crate_id, &compile_options)
.map_err(|_| JsCompileError::new("Failed to compile contract".to_string()))?
.map_err(|errs| {
JsCompileError::new("Failed to compile contract", errs, &context.file_manager)
})?
.0;

let optimized_contract =
Expand All @@ -68,7 +70,9 @@ pub fn compile(
Ok(<JsValue as JsValueSerdeExt>::from_serde(&preprocessed_contract).unwrap())
} else {
let compiled_program = compile_main(&mut context, crate_id, &compile_options, None, true)
.map_err(|_| JsCompileError::new("Failed to compile program".to_string()))?
.map_err(|errs| {
JsCompileError::new("Failed to compile program", errs, &context.file_manager)
})?
.0;

let optimized_program =
Expand Down
91 changes: 72 additions & 19 deletions compiler/wasm/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,82 @@
use js_sys::JsString;

use gloo_utils::format::JsValueSerdeExt;
use serde::{Deserialize, Serialize};
use wasm_bindgen::prelude::*;

use fm::FileManager;
use noirc_errors::FileDiagnostic;

#[wasm_bindgen(typescript_custom_section)]
const COMPILE_ERROR: &'static str = r#"
export type CompileError = Error;
const DIAGNOSTICS: &'static str = r#"
export type Diagnostic = {
message: string;
file_path: string;
secondaries: ReadonlyArray<{
message: string;
start: number;
end: number;
}>;
}
interface CompileError {
diagnostics: ReadonlyArray<Diagnostic>;
}
"#;

/// `CompileError` is a raw js error.
/// It'd be ideal that `CompileError` was a subclass of `Error`, but for that we'd need to use JS snippets or a js module.
/// Currently JS snippets don't work with a nodejs target. And a module would be too much for just a custom error type.
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(extends = js_sys::Error, js_name = "CompileError", typescript_type = "CompileError")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub type JsCompileError;

#[wasm_bindgen(constructor, js_class = "Error")]
fn constructor(message: JsString) -> JsCompileError;
#[derive(Serialize, Deserialize)]
struct JsDiagnosticLabel {
message: String,
start: u32,
end: u32,
}

#[derive(Serialize, Deserialize)]
struct JsDiagnostic {
message: String,
file_path: String,
secondaries: Vec<JsDiagnosticLabel>,
}

impl JsDiagnostic {
fn new(file_diagnostic: &FileDiagnostic, file_path: String) -> JsDiagnostic {
let diagnostic = &file_diagnostic.diagnostic;
let message = diagnostic.message.clone();

let secondaries = diagnostic
.secondaries
.iter()
.map(|label| JsDiagnosticLabel {
message: label.message.clone(),
start: label.span.start(),
end: label.span.end(),
})
.collect();

JsDiagnostic { message, file_path, secondaries }
}
}

#[wasm_bindgen(getter_with_clone, js_name = "CompileError")]
pub struct JsCompileError {
pub message: js_sys::JsString,
pub diagnostics: JsValue,
}

impl JsCompileError {
/// Creates a new execution error with the given call stack.
/// Call stacks won't be optional in the future, after removing ErrorLocation in ACVM.
pub fn new(message: String) -> Self {
JsCompileError::constructor(JsString::from(message))
pub fn new(
message: &str,
file_diagnostics: Vec<FileDiagnostic>,
file_manager: &FileManager,
) -> JsCompileError {
let diagnostics: Vec<_> = file_diagnostics
.iter()
.map(|err| {
JsDiagnostic::new(err, file_manager.path(err.file_id).to_str().unwrap().to_string())
})
.collect();

JsCompileError {
message: js_sys::JsString::from(message.to_string()),
diagnostics: <JsValue as JsValueSerdeExt>::from_serde(&diagnostics).unwrap(),
}
}
}

0 comments on commit 55f63c9

Please sign in to comment.