feat(napi-derive): catch_unwind attribute (#1280)
This commit is contained in:
parent
bc69e15efb
commit
b7a3103f0c
14 changed files with 64 additions and 8 deletions
2
.github/workflows/zig.yaml
vendored
2
.github/workflows/zig.yaml
vendored
|
@ -129,6 +129,8 @@ jobs:
|
||||||
ls ./examples/napi-compat-mode
|
ls ./examples/napi-compat-mode
|
||||||
- name: Test
|
- name: Test
|
||||||
run: yarn test --verbose
|
run: yarn test --verbose
|
||||||
|
env:
|
||||||
|
SKIP_UNWIND_TEST: 1
|
||||||
if: matrix.settings.host == 'macos-latest'
|
if: matrix.settings.host == 'macos-latest'
|
||||||
- name: Test
|
- name: Test
|
||||||
uses: docker://multiarch/alpine:aarch64-latest-stable
|
uses: docker://multiarch/alpine:aarch64-latest-stable
|
||||||
|
|
|
@ -32,7 +32,7 @@ const ZIG_PLATFORM_TARGET_MAP = {
|
||||||
// https://github.com/ziglang/zig/issues/1759
|
// https://github.com/ziglang/zig/issues/1759
|
||||||
// 'x86_64-unknown-freebsd': 'x86_64-freebsd',
|
// 'x86_64-unknown-freebsd': 'x86_64-freebsd',
|
||||||
'x86_64-apple-darwin': 'x86_64-macos-gnu',
|
'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-gnu': 'aarch64-linux-gnu',
|
||||||
'aarch64-unknown-linux-musl': 'aarch64-linux-musl',
|
'aarch64-unknown-linux-musl': 'aarch64-linux-musl',
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ function processZigLinkerArgs(platform: string, args: string[]) {
|
||||||
!(arg === '-framework' && args[index + 1] === 'CoreFoundation') &&
|
!(arg === '-framework' && args[index + 1] === 'CoreFoundation') &&
|
||||||
!(arg === 'CoreFoundation' && args[index - 1] === '-framework'),
|
!(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
|
return newArgs
|
||||||
}
|
}
|
||||||
if (platform.includes('linux')) {
|
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(
|
}', 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',
|
mode: '777',
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub struct NapiFn {
|
||||||
pub writable: bool,
|
pub writable: bool,
|
||||||
pub enumerable: bool,
|
pub enumerable: bool,
|
||||||
pub configurable: bool,
|
pub configurable: bool,
|
||||||
|
pub catch_unwind: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -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! {
|
(quote! {
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -42,6 +42,7 @@ pub struct BindgenAttrs {
|
||||||
macro_rules! attrgen {
|
macro_rules! attrgen {
|
||||||
($mac:ident) => {
|
($mac:ident) => {
|
||||||
$mac! {
|
$mac! {
|
||||||
|
(catch_unwind, CatchUnwind(Span)),
|
||||||
(js_name, JsName(Span, String, Span)),
|
(js_name, JsName(Span, String, Span)),
|
||||||
(constructor, Constructor(Span)),
|
(constructor, Constructor(Span)),
|
||||||
(factory, Factory(Span)),
|
(factory, Factory(Span)),
|
||||||
|
|
|
@ -695,6 +695,7 @@ fn napi_fn_from_decl(
|
||||||
writable: opts.writable(),
|
writable: opts.writable(),
|
||||||
enumerable: opts.enumerable(),
|
enumerable: opts.enumerable(),
|
||||||
configurable: opts.configurable(),
|
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."
|
"#[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() {
|
if opts.object().is_some() && opts.custom_finalize().is_some() {
|
||||||
bail_span!(self, "Custom finalize is not supported for #[napi(object)]");
|
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."
|
"#[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
|
// #[napi] macro will be remove from impl items after converted to ast
|
||||||
let napi = self.convert_to_ast(opts);
|
let napi = self.convert_to_ast(opts);
|
||||||
self.to_tokens(tokens);
|
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."
|
"#[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);
|
let napi = self.convert_to_ast(opts);
|
||||||
self.to_tokens(tokens);
|
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."
|
"#[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);
|
let napi = self.convert_to_ast(opts);
|
||||||
self.to_tokens(tokens);
|
self.to_tokens(tokens);
|
||||||
napi
|
napi
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl<T> PersistedSingleThreadVec<T> {
|
||||||
.inner
|
.inner
|
||||||
.lock()
|
.lock()
|
||||||
.expect("Acquire persisted thread vec lock failed");
|
.expect("Acquire persisted thread vec lock failed");
|
||||||
f(&mut *locked);
|
f(&mut locked);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push(&self, item: T) {
|
fn push(&self, item: T) {
|
||||||
|
|
|
@ -854,7 +854,7 @@ impl Env {
|
||||||
))?;
|
))?;
|
||||||
let type_id = unknown_tagged_object as *const TypeId;
|
let type_id = unknown_tagged_object as *const TypeId;
|
||||||
if *type_id == TypeId::of::<T>() {
|
if *type_id == TypeId::of::<T>() {
|
||||||
Box::from_raw(unknown_tagged_object as *mut TaggedObject<T>);
|
drop(Box::from_raw(unknown_tagged_object as *mut TaggedObject<T>));
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(
|
Err(Error::new(
|
||||||
|
|
|
@ -103,6 +103,7 @@ Generated by [AVA](https://avajs.dev).
|
||||||
}␊
|
}␊
|
||||||
export function enumToI32(e: CustomNumEnum): number␊
|
export function enumToI32(e: CustomNumEnum): number␊
|
||||||
export function throwError(): void␊
|
export function throwError(): void␊
|
||||||
|
export function panic(): void␊
|
||||||
export function createExternal(size: number): ExternalObject<number>␊
|
export function createExternal(size: number): ExternalObject<number>␊
|
||||||
export function createExternalString(content: string): ExternalObject<string>␊
|
export function createExternalString(content: string): ExternalObject<string>␊
|
||||||
export function getExternal(external: ExternalObject<number>): number␊
|
export function getExternal(external: ExternalObject<number>): number␊
|
||||||
|
|
Binary file not shown.
|
@ -31,6 +31,7 @@ import {
|
||||||
mapOption,
|
mapOption,
|
||||||
readFile,
|
readFile,
|
||||||
throwError,
|
throwError,
|
||||||
|
panic,
|
||||||
readPackageJson,
|
readPackageJson,
|
||||||
getPackageJsonName,
|
getPackageJsonName,
|
||||||
getBuffer,
|
getBuffer,
|
||||||
|
@ -163,7 +164,7 @@ test('enum', (t) => {
|
||||||
t.is(enumToI32(CustomNumEnum.Eight), 8)
|
t.is(enumToI32(CustomNumEnum.Eight), 8)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.only('class', (t) => {
|
test('class', (t) => {
|
||||||
const dog = new Animal(Kind.Dog, '旺财')
|
const dog = new Animal(Kind.Dog, '旺财')
|
||||||
|
|
||||||
t.is(dog.name, '旺财')
|
t.is(dog.name, '旺财')
|
||||||
|
@ -371,6 +372,9 @@ test('Option', (t) => {
|
||||||
|
|
||||||
test('Result', (t) => {
|
test('Result', (t) => {
|
||||||
t.throws(() => throwError(), void 0, 'Manual Error')
|
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) => {
|
test('function ts type override', (t) => {
|
||||||
|
|
1
examples/napi/index.d.ts
vendored
1
examples/napi/index.d.ts
vendored
|
@ -93,6 +93,7 @@ export const enum CustomNumEnum {
|
||||||
}
|
}
|
||||||
export function enumToI32(e: CustomNumEnum): number
|
export function enumToI32(e: CustomNumEnum): number
|
||||||
export function throwError(): void
|
export function throwError(): void
|
||||||
|
export function panic(): void
|
||||||
export function createExternal(size: number): ExternalObject<number>
|
export function createExternal(size: number): ExternalObject<number>
|
||||||
export function createExternalString(content: string): ExternalObject<string>
|
export function createExternalString(content: string): ExternalObject<string>
|
||||||
export function getExternal(external: ExternalObject<number>): number
|
export function getExternal(external: ExternalObject<number>): number
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
use napi::bindgen_prelude::*;
|
use napi::bindgen_prelude::*;
|
||||||
|
|
||||||
#[napi]
|
#[napi]
|
||||||
fn throw_error() -> Result<()> {
|
pub fn throw_error() -> Result<()> {
|
||||||
Err(Error::new(Status::InvalidArg, "Manual Error".to_owned()))
|
Err(Error::new(Status::InvalidArg, "Manual Error".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[napi(catch_unwind)]
|
||||||
|
pub fn panic() {
|
||||||
|
panic!("Don't panic");
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![allow(unreachable_code)]
|
#![allow(unreachable_code)]
|
||||||
#![allow(clippy::blacklisted_name)]
|
#![allow(clippy::disallowed_names)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate napi_derive;
|
extern crate napi_derive;
|
||||||
|
|
Loading…
Reference in a new issue