From 071c76f250f5a6fd964def342417af653132937b Mon Sep 17 00:00:00 2001 From: Nathan Voglsam Date: Fri, 5 Dec 2025 16:39:20 +1100 Subject: [PATCH 1/2] Add 'JS_FreeCStringRT' --- quickjs.c | 8 ++++++++ quickjs.h | 1 + 2 files changed, 9 insertions(+) diff --git a/quickjs.c b/quickjs.c index 5a15e00eb..e4a98cecf 100644 --- a/quickjs.c +++ b/quickjs.c @@ -4360,6 +4360,14 @@ void JS_FreeCString(JSContext *ctx, const char *ptr) JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); } +void JS_FreeCStringRT(JSRuntime *rt, const char *ptr) +{ + if (!ptr) + return; + /* purposely removing constness */ + JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, (JSString *)ptr - 1)); +} + static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len) { int c, i; diff --git a/quickjs.h b/quickjs.h index b1ab008d9..effc62db5 100644 --- a/quickjs.h +++ b/quickjs.h @@ -820,6 +820,7 @@ static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1) return JS_ToCStringLen2(ctx, NULL, val1, 0); } JS_EXTERN void JS_FreeCString(JSContext *ctx, const char *ptr); +JS_EXTERN void JS_FreeCStringRT(JSRuntime *rt, const char *ptr); JS_EXTERN JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id); From 9c55a7ff970697f904a5e820167564fb40e49cf3 Mon Sep 17 00:00:00 2001 From: Nathan Voglsam Date: Sun, 7 Dec 2025 13:46:24 +1100 Subject: [PATCH 2/2] Add test for JS_FreeCStringRT --- api-test.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/api-test.c b/api-test.c index 28ff361a6..c6e9d2133 100644 --- a/api-test.c +++ b/api-test.c @@ -382,6 +382,44 @@ static void module_serde(void) JS_FreeRuntime(rt); } +static void runtime_cstring_free(void) +{ + JSRuntime *rt = JS_NewRuntime(); + JSContext *ctx = JS_NewContext(rt); + // string -> cstring + JS_FreeCStringRT + { + JSValue ret = eval(ctx, "\"testStringPleaseIgnore\""); + assert(JS_IsString(ret)); + const char *s = JS_ToCString(ctx, ret); + assert(s); + assert(strcmp(s, "testStringPleaseIgnore") == 0); + JS_FreeCStringRT(rt, s); + JS_FreeValue(ctx, ret); + } + // string -> cstring + JS_FreeCStringRT, destroying the source value first + { + JSValue ret = eval(ctx, "\"testStringPleaseIgnore\""); + assert(JS_IsString(ret)); + const char *s = JS_ToCString(ctx, ret); + assert(s); + JS_FreeValue(ctx, ret); + assert(strcmp(s, "testStringPleaseIgnore") == 0); + JS_FreeCStringRT(rt, s); + } + // number -> cstring + JS_FreeCStringRT + { + JSValue ret = eval(ctx, "123987"); + assert(JS_IsNumber(ret)); + const char *s = JS_ToCString(ctx, ret); + assert(s); + assert(strcmp(s, "123987") == 0); + JS_FreeCStringRT(rt, s); + JS_FreeValue(ctx, ret); + } + JS_FreeContext(ctx); + JS_FreeRuntime(rt); +} + static void two_byte_string(void) { JSRuntime *rt = JS_NewRuntime(); @@ -804,6 +842,7 @@ int main(void) raw_context_global_var(); is_array(); module_serde(); + runtime_cstring_free(); two_byte_string(); weak_map_gc_check(); promise_hook();