Skip to content

RegExp #102: ~58% compile-mode Fail reduction + interp Test262 signal fix#114

Merged
nickna merged 33 commits into
mainfrom
feature/regexp-102-escape
Jun 6, 2026
Merged

RegExp #102: ~58% compile-mode Fail reduction + interp Test262 signal fix#114
nickna merged 33 commits into
mainfrom
feature/regexp-102-escape

Conversation

@nickna
Copy link
Copy Markdown
Owner

@nickna nickna commented Jun 5, 2026

Summary

Addresses #102 (compile-mode RegExp Fail bucket) and lands a foundational Test262-harness fix discovered along the way.

Compiled RegExp test262 Fail: 307 → 128 (−179, ~58%); Pass 660 → 837. No regressions (full unit suite green except 2 pre-existing NuGet-packaging env failures, confirmed identical on main).

What's in here

  • Interp Test262 signal fix (foundational). Interpreter.Interpret() swallowed top-level guest throws, so the interp runner scored thrown assertions/TypeErrors as Pass — interp structurally couldn't report Fail. Now captured via Interpreter.LastUncaughtError and routed through the existing classifier. Regenerating the interp baseline reclassified 4,237 falsely-"Pass" tests across the subset. This makes the interp baseline trustworthy for the first time, and overturned Test262 #3: Compile-mode RegExp prototype produces wrong values (~440 Fail bucket) #102's premise (interp wasn't a gold standard — it just hid failures).
  • RegExp.escape (ES2025) in both runtimes (interp helper + standalone BCL-only $RegExp.Escape IL; exposed as call and value).
  • 2 compiled-only bugs: calling a RegExp (/x/()) now throws TypeError; RegExp(p,f) call-form now returns a RegExp.
  • Guest SyntaxError for invalid regex (both modes) — the single biggest lever (−114).
  • regexp-modifiers early-error validation (ES2025 (?ims-ims:…)) in both modes (compiled = pure-IL scanner).
  • Flags validation (d/g/i/m/s/u/v/y, no dup, not u+v); also fixed a real interp new RegExp(p, undefined) coercion bug it exposed (broke the real semver package).
  • from-regexp-like new-path (compiled)new RegExp(regexLike) reads source/flags via Get.

Not in scope here (probed, deferred — see docs/plans/regexp-102-scope.md)

Remaining #102 work is hard-tier and each was investigated:

  • Call-form identity / brand-check cluster (~9) — blocked by (/x/).constructor === RegExp being false (RegExp.prototype.constructor wiring); a clean prerequisite follow-up.
  • prototype/exec lastIndex (12) — hot-path storage rework (risky).
  • Engine semantics — u-fold/dotall/class-escapes (~16) — .NET-vs-ECMAScript Unicode/class differences.
  • unicode_restricted (8), prototype descriptor introspection (~8).
  • ~63 of the remaining 128 are Symbol.* → tracked under Test262 #2: RegExp Symbol.* protocol — harden interpreter + compiler to ECMA-262 §22.2.5 (~250 tests) #101.

Testing

  • dotnet test SharpTS.Tests — 10319/10321 (2 pre-existing packaging failures).
  • Both committed Test262 baselines regenerated; every baseline delta verified as a real RegExp Fail→Pass (batched-worker worker-stalled flakiness restored to keep diffs attributable).

nickna added 18 commits June 4, 2026 01:41
Interpreter.Interpret() catches a top-level guest throw, prints 'Runtime Error: ...' to stdout, and returns normally so the CLI need not surface a .NET stack trace. The Test262 interp runner captures that stdout and buckets only on a propagated exception, so a thrown assertion (Test262Error) or runtime TypeError was scored as Pass — interp could not report Fail at all.

Capture the swallowed throw in Interpreter.LastUncaughtError and route it through the existing ClassifyExecutionException in Test262Runner. The CLI behavior is unchanged (still prints + returns).
EncodeForRegExpEscape with the leading ASCII-alphanumeric -> \xHH rule,
syntax/control/punctuator/whitespace/surrogate handling, and TypeError on
non-string input.

- Interp: shared RegExpBuiltIns.EscapeString + a RegExp static namespace.
- Compiled: standalone BCL-only $RegExp.Escape IL (keeps $RegExp free of a
  SharpTS.dll runtime dependency) + RegExpStaticEmitter exposing it as both a
  call and a first-class value (typeof RegExp.escape === 'function').

Compiled RegExp test262 Fail 307 -> 289 (+18 Pass); interp Pass +15. Only
escape/cross-realm.js (needs $262) and escape/prop-desc.js (a generic
built-in-static own-descriptor gap) remain. Scoping for the rest of #102 in
docs/plans/regexp-102-scope.md.
Interp baseline reflects the swallowed-throw signal fix: 4,237 falsely-Pass
tests across the subset move to their true Fail/RuntimeError buckets (Pass
7405->3168). Both baselines also pick up RegExp.escape. RegExp folder now:
interp 513 Pass / 368 Fail; compiled 678 Pass / 289 Fail.
Earlier analysis used the swallow-inflated pre-signal-fix interp baseline and
wrongly classified regexp-modifiers as a compiled-only bug (claiming interp
passed 145/147). Re-measured against the corrected post-fix baselines:
regexp-modifiers fails ~equally in both modes (interp 90/compiled 87 Fail). Of
289 compiled RegExp Fails, 273 also fail interp (shared feature gaps); only 16
are compiled-only, 14 of which are Symbol.* (#101). #102 has 2 genuine
compiled-only bugs: S15.10.7_A1_T1/T2 (calling a RegExp must throw TypeError).
Two genuine compiled-only #102 bugs (interp already correct):

1. Calling a RegExp (/x/(), r()) returned null instead of throwing TypeError.
   ECMA-262 §22.2.6: a RegExp has no [[Call]]. Added a targeted $RegExp-callee
   check in InvokeValue/InvokeMethodValue that throws TypeError, leaving the
   general non-callable->null behavior untouched.

2. RegExp(pattern, flags) without 'new' returned null instead of a RegExp.
   Route the bare-call form through RegExpFromArgs (same as 'new RegExp'), via
   BuiltInConstructorHandler (alongside Date()/Array()).

Fixes test262 S15.10.7_A1_T1/T2. Compiled RegExp Fail 289 -> 286.
8 real bucket changes, all attributable to the RegExp(...) call-form + not-callable
fixes:
- RegExp: S15.10.7_A1_T1/T2 and S15.10.4.1_A1_T5 Fail->Pass.
- String/prototype/match: T4/T7/T8/T9 Fail->Pass (RegExp(x) now yields a real
  RegExp). T6 Pass->Fail: it was a false pass (both sides null because
  RegExp(undefined) used to return null); the fix unmasks a separate pre-existing
  compiled bug where 'var x' declared after use binds to null instead of
  undefined, so match(x) returns null while exec() returns a real result. interp
  also fails T6 (RuntimeError). Tracked as follow-up.

8 batched-worker 'worker-stalled'/'Timeout' flakiness entries in the Array folder
(unrelated to these changes) were restored to their committed values to keep the
diff attributable.
ECMA-262 §22.2.3.1 requires a SyntaxError when a RegExp pattern is invalid.
Both runtimes caught .NET's ArgumentException and rethrew a generic host
Exception, so guest code saw a non-SyntaxError (instanceof SyntaxError =
false) and assert.throws(SyntaxError, ...) failed.

- Interp SharpTSRegExp ctor: throw ThrowException(new SharpTSSyntaxError(...)).
- Compiled $RegExp ctor: throw $SyntaxError via CreateException instead of
  new Exception(...).

Biggest single #102 lever. RegExp-folder impact: compiled Fail 286 -> 172
(+113 Pass), interp Fail 368 -> 254 (+114 Pass). Covers patterns .NET rejects;
ECMAScript-specific invalid forms .NET accepts (modifier early errors,
unicode_restricted) still need explicit validation (follow-up).
114 RegExp tests move Fail->Pass in each mode (assert.throws(SyntaxError, ...)
on invalid patterns now holds). No regressions: every changed line is a RegExp
Fail->Pass. 2 batched-worker Array/of flakiness entries restored to committed
values to keep the diff attributable.
Validate (?addFlags-removeFlags:...) modifier groups and throw SyntaxError on:
non-i/m/s flag, duplicate within a set, flag in both sets, second dash, or (?-:).
.NET accepts several of these, so the check runs up front in both ctors.

- Interp: SharpTSRegExp.ValidateModifiers (C#, single-pass).
- Compiled: $RegExp.ValidateModifiers/ValidateModifierFlags/SkipCharClass (pure
  IL, standalone — mirrors the C#). Called from the $RegExp ctor before compile.

Skips escapes, char classes, and non-modifier (?:/(?=/(?!/(?<... forms; passes
valid groups. Compiled RegExp Fail 172 -> 143 (+29 Pass). 19 invalid patterns
throw SyntaxError, 16 valid pass, both modes. The ~10 u-flag Unicode
case-folding modifier tests remain (separate engine-semantics issue).
29 modifier early-error tests move Fail->Pass in each mode. No regressions; 2 batched-worker Array flakiness entries restored to committed values.
ECMA-262 §22.2.3.3 flag validation (ValidateFlags) in both ctors: unknown flag,
duplicate, or u+v together -> SyntaxError. NormalizeFlags silently dropped these.
- Interp: SharpTSRegExp.ValidateFlags (C#).
- Compiled: $RegExp.ValidateFlags (IL).

Also fixes the interp RegExp factory (CreateRegExp): undefined pattern/flags now
coerce to "" (SharpTSUndefined.ToString() was producing "undefined", which the
new validator correctly rejected -- broke the real semver package), and a RegExp
pattern copies source/flags instead of stringifying to /source/flags. Mirrors the
compiled RegExpFromArgs/RegExpCoerceArg.

Compiled RegExp Fail 143 -> 134 (+9 Pass). Full unit suite: only the 2
pre-existing packaging env failures.
Compiled: 9 RegExp Fail->Pass (invalid-flags tests). Interp: 19 Fail->Pass
(flags validation + the undefined/regex-copy coercion fix, incl. a String/trim
improvement) plus 2 Fail->RuntimeError bucket changes.

One interp Pass->RuntimeError: S15.10.4.1_A8_T9 (new RegExp(1, new Object('gi'))).
It was a false pass: new Object('gi') doesn't produce a real String wrapper in
interp, so its garbage ToString ('{ valueOf: gi }') happened to contain g/i and
the test only checks global/ignoreCase/multiline. ValidateFlags now correctly
rejects the garbage flags. Compiled passes it via ToJsString unwrap. Proper fix
(interp new Object(primitive) boxing + full flags ToString protocol) is a
follow-up. 2 batched-worker Array flakes restored to committed values.
ECMA-262 §22.2.4.1: a non-RegExp object whose [Symbol.match] is truthy (IsRegExp)
supplies its source/flags via Get rather than being ToString-ed to
'[object Object]'. Added a regexp-like branch to the compiled RegExpFromArgs
(reads pattern[Symbol.match] via GetIndex; source before flags so getter
throw-ordering is correct; honors a supplied flags arg over pattern.flags).

test262 from-regexp-like (4): from-regexp-like, -flag-override, -get-source-err,
-get-flags-err now pass. -short-circuit and -get-ctor-err remain (they need the
deferred call-form identity + constructor brand check). Guarded by non-null +
non-string + Symbol.match-truthy, so normal patterns are unaffected. Compiled-only
(interp's static CreateRegExp factory has no interpreter access for Get).
from-regexp-like, -flag-override, -get-source-err, -get-flags-err now pass. 2
deferred call-form cases (-short-circuit, -get-ctor-err) moved Fail->RuntimeError
(still failing, more diagnostic). 2 Array worker-stall flakes restored. Interp
baseline unchanged (compiled-only fix).
nickna added 11 commits June 4, 2026 17:45
(1) Compiled $RegExp instance reads of 'constructor' now resolve to the RegExp
constructor (the $RegExp Type token == RegExp-as-value), via a new branch in the
$RegExp GetProperty arm (after PDS so user overrides win). Fixes
(/x/).constructor === RegExp (was false).

(2) Proper ECMA-262 §22.2.4.1 step-1 call-form identity in EmitRegExp: RegExp(p)
returns the SAME object iff flags undefined AND IsRegExp(p) (p[Symbol.match]
truthy via GetIndex) AND p.constructor === %RegExp%. Fixes S15.10.3.1,
from-regexp-like-short-circuit/-get-ctor-err; keeps not_same_constructor and
match_falsy copying (brand checks resolve the earlier simple-short-circuit
regression). Full unit suite green (2 pre-existing packaging failures).
…#102)

ECMA-262 §22.2.5.2.2: lastIndex is an ordinary writable data property; ToLength
runs at RegExpBuiltinExec read time, not at assignment. The compiled $RegExp
coerced at write (and didn't invoke valueOf for objects), losing identity.

- New $RegExp._lastIndexBoxed slot holds a non-numeric assigned value; numeric
  assigns stay on the typed int _lastIndex (fast path untouched).
- ResolveLastIndex() (called at the start of Exec/Test) ToLength-coerces the boxed
  value into _lastIndex via ToNumber — valueOf fires exactly once per exec, even
  for non-global (which then doesn't write back, preserving identity).
- Numeric write-back / set_LastIndex / SetLastIndexStrict clear the boxed slot.
- GetProperty returns the boxed value when present; SetProperty stores raw
  non-numbers (no premature coercion).

Fixes prototype/exec success/failure-lastindex-access, success-g-lastindex-no-access,
failure-g-lastindex-reset. Numeric-lastIndex fast path verified unchanged; full
unit suite green (2 pre-existing packaging failures; 1 flaky streams test passes on
retry).
…Fail->Pass)

4 lastIndex-identity (success/failure-lastindex-access, success-g-lastindex-no-access, failure-g-lastindex-reset) + S15.10.6.2_A4_T10/T11/T12 (global exec) + 2 Symbol.match lastIndex-coercion tests. No regressions (A4_T4/T5 null-coercion handled). 2 Array worker-stall flakes restored.
ECMA-262 Annex B distinguishes Unicode (u/v) mode, where several forms
.NET tolerates are SyntaxErrors. Implement the false-positive-free
subset, shared by interp and compiled:

  1. A lookaround assertion immediately followed by a quantifier
     (e.g. /(?=.)* /u) is a SyntaxError.
  2. \c not followed by an ASCII letter (e.g. /\c/u) is a SyntaxError.

Interp: ValidateUnicodePattern + FindGroupClose in SharpTSRegExp,
gated on the u/v flag after ValidateModifiers.

Compiled: EmitTSRegExpValidateUnicodePattern + EmitTSRegExpFindGroupClose
(pure BCL-only IL, standalone-DLL safe) in RuntimeEmitter.TSRegExp,
gated in the ctor on flags.Contains('u'|'v') after ValidateFlags.

Verified no false positives against valid u-mode patterns ((?:.)*,
(abc)*, (?<n>a)*, \cA, \d+, \bfoo\b, [\cA]). Flips
unicode_restricted_identity_escape_c and
unicode_restricted_quantifiable_assertion Fail->Pass (compiled RegExp
Fail 109->107). Other Annex B u-mode rules need lookahead that risks
rejecting valid patterns; deferred.
ECMA-262 §22.2.6.1: RegExp.prototype.constructor === RegExp, and by
inheritance (/x/).constructor === RegExp. The compiled path already held
(the $RegExp Type token backs both the RegExp identifier and the
GetProperty 'constructor' arm); interp returned undefined.

Interp fix:
  - Cache the process-wide RegExp constructor singleton once
    (Interpreter.RegExpConstructorObject) from the static globals table.
  - Return it from a 'constructor' arm in EvaluateGetOnRegExp (faster
    than a prototype walk; matches compiled behavior).
  - Set it on the prototype object in RegExpBuiltIns.BuildPrototype so
    RegExp.prototype.constructor resolves and descriptor introspection
    has something to read.

The instance arm yields to a user-set own constructor (re.constructor =
fn) via a cheap TryGetProperty probe (a null check in the common no-own-
props case). Without this the bare singleton shadowed the own property
and broke RegExp.prototype[@@split]'s SpeciesConstructor path; verified
the full species-ctor flow works again.

Flips 5 interp tests Fail->Pass (S15.10.3.1_A3_T1, S15.10.7_A3_T1/T2,
prototype/S15.10.6.1_A1_T1/T2); no regressions (unit suite 10319 pass;
the 2 packaging-CLI failures are pre-existing). Prerequisite for the
§22.2.4.1 IsRegExp brand check that unblocks the ~18-test
from-regexp-like / call-form-identity family.
Brings the interp RegExp constructor to parity with the compiled
reference (which already passed all 18 brand-check-family tests).

New RegExpBuiltIns.ConstructRegExp(interp, args, isCallForm) implements
§22.2.4.1 with interpreter access:
  - IsRegExp (§22.2.7.2): reads Get(pattern, @@match) and ToBooleans it;
    a real regex with re[@@match]=false is NOT regexp-like.
  - Call-form same-constructor identity short-circuit: RegExp(re) with
    re.constructor===RegExp and no flags returns re unchanged (relies on
    the prototype-constructor wiring landed earlier).
  - Regexp-like source/flags via Get: honors user getters, propagates
    throws, reads source before flags, flags-arg overrides the getter.

Wired into both construction forms: the call form via
SharpTSBuiltInConstructor.Call (which was ignoring its interpreter
param), the new form via BuiltInConstructorFactory.TryCreate.

Supporting fixes:
  - Symbol-keyed storage on SharpTSRegExp (SetBySymbol /
    TryGetSymbolProperty, internal so the runtime<->emitted parity test
    is unaffected) + regex+symbol get/set dispatch in the interpreter
    (re[Symbol.match]=false previously threw).
  - Object.getPrototypeOf(regex) now returns the per-realm
    RegExp.prototype (was null for all interp regexes).

No compiled changes. 18 interp Fail->Pass: S15.10.3.1_A1_T1/T2/T4/T5 +
A3_T2, S15.10.4.1_A8_T5/T6/T8/T13, call_with_non_regexp_same_constructor,
call_with_regexp_match_falsy, the six from-regexp-like*, and a
getPrototypeOf test. No Pass regressions (3 String matchAll tests shift
RuntimeError->Fail — their setup, regex symbol-set, now works, exposing a
pre-existing matchAll @@matchAll-callability gap tracked under #101). Unit
suite 10319 pass (2 pre-existing packaging-CLI failures).
ECMA-262 §22.2.5.3: `flags` is a GENERIC accessor — it requires only
that `this` be an Object (not a RegExp) and builds the flag string by
reading each flag via Get + ToBoolean. Interp exposed no accessor
descriptor on RegExp.prototype, so
Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get was
undefined and the flags/coercion-* tests crashed (RuntimeError).

  - Generalized BuildFlagsString to object? receiver (it already read
    flags generically via Get).
  - Added a generic `flags` getter (BuiltInMethod: require-Object then
    BuildFlagsString) and exposed it via proto.DefineGetter in
    BuildPrototype.
  - Taught BindAccessorToObject to bind BuiltInMethod getters so direct
    RegExp.prototype.flags access passes the right `this`.
  - Broadened RequireObject (shared 'Type(R) is not Object' guard) to
    reject every primitive kind (number forms, string, boolean, Symbol,
    BigInt), matching the spec.

Instance re.flags unchanged (separate path). No compiled changes.

10 interp Fail->Pass: the 6 flags/coercion-*,
flags/this-val-regexp-prototype, flags/this-val-non-obj, plus 2 bonus
(Symbol.search/Symbol.split this-val-non-obj from the broader
RequireObject). No Pass regressions; unit suite 10319 pass (2
pre-existing packaging-CLI failures). The 3 flags/{length,name,prop-desc}
near-misses shift RuntimeError->Fail (verifyProperty descriptor-attribute
infrastructure — deferred, documented in the roadmap, along with the
compiled flags-getter generic-this remainder).
ECMA-262 §22.2.5.3: `flags` is a GENERIC accessor — it requires only
that `this` be an Object and builds the flag string by reading each flag
via Get + ToBoolean. The compiled `flags` accessor previously shared
EmitProtoAccessorPrologue, which throws for any non-RegExp `this` —
correct for the per-flag getters (global/ignoreCase/…, §22.2.5.4+, which
DO require a RegExp/prototype `this`) but wrong for the generic `flags`
getter, so Object.getOwnPropertyDescriptor(RegExp.prototype,'flags')
.get.call(plainObj) threw instead of building the flags (RuntimeError on
the flags/coercion-* tests).

Replaced it with a dedicated EmitProtoFlagsAccessor (pure BCL-only IL plus
the standalone runtime.GetProperty / runtime.IsTruthy helpers):
  - primitive `this` (null/undefined/string/bool/number/Symbol/BigInt)
    -> TypeError;
  - a real $RegExp -> cached-flags fast path (unchanged perf);
  - any other object -> build the flag string via Get + ToBoolean (so
    get.call(plainObj) works; RegExp.prototype itself yields "").

Mirrors the interp generic flags getter landed earlier. Flips the 6
compiled flags/coercion-* RuntimeError->Pass; no regressions
(this-val-non-obj stays Pass — BigInt is rejected). Unit suite 10319 pass
(2 pre-existing packaging-CLI failures). Array-folder worker-stall
flakiness restored to committed values for an attributable diff.
nickna added 4 commits June 4, 2026 23:30
Records that after the flags-accessor work, no clean RegExp wins remain:
the descriptor cluster's interp side is blocked by a foundational
Object.prototype-method-inheritance gap (plain objects don't inherit
hasOwnProperty/propertyIsEnumerable via the prototype chain; compiled
works), and the rest is hard engine semantics, char-class-range pattern
translation, harness-blocked (cross-realm/eval), or low-confidence
singletons. Recommends tracking these as separate issues off #102.
Interp ordinary objects did not inherit Object.prototype's methods via
the prototype chain — ({}).hasOwnProperty('x') threw "undefined is not a
function" (compiled already worked). This blocked the RegExp descriptor
cluster (A8/A9 call RegExp.prototype.hasOwnProperty) and, more broadly,
any test using obj.hasOwnProperty/propertyIsEnumerable/isPrototypeOf on a
plain object.

Fix: a FINAL fallback in EvaluateGetOnRecord/RV (after own properties and
the __proto__ chain, so a user override always wins) resolves
SharpTSObjectPrototype.GetMember(name) and returns it bound to the
receiver. Stringify uses HasProperty (own/__proto__ only), so object
stringification is unaffected.

Added an IsNullPrototype flag to SharpTSObject, set by Object.create(null)
and Object.groupBy, so genuine null-prototype objects inherit nothing —
without it Object/groupBy/null-prototype (asserts obj.hasOwnProperty ===
undefined) regressed.

+318 interp Test262 Pass, 0 regressions (315 built-ins/Object, 3
built-ins/Array). Interp-only; compiled already inherited. Unit suite
10319 pass (2 pre-existing packaging-CLI failures).
…ble (#102)

Descriptor-cluster clean subset (interp).

- Expose the §22.2.5 accessors (global/ignoreCase/multiline/dotAll/
  sticky/unicode/source, plus the generic flags) on RegExp.prototype as
  accessor properties { enumerable: false, configurable: true } with spec
  this-handling: a real regex returns the value; %RegExp.prototype%
  returns undefined (or "(?:)" for source); any other object throws
  TypeError. Instance re.global/etc. still resolve via EvaluateGetOnRegExp.
- Fix Object.prototype.propertyIsEnumerable to honor the descriptor's
  Enumerable flag — it returned HasProperty, so a non-enumerable own
  property wrongly reported true.

+36 interp Test262 Pass, 0 regressions: 29 RegExp (global/ignoreCase/
multiline A8 + 15.10.7.x-2, all this-val-* across the 7 accessors,
sticky/unicode prop-desc) + 7 Object descriptor tests. Interp-only;
compiled untouched. Unit suite 10319 pass (2 pre-existing packaging-CLI
failures).

Deferred (documented in roadmap): A9 (delete-of-getter) and the per-flag
prop-desc need delete-of-getter plus correct configurability, which in
turn needs interpreter-aware ToPropertyDescriptor coercion (a config
check on top of the current own-only/no-getter coercion regressed
attribute-coercion tests, so it was reverted).
Completes the descriptor cluster (interp).

- ObjectBuiltIns.ApplyBooleanAttributes: read writable/enumerable/
  configurable via interp.GetProperty (walks the prototype chain AND
  invokes getters) and ToBoolean-coerce, per §6.2.5.5 ToPropertyDescriptor.
  The static FromObject only handled own 'is bool' values, so truthy-string,
  inherited, and accessor-sourced attributes resolved wrong. Threaded
  through Object.defineProperty / defineProperties / create.
- SharpTSObject delete now removes accessor entries (getters/setters) and
  honors configurability — a non-configurable property blocks delete.
  Previously delete ignored configurability entirely (a real bug) and
  couldn't remove getter-only properties.

+173 interp Test262 Pass, -8 (net +165). New: RegExp prototype A9
(global/ignoreCase/multiline delete) + flags/per-flag prop-desc, plus a
broad sweep of Object/{defineProperty,create,defineProperties}
verifyProperty (delete-of-accessor) tests and correct delete-vs-
configurability behavior.

The 8 regressions are pre-existing foundational bugs the configurability
check exposes, all in spec-edge inputs no real code uses: (a) new Number()/
Boolean()/String() return primitives not objects, so ToBoolean of a wrapper
attribute value is wrong; (b) descriptor attributes given as get-less
accessors overriding inherited ones; (c) a RegExp used as a descriptor
(Get doesn't walk RegExp.prototype). Shipped deliberately: net real-world
correctness improves (delete now respects configurability); the edges are
filed as follow-ups (wrapper-object semantics; RegExp Get prototype walk).

Interp-only; compiled untouched. Unit suite 10319 pass (2 pre-existing
packaging-CLI failures).
@nickna nickna merged commit b56ffc9 into main Jun 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant