ReviewOS

zig-utils/zig-regex

ECMAScript: quantified assertions and (?P groups are accepted, and three defects report no reason

#32
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Two acceptance bugs and three missing diagnostics, all in ECMAScript mode. Every "JavaScriptCore" column below was produced by running the same program under JavaScriptCore (home-tool); node v24.4.1 accepts and rejects the same patterns.

1. An Assertion takes no Quantifier. ECMAScript's Term lets a Quantifier follow an Atom, not an Assertion (22.2.1). Annex B adds exactly one exception, QuantifiableAssertion, which is a lookahead outside u mode. These compiled and matched:

patternzig-regexJavaScriptCore
/^*/ /^+/ /^?/ /^{2}/acceptedInvalid regular expression: nothing to repeat
/$*/ /\b*/ /\B*/acceptedInvalid regular expression: nothing to repeat
/^*/u /$*/u /\b*/uacceptedInvalid regular expression: nothing to repeat

/(?=a)*/, /(?!a)*/ and /(?=a){2}/ stay legal outside u, as Annex B says.

2. (?P<x>a) was parsed as a Python-style named group. After (? ECMAScript allows only :, =, ! and <. JavaScriptCore reports Invalid regular expression: unrecognized character after (? for (?P<x>a), (?P=x) and (?i)a.

3. Three defects reached the embedder as the bare fallback. CompileErrorReason had no variant for them, so an embedder could only print "invalid regular expression":

patternJavaScriptCore
/(?<=a)*/, /(?<!a)?/ (any mode)Invalid regular expression: invalid quantifier
/a{/u, /a{1/uInvalid regular expression: incomplete {} quantifier for Unicode pattern
/]/u, /}/uInvalid regular expression: unmatched ] or } bracket for Unicode pattern

Fixed in 1d3e5c9. The rejections are ECMAScript-only — other dialects still compile ^* and (?P<x>a) — and the new reasons are invalid_quantifier, incomplete_quantifier_for_unicode_pattern and unmatched_bracket_for_unicode_pattern. A 25-row table test pins each verdict, zig build test-core passes, and the 20,775-case node differential is unchanged (800 refused, the same 308 mismatches, 0 regressed).

9 days ago

Fixed by 1d3e5c9; pinned at the JavaScript level in zig-js next.

9 days ago

Downstream corpus check for 30c1fa9 and 1d3e5c9 together, run from zig-js against test262 (Debug test262-bin, one binary at bc14170 and one at 1d3e5c9, same corpus, --diag per subtree, diffed row by row):

  • 51 subtree groups, 1,947 passes, identical row for row, 0 changed: every RegExp/* subdirectory (CharacterClassEscapes, Symbol.species, dotall, escape, lookBehind, match-indices, named-groups, prototype, regexp-modifiers, unicodeSets), 23 prefix groups covering the RegExp/ top-level files, String/{match,matchAll,replace,replaceAll,search,split}, RegExpStringIteratorPrototype, language/literals/regexp, annexB/built-ins/RegExp, and 8 of the 31 sampled property-escapes/generated files.
  • The single failure, literals-regexp 1 fail, is on both binaries and unrelated (an unterminated-comment early error, since fixed downstream).
  • The run hit its 4-hour wall-clock bound with 23 of the sampled property-escapes files still to go; those are Unicode property tests with no quantified assertions or (? extensions in them, so nothing in this change can reach them.

So refusing ^*, $*, \b*, \B* and (?P<x>a), and accepting the nested quantifiers that the ReDoS guard used to reject, flips no test262 case in either direction.

Sign in to comment on this issue.