From 3418fd3e8f28de2e862161613d95913e50617991 Mon Sep 17 00:00:00 2001 From: Tom Sherman Date: Thu, 14 Sep 2023 00:45:14 +0200 Subject: [PATCH] feat(napi,sys): implement Symbol.for (#1721) Co-authored-by: LongYinan --- crates/napi/Cargo.toml | 3 +- .../src/bindgen_runtime/js_values/symbol.rs | 38 ++++++++++++++++-- crates/napi/src/env.rs | 12 ++++++ crates/sys/Cargo.toml | 1 + crates/sys/src/functions.rs | 20 +++++++++ examples/napi-compat-mode/Cargo.toml | 2 +- examples/napi/Cargo.toml | 2 +- .../__snapshots__/typegen.spec.ts.md | 2 + .../__snapshots__/typegen.spec.ts.snap | Bin 4066 -> 4077 bytes examples/napi/__tests__/values.spec.ts | 7 ++++ examples/napi/index.d.ts | 2 + examples/napi/src/symbol.rs | 5 +++ package.json | 2 +- 13 files changed, 89 insertions(+), 7 deletions(-) diff --git a/crates/napi/Cargo.toml b/crates/napi/Cargo.toml index 3528661a..9a5ad420 100644 --- a/crates/napi/Cargo.toml +++ b/crates/napi/Cargo.toml @@ -26,7 +26,7 @@ compat-mode = [] default = ["napi3", "compat-mode"] # for most Node.js users experimental = ["napi-sys/experimental"] chrono_date = ["chrono", "napi5"] -full = ["latin1", "napi8", "async", "serde-json", "experimental", "chrono_date"] +full = ["latin1", "napi9", "async", "serde-json", "experimental", "chrono_date"] latin1 = ["encoding_rs"] error_anyhow = ["anyhow"] napi1 = [] @@ -37,6 +37,7 @@ napi5 = ["napi4", "napi-sys/napi5"] napi6 = ["napi5", "napi-sys/napi6"] napi7 = ["napi6", "napi-sys/napi7"] napi8 = ["napi7", "napi-sys/napi8"] +napi9 = ["napi8", "napi-sys/napi9"] serde-json = ["serde", "serde_json"] tokio_fs = ["tokio/fs"] tokio_full = ["tokio/full"] diff --git a/crates/napi/src/bindgen_runtime/js_values/symbol.rs b/crates/napi/src/bindgen_runtime/js_values/symbol.rs index fef0edb5..39553b59 100644 --- a/crates/napi/src/bindgen_runtime/js_values/symbol.rs +++ b/crates/napi/src/bindgen_runtime/js_values/symbol.rs @@ -6,6 +6,8 @@ use super::{FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue}; pub struct Symbol { desc: Option, + #[cfg(feature = "napi9")] + for_desc: Option, } impl TypeName for Symbol { @@ -22,17 +24,43 @@ impl ValidateNapiValue for Symbol {} impl Symbol { pub fn new(desc: String) -> Self { - Self { desc: Some(desc) } + Self { + desc: Some(desc), + #[cfg(feature = "napi9")] + for_desc: None, + } } pub fn identity() -> Self { - Self { desc: None } + Self { + desc: None, + #[cfg(feature = "napi9")] + for_desc: None, + } + } + + #[cfg(feature = "napi9")] + pub fn for_desc(desc: String) -> Self { + Self { + desc: None, + for_desc: Some(desc.to_owned()), + } } } impl ToNapiValue for Symbol { unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> crate::Result { let mut symbol_value = ptr::null_mut(); + #[cfg(feature = "napi9")] + if let Some(desc) = val.for_desc { + check_status!( + unsafe { + sys::node_api_symbol_for(env, desc.as_ptr().cast(), desc.len(), &mut symbol_value) + }, + "Failed to call node_api_symbol_for" + )?; + return Ok(symbol_value); + } check_status!(unsafe { sys::napi_create_symbol( env, @@ -63,6 +91,10 @@ impl FromNapiValue for Symbol { _env: sys::napi_env, _napi_val: sys::napi_value, ) -> crate::Result { - Ok(Self { desc: None }) + Ok(Self { + desc: None, + #[cfg(feature = "napi9")] + for_desc: None, + }) } } diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index 37d92157..22c12abb 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -1250,6 +1250,18 @@ impl Env { }) } + #[cfg(feature = "napi9")] + pub fn symbol_for(&self, description: &str) -> Result { + let mut result = ptr::null_mut(); + let len = description.len(); + let description = CString::new(description)?; + check_status!(unsafe { + sys::node_api_symbol_for(self.0, description.as_ptr(), len, &mut result) + })?; + + Ok(unsafe { JsSymbol::from_raw_unchecked(self.0, result) }) + } + /// ### Serialize `Rust Struct` into `JavaScript Value` /// /// ``` diff --git a/crates/sys/Cargo.toml b/crates/sys/Cargo.toml index 21c2eb29..4a4fe9a5 100644 --- a/crates/sys/Cargo.toml +++ b/crates/sys/Cargo.toml @@ -21,6 +21,7 @@ napi5 = ["napi4"] napi6 = ["napi5"] napi7 = ["napi6"] napi8 = ["napi7"] +napi9 = ["napi8"] [package.metadata.workspaces] independent = true diff --git a/crates/sys/src/functions.rs b/crates/sys/src/functions.rs index 372f8705..28678466 100644 --- a/crates/sys/src/functions.rs +++ b/crates/sys/src/functions.rs @@ -698,6 +698,24 @@ mod napi8 { ); } +#[cfg(feature = "napi9")] +mod napi9 { + use std::os::raw::c_char; + + use super::super::types::*; + + generate!( + extern "C" { + fn node_api_symbol_for( + env: napi_env, + utf8name: *const c_char, + length: usize, + result: *mut napi_value, + ) -> napi_status; + } + ); +} + #[cfg(feature = "experimental")] mod experimental { use std::os::raw::c_char; @@ -739,6 +757,8 @@ pub use napi6::*; pub use napi7::*; #[cfg(feature = "napi8")] pub use napi8::*; +#[cfg(feature = "napi9")] +pub use napi9::*; #[cfg(windows)] pub(super) unsafe fn load() -> Result { diff --git a/examples/napi-compat-mode/Cargo.toml b/examples/napi-compat-mode/Cargo.toml index 8e483576..9111dadb 100644 --- a/examples/napi-compat-mode/Cargo.toml +++ b/examples/napi-compat-mode/Cargo.toml @@ -9,7 +9,7 @@ version = "0.1.0" crate-type = ["cdylib"] [features] -latest = ["napi/napi8"] +latest = ["napi/napi9"] napi3 = ["napi/napi3"] [dependencies] diff --git a/examples/napi/Cargo.toml b/examples/napi/Cargo.toml index cecb2959..1c10e596 100644 --- a/examples/napi/Cargo.toml +++ b/examples/napi/Cargo.toml @@ -16,7 +16,7 @@ chrono = "0.4" futures = "0.3" napi = { path = "../../crates/napi", default-features = false, features = [ "tokio_fs", - "napi8", + "napi9", "tokio_rt", "serde-json", "async", diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md index 921209c8..ad5833cf 100644 --- a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.md @@ -308,6 +308,8 @@ Generated by [AVA](https://avajs.dev). ␊ export function createSymbol(): symbol␊ ␊ + export function createSymbolFor(desc: string): symbol␊ + ␊ /** You could break the step and for an new continuous value. */␊ export const enum CustomNumEnum {␊ One = 1,␊ diff --git a/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap b/examples/napi/__tests__/__snapshots__/typegen.spec.ts.snap index ec00a21503d939194c1ca3734a89512b7527f7c4..213862635e39e293f65892867ef085c771625b0d 100644 GIT binary patch literal 4077 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 literal 4066 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 diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index b1765927..6a644e6b 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -62,6 +62,7 @@ import { getNull, setSymbolInObj, createSymbol, + createSymbolFor, threadsafeFunctionFatalMode, createExternal, getExternal, @@ -997,3 +998,9 @@ Napi5Test('Date from chrono::NativeDateTime test', (t) => { t.true(fixture instanceof Date) t.is(fixture?.toISOString(), '2016-12-23T15:25:59.325Z') }) + +const Napi9Test = Number(process.versions.napi) >= 9 ? test : test.skip + +Napi9Test('create symbol for', (t) => { + t.is(createSymbolFor('foo'), Symbol.for('foo')) +}) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index ce84fd02..c3a0e08d 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -298,6 +298,8 @@ export function createObjWithProperty(): { value: ArrayBuffer, get getter(): num export function createSymbol(): symbol +export function createSymbolFor(desc: string): symbol + /** You could break the step and for an new continuous value. */ export const enum CustomNumEnum { One = 1, diff --git a/examples/napi/src/symbol.rs b/examples/napi/src/symbol.rs index cc52be68..3a06425a 100644 --- a/examples/napi/src/symbol.rs +++ b/examples/napi/src/symbol.rs @@ -11,3 +11,8 @@ pub fn set_symbol_in_obj(env: Env, symbol: JsSymbol) -> Result { pub fn create_symbol() -> Symbol { Symbol::new("a symbol".to_owned()) } + +#[napi] +pub fn create_symbol_for(desc: String) -> Symbol { + Symbol::for_desc(desc) +} diff --git a/package.json b/package.json index 3b4bd882..4ec6ecff 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "format:rs": "cargo fmt", "format:toml": "taplo format", "lint": "eslint -c .eslintrc.yml .", - "test": "lerna run test --ignore @napi-rs/cli", + "test": "lerna run test --concurrency=1 --ignore @napi-rs/cli", "test:cli": "yarn workspace @napi-rs/cli test", "test:electron": "electron examples/napi/electron.js", "test:macro": "cargo test -p napi-examples",