From c5e6858f629761cd97c785c2be3c45f0b62f1a0f Mon Sep 17 00:00:00 2001 From: ppputtyo Date: Thu, 26 Oct 2023 13:49:43 +0900 Subject: [PATCH 1/3] show erro in wasm demo --- .github/workflows/CI.yml | 2 +- crates/uroborosql-fmt-wasm/src/lib.rs | 82 ++++++++++++++++++++------- wasm/index.html | 2 + wasm/ja.html | 2 + wasm/main.js | 15 +++-- 5 files changed, 77 insertions(+), 26 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/crates/uroborosql-fmt-wasm/src/lib.rs b/crates/uroborosql-fmt-wasm/src/lib.rs index 4aa1851..38f47fa 100644 --- a/crates/uroborosql-fmt-wasm/src/lib.rs +++ b/crates/uroborosql-fmt-wasm/src/lib.rs @@ -1,7 +1,30 @@ use std::ffi::{c_char, CStr, CString}; +static mut RESULT: &mut [u8] = &mut [0; 50000]; +static mut ERROR_MSG: &mut [u8] = &mut [0; 50000]; + 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 u8 { + &RESULT[0] +} + +/// 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 u8 { + &ERROR_MSG[0] +} + /// Formats SQL code given as char pointer `src` by WASM (JavaScript). /// /// # Safety @@ -10,31 +33,52 @@ 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: *mut c_char, config_json_str: *mut c_char) { 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(); + let result = format_sql_with_config(&src, config); - CString::new(result).unwrap().into_raw() -} + match result { + Ok(result) => { + CString::new(result) + .unwrap() + .as_bytes_with_nul() + .iter() + .enumerate() + .for_each(|(i, x)| { + RESULT[i] = *x; + }); -/// 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; + CString::new("") + .unwrap() + .as_bytes_with_nul() + .iter() + .enumerate() + .for_each(|(i, x)| { + ERROR_MSG[i] = *x; + }); + } + Err(err) => { + CString::new(err.to_string()) + .unwrap() + .as_bytes_with_nul() + .iter() + .enumerate() + .for_each(|(i, x)| { + ERROR_MSG[i] = *x; + }); + + CString::new("") + .unwrap() + .as_bytes_with_nul() + .iter() + .enumerate() + .for_each(|(i, x)| { + RESULT[i] = *x; + }); + } } - 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..db15f09 100644 --- a/wasm/main.js +++ b/wasm/main.js @@ -133,6 +133,10 @@ clear_all_button.addEventListener("click", () => clear_all_config()); // wasmの初期化が終了した際の処理 function initialize() { + // Rust側で確保したメモリのポインタを取得 + const result_ptr = ccall("get_result_address", "number", [], []); + const error_ptr = ccall("get_error_msg_address", "number", [], []); + function formatSql() { if (!src_editor || !dst_editor) { console.log("editors have not been loaded."); @@ -221,9 +225,9 @@ function initialize() { // タイマースタート const startTime = performance.now(); - const ptr = ccall( + ccall( "format_sql", - "number", + null, ["string", "string"], [target, config_str] ); @@ -234,12 +238,11 @@ function initialize() { console.log("format complete: " + (endTime - startTime) + "ms"); // Module.UTF8ToString() でポインタを js の string に変換 - const res = UTF8ToString(ptr); - - // Rust 側で確保したフォーマット文字列の所有権を返す - ccall("free_format_string", null, ["number"], [ptr]); + 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 }); From 81e44d55e85b0ae6d6e55cceefe0430b72784c85 Mon Sep 17 00:00:00 2001 From: ppputtyo Date: Fri, 27 Oct 2023 18:08:04 +0900 Subject: [PATCH 2/3] update build.sh --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From d163664e2e20a17160eb377a07a8844003ee211b Mon Sep 17 00:00:00 2001 From: ppputtyo Date: Fri, 27 Oct 2023 18:11:20 +0900 Subject: [PATCH 3/3] Changed to keep formatting results in CString --- Cargo.lock | 1 + crates/uroborosql-fmt-wasm/Cargo.toml | 1 + crates/uroborosql-fmt-wasm/src/lib.rs | 64 ++++++++------------------- wasm/main.js | 8 ++-- 4 files changed, 24 insertions(+), 50 deletions(-) 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/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 38f47fa..5604571 100644 --- a/crates/uroborosql-fmt-wasm/src/lib.rs +++ b/crates/uroborosql-fmt-wasm/src/lib.rs @@ -1,7 +1,11 @@ -use std::ffi::{c_char, CStr, CString}; +use once_cell::sync::Lazy; +use std::{ + ffi::{c_char, CStr, CString}, + sync::Mutex, +}; -static mut RESULT: &mut [u8] = &mut [0; 50000]; -static mut ERROR_MSG: &mut [u8] = &mut [0; 50000]; +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}; @@ -11,8 +15,8 @@ use uroborosql_fmt::{config::Config, format_sql_with_config}; /// /// This is unsafe because it returns a raw pointer. #[no_mangle] -pub unsafe extern "C" fn get_result_address() -> *const u8 { - &RESULT[0] +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. @@ -21,8 +25,8 @@ pub unsafe extern "C" fn get_result_address() -> *const u8 { /// /// This is unsafe because it returns a raw pointer. #[no_mangle] -pub unsafe extern "C" fn get_error_msg_address() -> *const u8 { - &ERROR_MSG[0] +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). @@ -33,7 +37,11 @@ pub unsafe extern "C" fn get_error_msg_address() -> *const u8 { /// [`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) { +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(); @@ -42,43 +50,7 @@ pub unsafe extern "C" fn format_sql_for_wasm(src: *mut c_char, config_json_str: let result = format_sql_with_config(&src, config); match result { - Ok(result) => { - CString::new(result) - .unwrap() - .as_bytes_with_nul() - .iter() - .enumerate() - .for_each(|(i, x)| { - RESULT[i] = *x; - }); - - CString::new("") - .unwrap() - .as_bytes_with_nul() - .iter() - .enumerate() - .for_each(|(i, x)| { - ERROR_MSG[i] = *x; - }); - } - Err(err) => { - CString::new(err.to_string()) - .unwrap() - .as_bytes_with_nul() - .iter() - .enumerate() - .for_each(|(i, x)| { - ERROR_MSG[i] = *x; - }); - - CString::new("") - .unwrap() - .as_bytes_with_nul() - .iter() - .enumerate() - .for_each(|(i, x)| { - RESULT[i] = *x; - }); - } + Ok(result) => *RESULT.lock().unwrap() = CString::new(result).unwrap(), + Err(err) => *ERROR_MSG.lock().unwrap() = CString::new(err.to_string()).unwrap(), } } diff --git a/wasm/main.js b/wasm/main.js index db15f09..1e2baf6 100644 --- a/wasm/main.js +++ b/wasm/main.js @@ -133,10 +133,6 @@ clear_all_button.addEventListener("click", () => clear_all_config()); // wasmの初期化が終了した際の処理 function initialize() { - // Rust側で確保したメモリのポインタを取得 - const result_ptr = ccall("get_result_address", "number", [], []); - const error_ptr = ccall("get_error_msg_address", "number", [], []); - function formatSql() { if (!src_editor || !dst_editor) { console.log("editors have not been loaded."); @@ -237,6 +233,10 @@ function initialize() { // 何ミリ秒かかったかを表示する console.log("format complete: " + (endTime - startTime) + "ms"); + // Rust側で確保したメモリのポインタを取得 + const result_ptr = ccall("get_result_address", "number", [], []); + const error_ptr = ccall("get_error_msg_address", "number", [], []); + // Module.UTF8ToString() でポインタを js の string に変換 const res = UTF8ToString(result_ptr); const err = UTF8ToString(error_ptr);