From 9d377fb9156bf9f583915c27e96e90c9a1750c70 Mon Sep 17 00:00:00 2001 From: Kota Kawabuchi <52311998+ppputtyo@users.noreply.github.com> Date: Fri, 27 Oct 2023 19:11:06 +0900 Subject: [PATCH] Display error messages in the wasm demo (#31) --- .github/workflows/CI.yml | 2 +- Cargo.lock | 1 + build.sh | 2 +- crates/uroborosql-fmt-wasm/Cargo.toml | 1 + crates/uroborosql-fmt-wasm/src/lib.rs | 58 +++++++++++++++++---------- wasm/index.html | 2 + wasm/ja.html | 2 + wasm/main.js | 15 ++++--- 8 files changed, 54 insertions(+), 29 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f4cf512..5b2ffa2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -167,7 +167,7 @@ jobs: -o ./wasm/uroborosql-fmt.js -s ALLOW_MEMORY_GROWTH=1 -s STACK_SIZE=5MB - -s EXPORTED_FUNCTIONS=['_format_sql','_free_format_string'] + -s EXPORTED_FUNCTIONS=['_format_sql','_get_result_address','_get_error_msg_address'] -s EXPORTED_RUNTIME_METHODS=ccall - name: Upload artifact diff --git a/Cargo.lock b/Cargo.lock index eb85b64..86c8208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,6 +378,7 @@ dependencies = [ name = "uroborosql-fmt-wasm" version = "0.1.0" dependencies = [ + "once_cell", "uroborosql-fmt", ] diff --git a/build.sh b/build.sh index 6cf976c..bca3d1e 100644 --- a/build.sh +++ b/build.sh @@ -11,7 +11,7 @@ export EMCC_CFLAGS="-O3 -o ./wasm/uroborosql-fmt.js -s ALLOW_MEMORY_GROWTH=1 -s STACK_SIZE=5MB - -s EXPORTED_FUNCTIONS=['_format_sql','_free_format_string'] + -s EXPORTED_FUNCTIONS=['_format_sql','_get_result_address','_get_error_msg_address'] -s EXPORTED_RUNTIME_METHODS=ccall" # 全体のビルドを実行 cargo build --package uroborosql-fmt-wasm --target wasm32-unknown-emscripten --release diff --git a/crates/uroborosql-fmt-wasm/Cargo.toml b/crates/uroborosql-fmt-wasm/Cargo.toml index 23df84b..0a19120 100644 --- a/crates/uroborosql-fmt-wasm/Cargo.toml +++ b/crates/uroborosql-fmt-wasm/Cargo.toml @@ -13,4 +13,5 @@ repository.workspace = true crate-type = ["cdylib"] [dependencies] +once_cell = "1.18.0" uroborosql-fmt = { workspace = true } diff --git a/crates/uroborosql-fmt-wasm/src/lib.rs b/crates/uroborosql-fmt-wasm/src/lib.rs index 4aa1851..5604571 100644 --- a/crates/uroborosql-fmt-wasm/src/lib.rs +++ b/crates/uroborosql-fmt-wasm/src/lib.rs @@ -1,7 +1,34 @@ -use std::ffi::{c_char, CStr, CString}; +use once_cell::sync::Lazy; +use std::{ + ffi::{c_char, CStr, CString}, + sync::Mutex, +}; + +static RESULT: Lazy> = Lazy::new(|| Mutex::new(CString::new("").unwrap())); +static ERROR_MSG: Lazy> = Lazy::new(|| Mutex::new(CString::new("").unwrap())); use uroborosql_fmt::{config::Config, format_sql_with_config}; +/// Returns the address of the result string. +/// +/// # Safety +/// +/// This is unsafe because it returns a raw pointer. +#[no_mangle] +pub unsafe extern "C" fn get_result_address() -> *const c_char { + RESULT.lock().unwrap().as_c_str().as_ptr() +} + +/// Returns the address of the error message string. +/// +/// # Safety +/// +/// This is unsafe because it returns a raw pointer. +#[no_mangle] +pub unsafe extern "C" fn get_error_msg_address() -> *const c_char { + ERROR_MSG.lock().unwrap().as_c_str().as_ptr() +} + /// Formats SQL code given as char pointer `src` by WASM (JavaScript). /// /// # Safety @@ -10,31 +37,20 @@ use uroborosql_fmt::{config::Config, format_sql_with_config}; /// [`CStr::from_ptr`](https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html#method.from_ptr). #[export_name = "format_sql"] #[no_mangle] -pub unsafe extern "C" fn format_sql_for_wasm( - src: *mut c_char, - config_json_str: *mut c_char, -) -> *mut c_char { +pub unsafe extern "C" fn format_sql_for_wasm(src: *const c_char, config_json_str: *const c_char) { + // Clear previous format result + *RESULT.lock().unwrap() = CString::new("").unwrap(); + *ERROR_MSG.lock().unwrap() = CString::new("").unwrap(); + let src = CStr::from_ptr(src).to_str().unwrap().to_owned(); let config_json_str = CStr::from_ptr(config_json_str).to_str().unwrap(); let config = Config::from_json_str(config_json_str).unwrap(); - // TODO: error handling - let result = format_sql_with_config(&src, config).unwrap(); - - CString::new(result).unwrap().into_raw() -} + let result = format_sql_with_config(&src, config); -/// Free the string `s` allocated by Rust. -/// -/// # Safety -/// -/// This is unsafe because it uses the unsafe function -/// [`CString::from_war()`](https://doc.rust-lang.org/std/ffi/struct.CString.html#method.from_raw). -#[no_mangle] -pub unsafe extern "C" fn free_format_string(s: *mut c_char) { - if s.is_null() { - return; + match result { + Ok(result) => *RESULT.lock().unwrap() = CString::new(result).unwrap(), + Err(err) => *ERROR_MSG.lock().unwrap() = CString::new(err.to_string()).unwrap(), } - let _ = CString::from_raw(s); } diff --git a/wasm/index.html b/wasm/index.html index 9a9d5c3..e94143f 100644 --- a/wasm/index.html +++ b/wasm/index.html @@ -201,6 +201,8 @@

Japanese

+
+ diff --git a/wasm/ja.html b/wasm/ja.html index c2e452d..fa2351f 100644 --- a/wasm/ja.html +++ b/wasm/ja.html @@ -197,6 +197,8 @@

English

+
+ diff --git a/wasm/main.js b/wasm/main.js index fa3a24c..1e2baf6 100644 --- a/wasm/main.js +++ b/wasm/main.js @@ -221,9 +221,9 @@ function initialize() { // タイマースタート const startTime = performance.now(); - const ptr = ccall( + ccall( "format_sql", - "number", + null, ["string", "string"], [target, config_str] ); @@ -233,13 +233,16 @@ function initialize() { // 何ミリ秒かかったかを表示する console.log("format complete: " + (endTime - startTime) + "ms"); - // Module.UTF8ToString() でポインタを js の string に変換 - const res = UTF8ToString(ptr); + // Rust側で確保したメモリのポインタを取得 + const result_ptr = ccall("get_result_address", "number", [], []); + const error_ptr = ccall("get_error_msg_address", "number", [], []); - // Rust 側で確保したフォーマット文字列の所有権を返す - ccall("free_format_string", null, ["number"], [ptr]); + // Module.UTF8ToString() でポインタを js の string に変換 + const res = UTF8ToString(result_ptr); + const err = UTF8ToString(error_ptr); dst_editor.setValue(res); + document.getElementById("error_msg").innerText = err; src_editor.updateOptions({ tabSize: tab_size }); dst_editor.updateOptions({ tabSize: tab_size });