fix(runtime): built-ins/RegExp (non-lookbehind) test262 parity - #4809
Merged
Conversation
Raises built-ins/RegExp test262 parity 70.7% -> ~93% (419 -> 551 pass), zero regressions. Root causes addressed: - RegExp.prototype accessor getters: `source`/`flags`/`global`/`ignoreCase`/ `multiline`/`dotAll`/`sticky`/`unicode`/`unicodeSets`/`hasIndices` are now real accessor descriptors on RegExp.prototype (brand-checked native getters, `set:undefined`, enumerable:false, configurable:true), so reflection (`getOwnPropertyDescriptor(RegExp.prototype,"source").get`) and `.call(this)` work and instances inherit them. New `regex_proto_thunks.rs`. The `flags` getter is spec-generic (reads d/g/i/m/s/u/v/y off the receiver via Get+ToBoolean); Symbol `this` throws TypeError. - `source` now applies EscapeRegExpPattern (empty -> "(?:)", `/`->`\/` outside a class, line terminators escaped). - exec/test: honor `lastIndex` + anchoring for the sticky `y` flag (not just global); `test` routes through `exec` for stateful regexes. `lastIndex` now stores an arbitrary value (ToLength on read) per spec, not a u32. - exec/test ToString-coerce their argument (String wrappers, numbers, objects with throwing toString) via new `js_jsvalue_to_string_coerce` (undefined->"undefined", null->"null"). - Real brand-checked `exec`/`test`/`toString` prototype methods. - Full RegExp constructor (`js_regexp_construct`): RegExp pattern (copy / flag override), undefined/null/object pattern (ToString), object flags (ToString -> SyntaxError). Fixes garbage from a regexp pattern being read as a string, and a HIR fold that dropped object-literal flags. - Empty character classes `[]` (never matches) and `[^]` (any) translate to `[^\s\S]` / `[\s\S]` (the `regex` crate rejects them). - instances walk RegExp.prototype for inherited members (`re.constructor`). Lookbehind is out of scope (the `regex` crate lacks it; `fancy-regex` covers some cases via the existing fallback) and was not targeted. Remaining failures are deep object-model items (lastIndex as a full own property, expando storage on the non-ObjectHeader regex, call-vs-new identity), `u`-mode strict early errors, and `regex`-crate limits (lone surrogates, huge quantifiers, JS `\s` vs Unicode `\s`), plus eval-based source tests Perry cannot AOT-evaluate. Files: regex.rs, regex/grammar.rs, regex/match_all.rs, object/regex_proto_thunks.rs (new), object/global_this.rs, object/field_get_set.rs, object/native_call_method.rs, value/to_string.rs, value/mod.rs, and codegen (instance_misc1.rs, logical_collections.rs, lower_call/builtin.rs, runtime_decls/strings.rs, hir lower/expr_new.rs).
proggeramlug
force-pushed
the
regexp-parity
branch
from
June 8, 2026 15:59
c46522a to
60839d5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Raises built-ins/RegExp test262 parity from 71.0% → 92.9% (421 → 551 pass, +130) measured against the branch's merge-base, with zero regressions validated across a broad
built-ins+languageshard.Validation (merge-base
dcfdfd8f3vs branch, same pinned test262, jobs=40)built-ins+language(1/12, strided)Failure-set diff on the broad shard: 0 tests went pass→fail (zero regressions); 14 newly-passing (13 RegExp + 1 Proxy bonus from the constructor/source fixes).
Root causes fixed (grouped)
source,flags,global,ignoreCase,multiline,dotAll,sticky,unicode,unicodeSets,hasIndicesare now real accessor descriptors onRegExp.prototype(brand-checked native getters;set: undefined,enumerable: false,configurable: true). Reflection (getOwnPropertyDescriptor(RegExp.prototype, "source").get) and.call(this)work, and instances inherit them. New moduleobject/regex_proto_thunks.rs. Theflagsgetter is spec-generic (assemblesd g i m s u v yfromGet+ToBooleanon the receiver); a Symbol/non-objectthisthrowsTypeError.sourceescaping —EscapeRegExpPattern: empty →(?:),/→\/outside a char class, line terminators escaped.y—exec/testconsult and advancelastIndexand anchor the match for sticky (previously onlyglobalwas stateful);testroutes throughexecfor stateful regexes.lastIndexsemantics — now stores an arbitrary value (re.lastIndex = objround-trips) withToLengthapplied on read, per spec, instead of a coercedu32.ToString— String wrappers / numbers / objects (incl. a throwingtoString) are coerced via newjs_jsvalue_to_string_coerce(undefined→"undefined",null→"null", objects dispatchtoString).exec/test/toStringprototype methods.js_regexp_construct) — RegExp pattern (copy / flag override),undefined/null/object pattern (ToString), object flags (ToString→SyntaxError). Fixes garbage output from a RegExp pattern being read as a string, and a HIR fold that silently dropped object-literal flags.[](never matches) and[^](any) translate to[^\s\S]/[\s\S](theregexcrate rejects them).RegExp.prototypefor inherited members (re.constructor, reflective method reads).Lookbehind (out of scope)
18 built-ins/RegExp tests use lookbehind (
(?<=…)/(?<!…)). The Rustregexcrate doesn't support it; these are served by the pre-existingfancy-regexfallback and were not targeted here (skip=0— none are among the remaining failures).Remaining (not addressed — deep object-model or crate limits)
lastIndexas a full own property (hasOwnProperty/non-writable attr), expando storage on the non-ObjectHeaderregex, and call-vs-newidentity (RegExp(re)returnsre) — require object-model changes.u-mode strict early errors (lone]/{, alpha identity escapes, incomplete quantifiers) — skipped to avoid false-positives on valid\p{…}/\u{…}patterns (zero-regression mandate).regex-crate limits: lone surrogates,b{9007199254740991}, JS\svs Unicode\s(U+0085).propertyIsEnumerableover inherited accessor descriptors (attr plumbing);isPrototypeOffor built-in instances (pre-existing, also affects Map/Set).prototype/source/value*tests rely oneval(), which Perry (AOT) cannot evaluate.Files
regex.rs,regex/grammar.rs,regex/match_all.rs,object/regex_proto_thunks.rs(new),object/global_this.rs,object/field_get_set.rs,object/native_call_method.rs,object/mod.rs,value/to_string.rs,value/mod.rs; codegen:expr/instance_misc1.rs,expr/logical_collections.rs,lower_call/builtin.rs,runtime_decls/strings.rs,hir lower/expr_new.rs.