You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
10000
V8 runtime rejects Unicode property escapes (\p{...}) because V8 is built without ICU #1195
qjs ok
v8 Script(line 1): SyntaxError: Invalid regular expression: /\p{L}/u: Invalid property name
The result was the same on 17.22.2 (V8 10.x) and 17.24.3 (V8 15.7).
Cause
meson_options.txt in frida/v8 has i18n disabled by default (L79-L83). Without V8_INTL_SUPPORT, ParsePropertyClassName and AddPropertyClassRange are stubs that always fail (regexp-parser.cc#L2572-L2588).
Possible fixes
Build V8 with ICU. The meson build already handles the Intl sources and Torque files under i18n. What's missing is an ICU dependency in releng (WrapDB's icu 78.2 port matches the ICU version V8 15.7 pins), ICU_UTIL_DATA_STATIC, and an ICU init call in gumv8platform.cpp. This also brings Intl, localeCompare and toLocaleString with locale support. The obvious costs are binary size and the chance of ICU symbols clashing with targets that load their own ICU.
Support \p{...} without ICU. Fill in the two non-ICU stubs using compact Unicode property tables, for example the ones QuickJS's libunicode already ships. That fixes regex only, with a much smaller size cost, but it's a patch to carry in the V8 fork.
Is either direction something you'd take? I'm happy to work on it and can bring size numbers before opening PRs. Was disabling i18n mainly about size, or are there other constraints I should design around?
I prototyped both directions to get real numbers. Setup: frida-gum d24632b, frida/v8 f6d7f661 (V8 15.7), Linux x86_64, GCC 15, Frida's own configure flags (-Os, --gc-sections), prebuilt SDK for everything except V8 and ICU. Sizes are of a stripped executable that links frida-gum and GumJS with both runtimes.
Size
Variant
Stripped size added (baseline 31.4 MB)
libunicode in the non-ICU stubs
+53 KB (+0.17%)
ICU 78.2, root-only locale data
+6.4 MB (+20%)
ICU 78.2, English-only data
+6.5 MB (+21%)
ICU 78.2, all locales
+28.8 MB (+92%)
ICU 78.2, full WrapDB data
+35.8 MB (+114%)
ICU's code alone is about 2.7 MB (icui18n 1.1 MB, icuuc 0.9 MB, V8's Intl objects 0.3 MB) no matter how much data is kept. The trimmed data sets were made with icupkg, so they are upper bounds.
Correctness (test262 built-ins/RegExp plus the related String.prototype directories, 2,016 files, run through the GumJS script backend)
V8 today
libunicode
ICU
QuickJS
RegExp/property-escapes (613)
163
613
613
613
RegExp/unicodeSets (114)
48
114
114
114
All 2,016
1457
1978
2000
1999
The libunicode build regresses nothing. What it still fails compared to ICU are the other existing non-ICU gaps: /ui folding of ſ, the Kelvin sign and astral letters, normalize() being a no-op, astral identifiers and group names, and astral case mapping.
libunicode prototype: it only replaces the two #else stubs in regexp-parser.cc (+429/-4) and mirrors the ICU path: General_Category, Script and Script_Extensions values with their aliases, the same binary property allowlist V8 uses with ICU, Any/ASCII/Assigned, the /v properties of strings, and /vi case closure before negation. libunicode (Unicode 17, same as ICU 78) is vendored unmodified and compiled with its symbols renamed, so it cannot clash with QuickJS's copy. Compile and match times are on par with ICU.
ICU prototype: this took more than the issue suggested:
The i18n option also needs the gen-regexp-special-case step (BUILD.gn generates special-case.cc with ICU at build time) and ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC.
WrapDB's ICU in static mode links icuuc against a stub data library that defines the same icudt78_dat symbol as the real one, so link order decides which wins. I embedded the data from V8's own build instead.
Cross builds would need an ICU for the build machine to run the generator. I have not wired that up.
It changes runtime behavior in the host process. V8 computes local time through ICU, and Date.prototype.toString() zone names change from (CST) to (Central Standard Time). ICU resolves the zone through /etc/localtime and can fall back to reading all of /usr/share/zoneinfo. Even with UDATA_NO_FILES it still probes $ICU_DATA and checks $ICU_TIMEZONE_FILES_DIR for tz files to load.
Given that, I'd lean towards the libunicode approach for this issue and treat shipping Intl as a separate decision. Since the patch only touches the non-ICU branch, enabling ICU later would just bypass it. Would you take a PR to frida/v8 along those lines? Sharing QuickJS's libunicode instead of vendoring a second copy would also save 33 KB when both runtimes are built, if you'd prefer that.
Activity
Bump effect to 4.0.5 and frida to 17.24.3
leonitousconforti commentedon Oct 30, 2026
I prototyped both directions to get real numbers. Setup: frida-gum
d24632b, frida/v8f6d7f661(V8 15.7), Linux x86_64, GCC 15, Frida's own configure flags (-Os,--gc-sections), prebuilt SDK for everything except V8 and ICU. Sizes are of a stripped executable that links frida-gum and GumJS with both runtimes.Size
ICU's code alone is about 2.7 MB (icui18n 1.1 MB, icuuc 0.9 MB, V8's Intl objects 0.3 MB) no matter how much data is kept. The trimmed data sets were made with
icupkg, so they are upper bounds.Correctness (test262
built-ins/RegExpplus the relatedString.prototypedirectories, 2,016 files, run through the GumJS script backend)RegExp/property-escapes(613)RegExp/unicodeSets(114)The libunicode build regresses nothing. What it still fails compared to ICU are the other existing non-ICU gaps:
/uifolding ofſ, the Kelvin sign and astral letters,normalize()being a no-op, astral identifiers and group names, and astral case mapping.libunicode prototype: it only replaces the two
#elsestubs inregexp-parser.cc(+429/-4) and mirrors the ICU path: General_Category, Script and Script_Extensions values with their aliases, the same binary property allowlist V8 uses with ICU,Any/ASCII/Assigned, the/vproperties of strings, and/vicase closure before negation. libunicode (Unicode 17, same as ICU 78) is vendored unmodified and compiled with its symbols renamed, so it cannot clash with QuickJS's copy. Compile and match times are on par with ICU.ICU prototype: this took more than the issue suggested:
i18noption also needs thegen-regexp-special-casestep (BUILD.gn generatesspecial-case.ccwith ICU at build time) andICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC.icuucagainst a stub data library that defines the sameicudt78_datsymbol as the real one, so link order decides which wins. I embedded the data from V8's own build instead.Date.prototype.toString()zone names change from(CST)to(Central Standard Time). ICU resolves the zone through/etc/localtimeand can fall back to reading all of/usr/share/zoneinfo. Even withUDATA_NO_FILESit still probes$ICU_DATAand checks$ICU_TIMEZONE_FILES_DIRfor tz files to load.Given that, I'd lean towards the libunicode approach for this issue and treat shipping Intl as a separate decision. Since the patch only touches the non-ICU branch, enabling ICU later would just bypass it. Would you take a PR to frida/v8 along those lines? Sharing QuickJS's libunicode instead of vendoring a second copy would also save 33 KB when both runtimes are built, if you'd prefer that.