diff --git a/.github/workflows/zig.yaml b/.github/workflows/zig.yaml index 4cd6e607..85e74a6e 100644 --- a/.github/workflows/zig.yaml +++ b/.github/workflows/zig.yaml @@ -129,6 +129,8 @@ jobs: ls ./examples/napi-compat-mode - name: Test run: yarn test --verbose + env: + SKIP_UNWIND_TEST: 1 if: matrix.settings.host == 'macos-latest' - name: Test uses: docker://multiarch/alpine:aarch64-latest-stable diff --git a/cli/src/build.ts b/cli/src/build.ts index 04adabfe..282c4d2e 100644 --- a/cli/src/build.ts +++ b/cli/src/build.ts @@ -32,7 +32,7 @@ const ZIG_PLATFORM_TARGET_MAP = { // https://github.com/ziglang/zig/issues/1759 // 'x86_64-unknown-freebsd': 'x86_64-freebsd', 'x86_64-apple-darwin': 'x86_64-macos-gnu', - 'aarch64-apple-darwin': 'aarch64-macos-gnu', + 'aarch64-apple-darwin': 'aarch64-macos', 'aarch64-unknown-linux-gnu': 'aarch64-linux-gnu', 'aarch64-unknown-linux-musl': 'aarch64-linux-musl', } @@ -48,7 +48,7 @@ function processZigLinkerArgs(platform: string, args: string[]) { !(arg === '-framework' && args[index + 1] === 'CoreFoundation') && !(arg === 'CoreFoundation' && args[index - 1] === '-framework'), ) - newArgs.push('-Wl,"-undefined=dynamic_lookup"', '-dead_strip') + newArgs.push('-Wl,"-undefined=dynamic_lookup"', '-dead_strip', '-lunwind') return newArgs } if (platform.includes('linux')) { @@ -334,7 +334,9 @@ export class BuildCommand extends Command { }', process.argv.slice(2)), '-target', '${zigTarget}'], { stdio: 'inherit', shell: true })\nwriteFileSync('${linkerWrapper.replaceAll( '\\', '/', - )}.args.log', process.argv.slice(2).join(' '))\n\nprocess.exit(status || 0)\n`, + )}.args.log', processZigLinkerArgs('${ + triple.raw + }', process.argv.slice(2)).join(' '))\n\nprocess.exit(status || 0)\n`, { mode: '777', }, diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 101dfc54..544c9588 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -26,6 +26,7 @@ pub struct NapiFn { pub writable: bool, pub enumerable: bool, pub configurable: bool, + pub catch_unwind: bool, } #[derive(Debug, Clone)] diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index 0c7e48a4..7fbb0344 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -69,6 +69,20 @@ impl TryToTokens for NapiFn { } }; + let function_call = if self.catch_unwind { + quote! { + { + std::panic::catch_unwind(|| { #function_call }) + .map_err(|e| napi::Error::new(napi::Status::GenericFailure, format!("{:?}", e))) + .and_then(|r| r) + } + } + } else { + quote! { + #function_call + } + }; + (quote! { #(#attrs)* #[doc(hidden)] diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index f7e2a7e0..c9c6fd04 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -42,6 +42,7 @@ pub struct BindgenAttrs { macro_rules! attrgen { ($mac:ident) => { $mac! { + (catch_unwind, CatchUnwind(Span)), (js_name, JsName(Span, String, Span)), (constructor, Constructor(Span)), (factory, Factory(Span)), diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index ec91abfd..a38db36a 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -695,6 +695,7 @@ fn napi_fn_from_decl( writable: opts.writable(), enumerable: opts.enumerable(), configurable: opts.configurable(), + catch_unwind: opts.catch_unwind().is_some(), } }) } @@ -747,6 +748,12 @@ impl ParseNapi for syn::ItemStruct { "#[napi(return_if_invalid)] can only be applied to a function or method." ); } + if opts.catch_unwind().is_some() { + bail_span!( + self, + "#[napi(catch_unwind)] can only be applied to a function or method." + ); + } if opts.object().is_some() && opts.custom_finalize().is_some() { bail_span!(self, "Custom finalize is not supported for #[napi(object)]"); } @@ -776,6 +783,12 @@ impl ParseNapi for syn::ItemImpl { "#[napi(return_if_invalid)] can only be applied to a function or method." ); } + if opts.catch_unwind().is_some() { + bail_span!( + self, + "#[napi(catch_unwind)] can only be applied to a function or method." + ); + } // #[napi] macro will be remove from impl items after converted to ast let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -802,6 +815,12 @@ impl ParseNapi for syn::ItemEnum { "#[napi(return_if_invalid)] can only be applied to a function or method." ); } + if opts.catch_unwind().is_some() { + bail_span!( + self, + "#[napi(catch_unwind)] can only be applied to a function or method." + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); @@ -826,6 +845,12 @@ impl ParseNapi for syn::ItemConst { "#[napi(return_if_invalid)] can only be applied to a function or method." ); } + if opts.catch_unwind().is_some() { + bail_span!( + self, + "#[napi(catch_unwind)] can only be applied to a function or method." + ); + } let napi = self.convert_to_ast(opts); self.to_tokens(tokens); napi diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index d3d609e0..27bca024 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -38,7 +38,7 @@ impl PersistedSingleThreadVec { .inner .lock() .expect("Acquire persisted thread vec lock failed"); - f(&mut *locked); + f(&mut locked); } fn push(&self, item: T) { diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index e9294e28..88b6a1d4 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -854,7 +854,7 @@ impl Env { ))?; let type_id = unknown_tagged_object as *const TypeId; if *type_id == TypeId::of::() { - Box::from_raw(unknown_tagged_object as *mut TaggedObject); + drop(Box::from_raw(unknown_tagged_object as *mut TaggedObject)); Ok(()) } else { Err(Error::new( diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 839b73db..a05091fd 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -103,6 +103,7 @@ Generated by [AVA](https://avajs.dev). }␊ export function enumToI32(e: CustomNumEnum): number␊ export function throwError(): void␊ + export function panic(): void␊ export function createExternal(size: number): ExternalObject␊ export function createExternalString(content: string): ExternalObject␊ export function getExternal(external: ExternalObject): number␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 4b49767a..69867ce3 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index 2b884642..39fcd131 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -31,6 +31,7 @@ import { mapOption, readFile, throwError, + panic, readPackageJson, getPackageJsonName, getBuffer, @@ -163,7 +164,7 @@ test('enum', (t) => { t.is(enumToI32(CustomNumEnum.Eight), 8) }) -test.only('class', (t) => { +test('class', (t) => { const dog = new Animal(Kind.Dog, '旺财') t.is(dog.name, '旺财') @@ -371,6 +372,9 @@ test('Option', (t) => { test('Result', (t) => { t.throws(() => throwError(), void 0, 'Manual Error') + if (!process.env.SKIP_UNWIND_TEST) { + t.throws(() => panic(), void 0, `Don't panic`) + } }) test('function ts type override', (t) => { diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 970ce77a..30690e82 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -93,6 +93,7 @@ export const enum CustomNumEnum { } export function enumToI32(e: CustomNumEnum): number export function throwError(): void +export function panic(): void export function createExternal(size: number): ExternalObject export function createExternalString(content: string): ExternalObject export function getExternal(external: ExternalObject): number diff --git a/examples/napi/src/error.rs b/examples/napi/src/error.rs index e1533117..42aa4ef1 100644 --- a/examples/napi/src/error.rs +++ b/examples/napi/src/error.rs @@ -1,6 +1,11 @@ use napi::bindgen_prelude::*; #[napi] -fn throw_error() -> Result<()> { +pub fn throw_error() -> Result<()> { Err(Error::new(Status::InvalidArg, "Manual Error".to_owned())) } + +#[napi(catch_unwind)] +pub fn panic() { + panic!("Don't panic"); +} diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 1825406c..48f23ca5 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -1,6 +1,6 @@ #![allow(dead_code)] #![allow(unreachable_code)] -#![allow(clippy::blacklisted_name)] +#![allow(clippy::disallowed_names)] #[macro_use] extern crate napi_derive;