Run exec on regular expressions held as any (#1600)

This commit is contained in:
Chris Tate
2026-10-20 20:16:04 -05:00
committed by GitHub
4 changed files with 72 additions and 0 deletions
@@ -0,0 +1,6 @@
{
"order": [
"<repo>/tests/corpus/6902-dyn-regexp-exec.js"
],
"diags": []
}
+18
View File
@@ -2784,6 +2784,24 @@ static ScrDyn *scr_native_regex_invoke(void *ptr, ScrDyn *self, const char *meth
scr_str_release(subject);
return scr_exc_pending() ? NULL : scr_dyn_new_bool(matches);
}
if (strcmp(method, "exec") == 0) {
/* The match array: each capture (undefined when it did not
* participate), then index, input and groups; null without a match.
* The exec itself reads and advances lastIndex. */
ScrStr *subject = scr_dyn_string_coerce_js(argc ? args[0] : scr_dyn_undefined());
if (!subject) return NULL;
ScrArr *row = scr_regex_exec(subject, re);
scr_str_release(subject);
if (!row) return scr_exc_pending() ? NULL : scr_dyn_new_null();
ScrDyn *match = scr_dyn_new_arr();
for (size_t k = 0; k < row->len; k++)
scr_dyn_arr_push(match, *scr_arr_state_ptr(row, k) == SCR_ARR_VALUE
? scr_dyn_new_str((ScrStr *)(uintptr_t)row->data[k])
: scr_dyn_retain(scr_dyn_undefined()));
scr_arr_copy_metadata(row, match);
scr_arr_release(row);
return match;
}
static const char message[] = "Native RegExp method has no lowering";
scr_throw_error_msg_code(SCR_ERR_ERROR, message, sizeof message - 1, "SC2020");
return NULL;
+47
View File
@@ -0,0 +1,47 @@
// exec on a regular expression held as an untyped value returns Node's
// match array: the captures (undefined for one that did not participate),
// index, input and groups. Global and sticky expressions read and advance
// lastIndex, and reset it when nothing matches.
function dyn(value) {
return [value][0];
}
function show(label, match) {
if (match === null) return console.log(label, null);
console.log(label, JSON.stringify([...match]), match.index, match.input, JSON.stringify(match.groups), match.length);
}
const plain = dyn(/(\d+)-(x)?/);
show("plain", plain.exec("a 12- b"));
show("miss", plain.exec("none"));
console.log("plain lastIndex", plain.lastIndex);
const global = dyn(/o(.)/g);
show("g1", global.exec("foo boo"));
console.log("after g1", global.lastIndex);
show("g2", global.exec("foo boo"));
console.log("after g2", global.lastIndex);
show("g3", global.exec("foo boo"));
console.log("after g3", global.lastIndex);
global.lastIndex = 5;
show("from 5", global.exec("foo boo"));
const sticky = dyn(/b/y);
show("sticky miss", sticky.exec("ab"));
sticky.lastIndex = 1;
show("sticky hit", sticky.exec("ab"));
console.log("sticky after", sticky.lastIndex);
const named = dyn(/(?<year>\d{4})-(?<month>\d{2})(?:-(?<day>\d{2}))?/);
const date = named.exec("on 2024-05");
show("named", date);
console.log("year", date.groups.year, "day", date.groups.day, Object.getPrototypeOf(date.groups) === null);
const unicode = dyn(/\u{1F600}(.)/u);
show("unicode", unicode.exec("x\u{1F600}y"));
show("coerced", dyn(/1/).exec(123));
const matches = [];
const all = dyn(/a(\d)/g);
let m;
while ((m = all.exec("a1 a2 a3")) !== null) matches.push(m[1] + "@" + m.index);
console.log("loop", matches.join(","), all.lastIndex);
console.log("Array.isArray", Array.isArray(dyn(/x/).exec("x")));
@@ -0,0 +1 @@
{ "plain": 2611, "san": 3084 }