fix(napi): JsStringUtf8 memory leak (#2911)

This commit is contained in:
LongYinan 2025-09-08 13:45:17 +08:00 committed by GitHub
parent e633c472e7
commit 3894db5e42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 26 additions and 11 deletions

View File

@ -109,13 +109,11 @@ impl<'env> JsString<'env> {
)
})?;
// respect '\0' with js string, for example: `let hello = [a,'\0',b,'\0',c].join('')`
let mut result = mem::ManuallyDrop::new(result);
let buf_ptr = result.as_mut_ptr();
let bytes = unsafe { std::slice::from_raw_parts(buf_ptr.cast(), written_char_count) };
mem::forget(result);
Ok(JsStringUtf8 {
inner: self,
buf: bytes,
buf: unsafe { Vec::from_raw_parts(buf_ptr.cast(), written_char_count, written_char_count) },
})
}
@ -161,7 +159,9 @@ impl<'env> JsString<'env> {
Ok(JsStringLatin1 {
inner: self,
buf: unsafe { std::slice::from_raw_parts(buf_ptr.cast(), written_char_count) },
_inner_buf: vec![],
_inner_buf: unsafe {
Vec::from_raw_parts(buf_ptr.cast(), written_char_count, written_char_count)
},
})
}
}

View File

@ -1,20 +1,21 @@
use std::convert::TryFrom;
use std::str;
use crate::{bindgen_prelude::ToNapiValue, sys, Error, JsString, Result};
use crate::{bindgen_prelude::ToNapiValue, sys, Error, JsString, Result, Status};
pub struct JsStringUtf8<'env> {
pub(crate) inner: JsString<'env>,
pub(crate) buf: &'env [u8],
pub(crate) buf: Vec<u8>,
}
impl<'env> JsStringUtf8<'env> {
pub fn as_str(&self) -> Result<&str> {
Ok(unsafe { str::from_utf8_unchecked(self.buf) })
str::from_utf8(self.buf.as_slice())
.map_err(|err| Error::new(Status::InvalidArg, err.to_string()))
}
pub fn as_slice(&self) -> &[u8] {
self.buf
self.buf.as_slice()
}
pub fn len(&self) -> usize {

View File

@ -832,6 +832,8 @@ Generated by [AVA](https://avajs.dev).
export declare function indexSetToRust(set: Set<string>): void␊
export declare function intoUtf8(s: string): string␊
export declare function jsErrorCallback(value: unknown): Array<Error>
/** default enum values are continuos i32s start from 0 */␊

View File

@ -286,6 +286,7 @@ import {
arrayParams,
indexSetToRust,
indexSetToJs,
intoUtf8,
} from '../index.cjs'
// import other stuff in `#[napi(module_exports)]`
import nativeAddon from '../index.cjs'
@ -337,6 +338,7 @@ test('string', (t) => {
t.is(createExternalLatin1String(), 'External Latin1')
t.is(createStaticLatin1String(), 'Static Latin1 string')
t.is(createStaticUtf16String(), 'Static UTF16')
t.is(intoUtf8('Hello'), 'Hello')
})
test('JsStringLatin1::from_external tests', (t) => {

View File

@ -283,6 +283,7 @@ export const i8ArrayToArray = __napiModule.exports.i8ArrayToArray
export const indexmapPassthrough = __napiModule.exports.indexmapPassthrough
export const indexSetToJs = __napiModule.exports.indexSetToJs
export const indexSetToRust = __napiModule.exports.indexSetToRust
export const intoUtf8 = __napiModule.exports.intoUtf8
export const jsErrorCallback = __napiModule.exports.jsErrorCallback
export const Kind = __napiModule.exports.Kind
export const KindInValidate = __napiModule.exports.KindInValidate

View File

@ -328,6 +328,7 @@ module.exports.i8ArrayToArray = __napiModule.exports.i8ArrayToArray
module.exports.indexmapPassthrough = __napiModule.exports.indexmapPassthrough
module.exports.indexSetToJs = __napiModule.exports.indexSetToJs
module.exports.indexSetToRust = __napiModule.exports.indexSetToRust
module.exports.intoUtf8 = __napiModule.exports.intoUtf8
module.exports.jsErrorCallback = __napiModule.exports.jsErrorCallback
module.exports.Kind = __napiModule.exports.Kind
module.exports.KindInValidate = __napiModule.exports.KindInValidate

View File

@ -728,6 +728,7 @@ module.exports.i8ArrayToArray = nativeBinding.i8ArrayToArray
module.exports.indexmapPassthrough = nativeBinding.indexmapPassthrough
module.exports.indexSetToJs = nativeBinding.indexSetToJs
module.exports.indexSetToRust = nativeBinding.indexSetToRust
module.exports.intoUtf8 = nativeBinding.intoUtf8
module.exports.jsErrorCallback = nativeBinding.jsErrorCallback
module.exports.Kind = nativeBinding.Kind
module.exports.KindInValidate = nativeBinding.KindInValidate

View File

@ -793,6 +793,8 @@ export declare function indexSetToJs(): Set<string>
export declare function indexSetToRust(set: Set<string>): void
export declare function intoUtf8(s: string): string
export declare function jsErrorCallback(value: unknown): Array<Error>
/** default enum values are continuos i32s start from 0 */

View File

@ -1,4 +1,4 @@
use napi::{bindgen_prelude::*, JsString, JsStringLatin1, JsStringUtf16};
use napi::{bindgen_prelude::*, JsString, JsStringLatin1, JsStringUtf16, JsStringUtf8};
#[napi(object)]
pub struct Latin1MethodsResult {
@ -40,6 +40,11 @@ pub fn return_c_string() -> RawCString {
RawCString::new(mock_c_string_ptr, NAPI_AUTO_LENGTH)
}
#[napi]
pub fn into_utf8(s: JsString) -> Result<JsStringUtf8> {
s.into_utf8()
}
#[napi]
/// Function to test escaped quotes in comments.
/// This comment contains escaped quotes: \\"g+sx\\" and should not break JSON parsing.