fix(runtime): built-ins/Array sort + mutation + species test262 parity#4994
Merged
Conversation
…est262 parity built-ins/Array mutation dirs (sort/splice/concat/filter/some/map/flat/ flatMap/copyWithin/fill/toSorted/toSpliced): 729 -> 821 of 823 (+92). Broad shard 0/12 of built-ins+language vs origin/main: 0 new failures, +252 fixed (85.2% -> 94.7%). Root causes: - HIR array-literal element inference took the FIRST element's type, so a mixed literal claimed Array(Number) and === between two such loads compiled to a raw fcmp (NaN-boxed bool/string/undefined never equal). Mixed literals now infer Array(Any). - Array.prototype.sort gained the spec-ops SortIndexedProperties path for exotic receivers (index accessors, sparse storage, inherited Array.prototype / Object.prototype elements, custom array prototypes): HasProperty/Get collection (undefined partitioned, never compared), GC-rooted temp arrays across comparator calls, Set write-back that fires inherited Object.prototype setters, trailing DeletePropertyOrThrow. Default sort partitions holes/undefined; comparator results go through ToNumber (NaN -> +0). - Array.prototype noop thunks (sort, concat, forEach/map/filter/some/every/ find*/reduce*/indexOf/lastIndexOf/includes/at/join) replaced with real thunks routing the generic js_arraylike_* engine, so reflective borrows (obj.sort = Array.prototype.sort) and prototype-chain hits run the real algorithm. The generic engine gained sort/splice/concat entries, Proxy receivers (traps incl. revoked-throw), Date/RegExp/Error expando receivers, own-accessor shadowing, and canonical Object.prototype fallbacks for inherited length/indices. - Type-confused receivers (a statically-Array variable reassigned to a plain object) reaching dense ArrayHeader entry points (sort/splice/ concat/length) are detected on the RAW pointer before clean_arr_ptr and routed to the generic engine. - concat/splice run ArraySpeciesCreate (poisoned constructor accessors, non-constructor species TypeError, ArrayCreate RangeError for len >= 2^32); splice throws on non-writable/getter-only length; spliced/concat holes read through the prototype chain. - copyWithin: side-effecting index coercions that mutate the array divert to a per-index spec loop; generic form ToLength clamps at 2^53-1 and routes Proxy has/get/set/delete traps. fill: explicit-undefined end means length, -Infinity end means 0, accessor-aware length read. - Inline index/length fast paths stand down via runtime guard flags when index accessors, Array.prototype / Object.prototype indexed pollution, or a custom array [[Prototype]] exist; hole and OOB reads then walk the real chain. delete of an accessor-only object property now clears the descriptor side table. foo.prototype = someArray is stored and linked to instances. Proxy/handle id bands are never dereferenced as GcHeaders. Remaining 2: filter/15.4.4.20-9-b-6 (bare Array[1] global lowering gap) and sort/call-with-primitive (needs Symbol/BigInt wrapper objects). Verified: 12-dir sweep 821/823 with clean compile cache, runtime unit tests 1022 pass, shard 0/12 built-ins+language zero-regression vs origin/main.
proggeramlug
pushed a commit
that referenced
this pull request
Jun 11, 2026
da4f8be (#4994) landed with a red lint job (admin merge), so every PR's merge-ref now fails it. Two findings, both from that commit: - array/sort.rs:126 — the second tail-copy loop's store sits one line outside the ±6-line window of the existing GC_STORE_AUDIT(STACK) marker; add the same marker next to that loop (same caller-rooted scratch-buffer justification). - array/from_concat.rs:174 + object/prototype_chain.rs:58 — new GcHeader probes outside addr_class.rs (from_concat is the #4994 split of the already-allowlisted concat_reverse probe; prototype_chain's is guarded by is_above_handle_band + is_valid_obj_ptr). Allowlist both with justifications.
proggeramlug
added a commit
that referenced
this pull request
Jun 11, 2026
…ed adapter options and no-ops (#4996) * fix(stdlib): real semantics or loud warn for silently-ignored adapter options and no-ops (#4917) zlib: deflate-family stream factories + deflateRawSync honor options.level (threaded through make_codec_state; .reset() rebuilds at the same level); a supplied dictionary warns once via the shared runtime validator (covers ext-zlib too); Brotli/zstd factories warn once when an options object is passed. exponential-backoff: real npm semantics — numOfAttempts/startingDelay/ timeMultiple/maxDelay/delayFirstAttempt/jitter/retry parsed and honored, and Promise-returning tasks now retry on REJECTION (previously the first promise was passed through and no retry ever happened) via a GC-rooted state machine chaining js_promise_then + timer-queue delays instead of blocking thread::sleep. mongodb: findOne resolves a parsed document object (the JSON.parse property-access bug that blocked this is fixed); BSON types surface in relaxed extended-JSON shape. mysql2/pg: FieldPacket.type/columnType carry the numeric MySQL wire type ID (name->ID map; sqlx 0.8 keeps the raw byte pub(crate)); pg fields get numeric dataTypeID (type OID), tableID/columnID from the RowDescription via sqlx relation_id()/relation_attribute_no(), dataTypeSize/-Modifier sentinel -1, format "text". Both stdlib and ext twins updated. http.Agent: keepSocketAlive/reuseSocket warn once (reqwest owns the pool); destroy() un-flagged — it really drops the per-agent client on the ext path. fastify: storing an 'upgrade' handler warns at registration (#1113 tracks real dispatch). worker.ref/unref: already real (event-loop refcount verified both directions); stale stub notes dropped — the lines #4917 cited are MessagePort no-ops, not Worker. Manifest: #4917 stub inventory 18 -> 9 with narrowed notes; keystone test updated; docs regenerated. New parity test test_gap_zlib_4917_level.ts matches node v26 byte-for-byte. * lint: unbreak main's audit gates inherited from #4994 da4f8be (#4994) landed with a red lint job (admin merge), so every PR's merge-ref now fails it. Two findings, both from that commit: - array/sort.rs:126 — the second tail-copy loop's store sits one line outside the ±6-line window of the existing GC_STORE_AUDIT(STACK) marker; add the same marker next to that loop (same caller-rooted scratch-buffer justification). - array/from_concat.rs:174 + object/prototype_chain.rs:58 — new GcHeader probes outside addr_class.rs (from_concat is the #4994 split of the already-allowlisted concat_reverse probe; prototype_chain's is guarded by is_above_handle_band + is_valid_obj_ptr). Allowlist both with justifications. --------- Co-authored-by: Ralph Küpper <ralph@skelpo.com>
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
Closes the built-ins/Array mutation-method tail: 729 → 821 of 823 (+92) across the 12 swept dirs (
sort splice concat filter some map flat flatMap copyWithin fill toSorted toSpliced), judged against the node v26 oracle. The two remaining failures are out-of-scope tails (bareArray[1]global-ident lowering gap;sort.call(Symbol())needs Symbol/BigInt wrapper objects).Zero-regression validation
===-correctness,in-operator, and delete fixes help well beyond Array.cargo test -p perry-runtime(RUST_TEST_THREADS=1): 1022 pass.Root causes (before → after)
[1,true,"x"]claimedArray(Number);===between two such loads compiled to rawfcmp— NaN-boxed bool/string/undefined never equalArray.prototype/Object.prototypeelements mis-sorted; holes & undefined stringified instead of partitioned to the end;sort/precise-*all redArray.prototype.{sort,concat,forEach,map,filter,some,every,find*,reduce*,indexOf,lastIndexOf,includes,at,join}were noop thunksobj.sort = Array.prototype.sort) or prototype-chain hit silently returned garbagevar x=[]; … x={0:0}; x.sort()/x.splice()/x.lengthread an ObjectHeader as ArrayHeader (orclean_arr_ptrNULLed it → silent no-op)lengthwritabilityArray.prototype[i]/Object.prototype[i], custom array[[Prototype]]— fixed via cheap runtime guard flags (relaxed atomics) so unpolluted programs keep the fast pathArray.prototype.indexOf.call(proxy, …)SIGSEGV; generic engine now routes Proxy traps (incl. revoked-throw)deleteof an accessor-only object property succeeded vacuouslyAlso: own setter-only accessors now shadow inherited props in the generic engine;
fillend-coercions (explicit undefined → len, −∞ → 0);foo.prototype = someArrayis stored and linked to instances; sort comparator results go through ToNumber (NaN → +0); callbacks bindthis = undefinedwhen no thisArg (explicit thisArg routes the generic engine).Code-only PR per contributor guidelines — no version bump / changelog (maintainer folds at merge).