From aada491c4d2824d6098406d0f82f926abf767ac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Wed, 15 Jul 2026 12:35:22 +0200 Subject: [PATCH] Fix type confusion in RegExp.escape with rope strings Fixes #1571 --- quickjs.c | 14 ++++++++++++-- tests/bug1571.js | 13 +++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 tests/bug1571.js diff --git a/quickjs.c b/quickjs.c index 41907fc50..d4731095d 100644 --- a/quickjs.c +++ b/quickjs.c @@ -48971,13 +48971,21 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { StringBuffer b_s, *b = &b_s; + JSValue str, ret; JSString *p; uint32_t c, i; char s[16]; if (!JS_IsString(argv[0])) return JS_ThrowTypeError(ctx, "not a string"); - p = JS_VALUE_GET_STRING(argv[0]); + if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_STRING_ROPE) { + str = js_linearize_string_rope(ctx, argv[0]); + if (JS_IsException(str)) + return JS_EXCEPTION; + } else { + str = js_dup(argv[0]); + } + p = JS_VALUE_GET_STRING(str); string_buffer_init2(ctx, b, 0, p->is_wide_char); for (i = 0; i < p->len; i++) { c = p->is_wide_char ? (uint32_t)str16(p)[i] : (uint32_t)str8(p)[i]; @@ -49011,7 +49019,9 @@ static JSValue js_regexp_escape(JSContext *ctx, JSValueConst this_val, string_buffer_putc16(b, c); } } - return string_buffer_end(b); + ret = string_buffer_end(b); + JS_FreeValue(ctx, str); + return ret; } static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, diff --git a/tests/bug1571.js b/tests/bug1571.js new file mode 100644 index 000000000..b0876810c --- /dev/null +++ b/tests/bug1571.js @@ -0,0 +1,13 @@ +import { assert } from "./assert.js"; + +function check(unit, halfCount) { + const rope = unit.repeat(halfCount) + unit.repeat(halfCount); + const flat = unit.repeat(halfCount * 2); + assert(rope.length, flat.length); + assert(RegExp.escape(rope), RegExp.escape(flat)); +} + +check("a", 50000); +check("a.b-c|d", 4000); +check("€☃", 4000); +