From 3dce1533fd123cde9e09ba1a435f176f5d34e034 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 24 Jun 2025 16:31:26 +0800 Subject: [PATCH] test(napi): add fixture for re-export complex class (#2733) --- .cargo/config.toml | 7 +- .github/workflows/test-release.yaml | 2 +- examples/napi-shared/src/lib.rs | 69 ++++++++++++++++++ .../__tests__/__snapshots__/values.spec.ts.md | 11 +++ .../__snapshots__/values.spec.ts.snap | Bin 6907 -> 6977 bytes examples/napi/__tests__/values.spec.ts | 43 +++++++++++ examples/napi/example.wasi-browser.js | 1 + examples/napi/example.wasi.cjs | 1 + examples/napi/index.cjs | 1 + examples/napi/index.d.cts | 11 +++ examples/napi/src/lib.rs | 1 + 11 files changed, 145 insertions(+), 2 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 969c2747..1a562eb6 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,4 +3,9 @@ # https://github.com/rust-lang/rust/issues/134820 # pthread_key_create() destructors and segfault after a DSO unloading [target.'cfg(any(all(target_env = "gnu", not(target_os = "windows")), target_os = "freebsd"))'] -rustflags = ["-C", "link-args=-Wl,-z,nodelete"] +rustflags = [ + "-C", + "link-args=-Wl,--warn-unresolved-symbols", + "-C", + "link-args=-Wl,-z,nodelete", +] diff --git a/.github/workflows/test-release.yaml b/.github/workflows/test-release.yaml index 2ccf73f1..bc8f1540 100644 --- a/.github/workflows/test-release.yaml +++ b/.github/workflows/test-release.yaml @@ -91,7 +91,7 @@ jobs: yarn test:cli yarn test yarn tsc -p examples/napi/tsconfig.json --noEmit --skipLibCheck - yarn test:macro + RUSTFLAGS="-C link-args=-Wl,-undefined,dynamic_lookup,-no_fixup_chains" yarn test:macro toolchain: stable - host: windows-latest target: x86_64-pc-windows-msvc diff --git a/examples/napi-shared/src/lib.rs b/examples/napi-shared/src/lib.rs index ef4b2251..788218ab 100644 --- a/examples/napi-shared/src/lib.rs +++ b/examples/napi-shared/src/lib.rs @@ -1,6 +1,75 @@ +use napi::{bindgen_prelude::ClassInstance, Either}; use napi_derive::napi; #[napi(object)] pub struct Shared { pub value: u32, } + +// Test fixture for GitHub issue #2722: Complex struct with constructor and multiple methods +#[napi] +pub struct ComplexClass { + pub value: String, + pub number: i32, +} + +impl From<(String, i32)> for ComplexClass { + fn from(value: (String, i32)) -> Self { + ComplexClass { + value: value.0, + number: value.1, + } + } +} + +impl<'env> From, String>> for ComplexClass { + fn from(value: Either, String>) -> Self { + match value { + Either::A(instance) => ComplexClass { + value: (&*instance).value.clone(), + number: instance.number, + }, + Either::B(value) => ComplexClass { value, number: 0 }, + } + } +} + +#[napi] +impl ComplexClass { + #[napi(constructor)] + pub fn new(value: Either>, number: i32) -> Self { + let value_str = match value { + Either::A(s) => s, + Either::B(instance) => format!("cloned:{}", (&*instance).value), + }; + ComplexClass { + value: value_str, + number, + } + } + + #[napi] + pub fn method_one(&self) -> String { + format!("method_one: {}", self.value) + } + + #[napi] + pub fn method_two(&self) -> i32 { + self.number * 2 + } + + #[napi] + pub fn method_three(&self) -> String { + format!("method_three: {} - {}", self.value, self.number) + } + + #[napi] + pub fn method_four(&self) -> bool { + self.number > 0 + } + + #[napi] + pub fn method_five(&self) -> String { + self.value.to_uppercase() + } +} diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.md b/examples/napi/__tests__/__snapshots__/values.spec.ts.md index 249eb8a9..7328dfae 100644 --- a/examples/napi/__tests__/__snapshots__/values.spec.ts.md +++ b/examples/napi/__tests__/__snapshots__/values.spec.ts.md @@ -1050,6 +1050,17 @@ Generated by [AVA](https://avajs.dev). export function xxh128(input: Buffer): bigint␊ export function xxh3_64(input: Buffer): bigint␊ }␊ + export declare class ComplexClass {␊ + value: string␊ + number: number␊ + constructor(value: string | ComplexClass, number: number)␊ + methodOne(): string␊ + methodTwo(): number␊ + methodThree(): string␊ + methodFour(): boolean␊ + methodFive(): string␊ + }␊ + ␊ export interface Shared {␊ value: number␊ }␊ diff --git a/examples/napi/__tests__/__snapshots__/values.spec.ts.snap b/examples/napi/__tests__/__snapshots__/values.spec.ts.snap index b79a6336ced363b106d60dfb69dce6a4e0a0471d..d5127807400bac49b70ae103bb78edcc86d12af6 100644 GIT binary patch delta 6867 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 diff --git a/examples/napi/__tests__/values.spec.ts b/examples/napi/__tests__/values.spec.ts index ccae19ca..68d97510 100644 --- a/examples/napi/__tests__/values.spec.ts +++ b/examples/napi/__tests__/values.spec.ts @@ -243,6 +243,7 @@ import { JSOnlyMethodsClass, RustOnlyMethodsClass, OriginalRustNameForJsNamedStruct, + ComplexClass, } from '../index.cjs' // import other stuff in `#[napi(module_exports)]` import nativeAddon from '../index.cjs' @@ -1841,3 +1842,45 @@ test('escapable handle scope', (t) => { shorterEscapableScope(makeIterFunction()) }) }) + +test('complex class with multiple methods - issue #2722', (t) => { + // Test creating instance of re-exported class with constructor (Either>) + t.notThrows(() => { + const complex = new ComplexClass('test_value', 42) + + // Test that constructor worked + t.is(complex.value, 'test_value') + t.is(complex.number, 42) + + // Test all methods work + t.is(complex.methodOne(), 'method_one: test_value') + t.is(complex.methodTwo(), 84) + t.is(complex.methodThree(), 'method_three: test_value - 42') + t.is(complex.methodFour(), true) + t.is(complex.methodFive(), 'TEST_VALUE') + }) + + // Test with Either::B variant (ClassInstance instead of string) + t.notThrows(() => { + const original = new ComplexClass('original', 100) + const complex2 = new ComplexClass(original, -10) + t.is(complex2.value, 'cloned:original') // Should clone the value + t.is(complex2.methodFour(), false) + }) + + // Test that we can create multiple instances (stress test with Either) + t.notThrows(() => { + const baseInstance = new ComplexClass('base', 999) + for (let i = 0; i < 10; i++) { + // Alternate between string and ClassInstance for Either parameter + const instance = + i % 2 === 0 + ? new ComplexClass(`test${i}`, i) + : new ComplexClass(baseInstance, i) + + const expectedValue = i % 2 === 0 ? `test${i}` : 'cloned:base' + t.is(instance.value, expectedValue) + t.is(instance.number, i) + } + }) +}) diff --git a/examples/napi/example.wasi-browser.js b/examples/napi/example.wasi-browser.js index 034e4c29..94fcfaee 100644 --- a/examples/napi/example.wasi-browser.js +++ b/examples/napi/example.wasi-browser.js @@ -365,3 +365,4 @@ export const withoutAbortController = __napiModule.exports.withoutAbortControlle export const xxh64Alias = __napiModule.exports.xxh64Alias export const xxh2 = __napiModule.exports.xxh2 export const xxh3 = __napiModule.exports.xxh3 +export const ComplexClass = __napiModule.exports.ComplexClass diff --git a/examples/napi/example.wasi.cjs b/examples/napi/example.wasi.cjs index b4f0b65f..17277f68 100644 --- a/examples/napi/example.wasi.cjs +++ b/examples/napi/example.wasi.cjs @@ -389,3 +389,4 @@ module.exports.withoutAbortController = __napiModule.exports.withoutAbortControl module.exports.xxh64Alias = __napiModule.exports.xxh64Alias module.exports.xxh2 = __napiModule.exports.xxh2 module.exports.xxh3 = __napiModule.exports.xxh3 +module.exports.ComplexClass = __napiModule.exports.ComplexClass diff --git a/examples/napi/index.cjs b/examples/napi/index.cjs index 0d35d555..f256a085 100644 --- a/examples/napi/index.cjs +++ b/examples/napi/index.cjs @@ -680,3 +680,4 @@ module.exports.withoutAbortController = nativeBinding.withoutAbortController module.exports.xxh64Alias = nativeBinding.xxh64Alias module.exports.xxh2 = nativeBinding.xxh2 module.exports.xxh3 = nativeBinding.xxh3 +module.exports.ComplexClass = nativeBinding.ComplexClass diff --git a/examples/napi/index.d.cts b/examples/napi/index.d.cts index 8d05b8d1..650c0a73 100644 --- a/examples/napi/index.d.cts +++ b/examples/napi/index.d.cts @@ -1012,6 +1012,17 @@ export declare namespace xxh3 { export function xxh128(input: Buffer): bigint export function xxh3_64(input: Buffer): bigint } +export declare class ComplexClass { + value: string + number: number + constructor(value: string | ComplexClass, number: number) + methodOne(): string + methodTwo(): number + methodThree(): string + methodFour(): boolean + methodFive(): string +} + export interface Shared { value: number } diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 9ce5cad7..b18a795b 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -8,6 +8,7 @@ #[cfg(not(target_family = "wasm"))] use napi::bindgen_prelude::create_custom_tokio_runtime; use napi::bindgen_prelude::{JsObjectValue, Object, Result, Symbol}; +pub use napi_shared::*; #[macro_use] extern crate napi_derive;