Repository navigation
test262 built-ins/String — 32 (self-contained worklist) #5902
Description
Self-contained worklist — 32 failing. Snapshot 2026-07-03 full sweep (perry 0.5.1218, node v26.3.0). Everything you need is in this issue; no history to read, no re-measure to start.
String methods on non-generic receivers, normalize, well-formed, symbol coercion. Crate: perry-runtime (string), perry-codegen.
Setup (any box)
git clone https://github.com/PerryTS/perry && cd perry
cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static
git clone --depth 1 https://github.com/tc39/test262 vendor/test262 # checkout test-compat/test262/pinned-sha.txt
# node on PATH (differential oracle). See test-compat/test262/README.md.Measure/verify: scripts/test262_subset.py --root vendor/test262 --dir built-ins/String --jobs 8 (the .failures.txt sidecar lists failures; diff before/after for zero-regression).
Failure reasons
| n | reason |
|---|---|
| 4 | Uncaught exception: #1.1: Exception === "intostr". Actu |
| 3 | Uncaught exception: #1: __instance = new Object(true); |
| 2 | Uncaught exception: #1: __instance = new Boolean; __ins |
| 2 | Uncaught exception: #1.1: Exception is instance of Type |
| 2 | Uncaught exception: Expected a TypeError to be thrown b |
| 1 | Uncaught exception: #2: toString=function(){return "__T |
| 1 | Uncaught exception: #1: Function.prototype.indicator = |
| 1 | TypeError: Cannot assign to read only property 'length' |
| 1 | Uncaught exception: #2: var __str__instance = new Strin |
| 1 | Uncaught exception: #1: delete String.prototype.toStrin |
| 1 | Uncaught exception: #2: delete String.prototype.toStrin |
| 1 | Uncaught exception: #1: var x; __instance = {toString:f |
All 32 failing cases (path — reason)
built-ins/String/S15.5.1.1_A1_T9.js Uncaught exception: #2: toString=function(){return "__THIS__";}; __str = String(
built-ins/String/S15.5.3_A2_T2.js Uncaught exception: #1: Function.prototype.indicator = 1; String.indicator === 1
built-ins/String/S15.5.5.1_A4_T1.js TypeError: Cannot assign to read only property 'length' of object '#<Object>'
built-ins/String/S15.5.5.1_A5.js Uncaught exception: #2: var __str__instance = new String("ABC\u0041\u0042\u0043"
built-ins/String/prototype/S15.5.4_A1.js Uncaught exception: #1: delete String.prototype.toString; String.prototype.toStr
built-ins/String/prototype/S15.5.4_A3.js Uncaught exception: #2: delete String.prototype.toString; String.prototype.toStr
built-ins/String/prototype/concat/S15.5.4.6_A4_T1.js Uncaught exception: #1: var x; __instance = {toString:function(){return "one"}};
built-ins/String/prototype/constructor/S15.5.4.1_A1_T2.js TypeError: String.prototype.valueOf called on incompatible receiver
built-ins/String/prototype/indexOf/S15.5.4.7_A1_T1.js Uncaught exception: #1: __instance = new Object(true); __instance.indexOf = Stri
built-ins/String/prototype/indexOf/S15.5.4.7_A1_T2.js Uncaught exception: #1: __instance = new Boolean; __instance.indexOf = String.pr
built-ins/String/prototype/indexOf/S15.5.4.7_A4_T4.js Uncaught exception: #1.1: Exception === "intostr". Actual: Test262Error: #1: "va
built-ins/String/prototype/indexOf/S15.5.4.7_A4_T5.js Uncaught exception: #1.1: Exception === "intostr". Actual: Test262Error: #1: "va
built-ins/String/prototype/lastIndexOf/S15.5.4.8_A1_T1.js Uncaught exception: #1: __instance = new Object(true); __instance.lastIndexOf =
built-ins/String/prototype/lastIndexOf/S15.5.4.8_A1_T2.js Uncaught exception: #1: __instance = new Boolean; __instance.lastIndexOf = Strin
built-ins/String/prototype/lastIndexOf/S15.5.4.8_A4_T2.js Uncaught exception: #1.1: Exception === "intointeger". Actual: Test262Error: #1:
built-ins/String/prototype/lastIndexOf/S15.5.4.8_A4_T4.js Uncaught exception: #1.1: Exception === "intostr". Actual: Test262Error: #1: var
built-ins/String/prototype/lastIndexOf/S15.5.4.8_A4_T5.js Uncaught exception: #1.1: Exception === "intostr". Actual: Test262Error: #1: var
built-ins/String/prototype/localeCompare/15.5.4.9_CE.js Uncaught exception: String.prototype.localeCompare considers ö (\u006f\u0308) ≠
built-ins/String/prototype/padEnd/observable-operations.js Uncaught exception: Expected SameValue(«"abc"», «"abcdefdefde"») to be true
built-ins/String/prototype/padStart/observable-operations.js Uncaught exception: Expected SameValue(«"abc"», «"defdefdeabc"») to be true
built-ins/String/prototype/replace/S15.5.4.11_A1_T1.js Uncaught exception: #1: __instance = new Object(true); __instance.replace = Stri
built-ins/String/prototype/replace/S15.5.4.11_A1_T10.js Uncaught exception: #1: var x; var __obj = {toString:function(){return "AB";}};
built-ins/String/prototype/replace/S15.5.4.11_A1_T12.js Uncaught exception: #1.1: Exception === "insearchValue". Actual: Test262Error: #
built-ins/String/prototype/replace/S15.5.4.11_A1_T15.js Uncaught exception: #1.1: Exception is instance of TypeError. Actual: Test262Err
built-ins/String/prototype/replace/S15.5.4.11_A1_T16.js Uncaught exception: #1.1: Exception is instance of TypeError. Actual: Test262Err
built-ins/String/prototype/replace/S15.5.4.11_A1_T2.js Uncaught exception: #1: var x; __instance = new Boolean; __instance.replace = St
built-ins/String/prototype/replace/S15.5.4.11_A1_T5.js Uncaught exception: #1: function(){return "gnulluna"}().replace(null, Function()
built-ins/String/prototype/replace/S15.5.4.11_A1_T9.js Uncaught exception: #1: __obj = {valueOf:function(){}, toString:void 0}; functio
built-ins/String/prototype/replace/replaceValue-evaluation-order-regexp-object.js Uncaught exception: Expected SameValue(«0», «1») to be true
built-ins/String/prototype/split/separator-regexp.js SyntaxError: Invalid regular expression: /\k<x>/: invalid pattern
built-ins/String/prototype/toString/non-generic.js Uncaught exception: Expected a TypeError to be thrown but no exception was throw
built-ins/String/prototype/valueOf/non-generic.js Uncaught exception: Expected a TypeError to be thrown but no exception was throw
Workflow
Pick a coherent single-root subcluster, localize (cargo run --release -- f.ts --trace hir --focus <fn> / --trace llvm), fix, verify listed cases pass AND re-run the slice for zero regressions. Code-only PR (no Cargo.toml/Cargo.lock version bump, no CLAUDE.md Current-Version, no CHANGELOG — maintainer folds metadata). cargo fmt --all -- --check + bash scripts/check_file_size.sh must pass (files < 2000 lines). Reference this issue. Never reference internal infrastructure hostnames.
Activity
proggeramlug commentedon Jul 4, 2026
Localized the biggest subcluster (String.prototype methods borrowed onto non-string objects —
obj.indexOf = String.prototype.indexOf; obj.indexOf(true, false)→ -1 instead of coercingthis; alsolastIndexOf/concat).Root is at the HIR lowering layer, not runtime dispatch:
x.indexOf(a, b)on an any-typed receiver is eagerly folded toExpr::ArrayIndexOf { array: x, ... }(confirmed via--print-hir), so a borrowedString.prototype.indexOfthunk stored as an own property never runs — the array-like engine searches the receiver's indexed elements and returns -1.String.prototype.indexOf.call(obj, ...)works (the.callfold-exclusion routes it through the generic string thunk); only the direct member formobj.indexOf(...)mis-folds.The fix belongs in the
.indexOf/.lastIndexOf/.concatfold in perry-hir (don't fold toArrayIndexOfwhen the receiver has, or may have, an own property of that name). That's a broad optimization change with regression surface across all array indexOf/lastIndexOf/concat call sites, so leaving it for a dedicated maintainer pass rather than rushing it. A runtime-layerclassify_own_slotattempt does not help (codegen never reaches the runtime dispatch for these folded calls).fix(runtime,hir): observable spec-order argument coercion for reflect…
fix(runtime,hir): observable spec-order argument coercion for reflect…
proggeramlug commentedon Aug 5, 2026
Localized
S15.5.5.1_A4_T1— it is not a String bug, it is sloppy-mode read-modify-writeFiling the diagnosis rather than a fix: the root is a missing field on a HIR variant, which is wider than this worklist entry and deserves its own PR.
The failing line in that test is neither of the plain assignments — it is
__str__instance.length++(and thewith(...) length = 0further down). Plain__str__instance.length = -1already behaves correctly.Characterization (node 26.5.1 vs perry @
0f9bb6cc5, all sloppy mode):new String("globglob").length++8new String("globglob").length += 18new String("globglob").length = -188✅Object.freeze({a:1}).a++1Object.freeze({a:1}).a = 211✅Object.defineProperty(o,"x",{value:5,writable:false}); o.x++5(function fn(a,b){}).length++22✅a[0]++11✅So this is not String-specific —
Object.freeze(cfg); cfg.count++throws on any frozen plain object, which is ordinary real-world code. Only the named-property read-modify-write path is affected; closures and array indices take other paths and are already correct.Root cause. The two forms lower to different HIR, and only one of them carries the strict flag:
Expr::PropertyUpdate(perry-hir/src/ir/expr.rs) has no strict-mode information, so its codegen cannot make the sloppy-vs-strict decision and ends up on the runtime setter that throws unconditionally — e.g. theif is_frozen { throw_immutable_write(0, …) }gates inperry-runtime/src/object/field_set_by_name/tail.rs. Per spec these are[[Set]]failures that returnfalse; only strict mode turns that into aTypeError.Fix shape. Give
PropertyUpdatethe samestrictflagPutValueSetalready carries, populate it inperry-hir/src/lower/expr_misc.rsfrom the enclosing strictness, and have codegen select the throwing vs. silent setter from it. Mechanically that means the variant plusstable_hash/expr.rs,monomorph/substitute_expr.rs, and the codegen consumer; the ~50 files that mentionPropertyUpdatemostly match with..and are unaffected.Note
IndexUpdateis the sibling variant and is worth checking for the same gap while in there.The
with(__str__instance) length = 0;line in the same test is a separate question (whetherwithis supported at all) and is not covered by the above.Found while landing #7470 / #7471.
fix(hir): boxed primitive wrappers are not arrays — stop the array fo…
fix(runtime): a borrowed builtin is only an Array builtin when it IS …
fix(string): honor canonical equivalence in localeCompare (#5902)
fix(string): honor canonical equivalence in localeCompare (#5902) (#8349
proggeramlug commentedon Aug 18, 2026
The final localeCompare canonical-equivalence fix landed in #8349. Closing this String compatibility worklist as completed.