Skip to content

Merge train 188: runtime, JSON, GC and compiler fixes (v0.5.1566) - #10261

Merged
proggeramlug merged 30 commits into
mainfrom
train188r
Sep 14, 2026
Merged

proggeramlug merged 30 commits into
mainfrom
train188r

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Land ordinary property Get, JSON traversal feedback, declared-class symbol properties, pure re-export pruning, registry-index preparation, handle-stack and GC polling changes, nursery-cap checks, and native numeric loop state as Perry 0.5.1566. This train contains #10248, #10249, #10250, #10251, #10252, #10253, #10254, #10255, and the remaining two follow-ups from #10245.

Integration fixes preserve symbol-property presence without invoking getters, honor prototype overrides, and validate tracked allocation ownership before reading class metadata. Android and OHOS retain scoped access to OS-owned TLS so teardown cannot leave a cached dangling stack reference; native handle buffers keep the prior four-slot initial capacity. GC holder records are merged structurally with pins recomputed from the merged source. The source-graph test helper clears an earlier compiled output before asserting that collect-only mode creates none.

Validation uses the approved fast protocol. Each original PR's full gap, GC-stress, and core cargo-test CI was attributed against main; the current main run has exactly the same three gated gap failures as the earlier baseline. All original PR GC-stress jobs passed. The core cargo-test failures are the known custom-thread-stack test; #10245's final run passed it.

  • Full lint: 80/83 pass. Public benchmark freshness, API-docs drift, and the WebAssembly dead-code warning reproduce on main. Raw-handle debt and holder pins pass without relaxing either ratchet.
  • Debug runtime: 3,857 passed, four ignored. Release runtime: 3,855 passed, four ignored, with only the known release-only heap-generation debug-assertion test failing. Codegen: 1,538 passed, one ignored. Compiler unit tests: 1,137 passed. Stdlib: 139 passed. The host Android-TLS integration passed.
  • The declared-class symbol integration passed all 16 assertions. The final source-graph rerun passed all 57 tests and GC instrumentation smoke passed. Native-property (1), thenable (2), and symbol (19) gap tests pass. JSON passed 69 cases with the accepted descriptor gap JSON.parse lazy array: Object.defineProperty index accessor is bypassed by reads #10097 and two unchanged lifecycle skips. Reflect-metadata fails in Node on identical main source; re-export fails on the previously verified missing RxJS Node fixture. All 25 regex cases and the concrete allocation-point GC fixture pass. Actual Android execution passed 31 runtime tests, both TLS-pool integration tests, and a scope-drop check in the third POSIX destructor pass after TLS teardown. The proxy/reflect array-to-string mismatch reproduces byte-for-byte on main 4945fc1, both in the original fixture and a smaller coercion probe, using a fresh build with the same five-package selection.
  • All 52 JSON fixture/operation outputs match Node 26.5.1. Four runtime probes also match Node. Both performance arms use the same five Cargo packages, static wrappers, and runtime settings.

Measured tradeoffs: four runtime workloads use 8.57–15.50% less CPU with flat or lower RSS; record-array scans use 25.8–28.6% less CPU, and two small-object parse cases use about half the peak RSS. Longer repeated measurements also show empty-object parse CPU +2.49%, empty-object stringify CPU +3.51%, and 1 MiB record-array round-trip peak RSS increasing from 63.59 to 80.61 MiB (+17.02 MiB, +26.76%) over 970 calls. Escaped-string stringify measured +1.66% CPU in the longer follow-up; its instruction count was flat. The round-trip retention change has not been attributed to a single PR.

The final test-harness fix produces byte-identical JSON benchmark machine code to the measured candidate (11,808,956-byte __text section, SHA-256 f63e00306116c2795f58f81648f88c3d0091321e52a12644ab0ee26c909247e0). The owner approved these measured compute/RSS tradeoffs. Local validation is complete.

Outside the three approved CI tiers, #10252 has a Linux debug-runtime property-read abort that has not been reproduced or attributed locally. The same probes pass on the landed macOS baseline in release and paired debug configurations, and on this train in release. Other auxiliary failures are runner/build/oracle issues or have explicit baseline reproduction; they are not being presented as green tests.

CI baseline: https://github.com/PerryTS/perry/actions/runs/34813821843

Closes #10180

Summary by CodeRabbit

  • Performance

    • Faster ordinary property access, Reflect.get, and inherited toJSON lookups.
    • Reduced overhead for garbage-collection checks, runtime handle management, JSON parsing, and regular-expression workloads.
    • Improved optimization of JSON record loops and lazy-array traversal feedback.
  • Bug Fixes

    • Fixed symbol-keyed properties added to declared class prototypes.
    • Improved correctness for runtime handles during growth, garbage collection, exceptions, and thread teardown.
    • Re-export pruning now safely handles more side-effect-free modules.
  • Documentation

    • Added benchmark reports, measurement instructions, validation results, and performance evidence.
  • Chores

    • Updated the project version to 0.5.1566.

Ralph Küpper added 30 commits September 14, 2026 08:39
…aversal evidence

Traversal feedback (#10150) learns that a program scans its parsed arrays
from element reads in lazy_get_rooted. The element-shape loop clone (#10171)
materializes a lazy array whole in its preheader through
js_array_refresh_local_head before any element is read lazily, so a scan loop
served by the clone never produced evidence: every parse built the tape and
then materialized every record anyway. On the quiet bench mini
records_array_16k:scan and records_array_1m:scan read 1.11x and 1.18x the
better of Node and Bun, with a third of the parse samples in build_tape_into.

js_array_refresh_local_head now notes one flip's worth of evidence the first
time it materializes a lazy array. Its emitters are all cold arms that run
about once per receiver, so the added tracked-header probe is not on a hot path.
Refs #10110. Adapt the reusable index and population/churn witnesses from proggeramlug#1 for main. Scale all three hash slices with the per-owner width and cover concurrent refreshes and address reuse. The canonical-wrapper owner is still on the campaign branch; document its required integration without closing the issue.
…d trigger checks

Runtime safepoint polls (regex quanta, the microtask pump, the event loop),
gc_malloc and the other gc_check_trigger callers spent hundreds to thousands
of instructions answering "is a collection due?" when nothing was. Three
changes, each leaving every collection decision unchanged:

- Runtime polls no longer build a JsGcStepResult. The budgeted step returns a
  debt-free GcStepReport; the FFI and test entry points attach the
  GcDebtSnapshot after the step returns, which reads the same values.
  The cycle start/step machinery moved out of line so a no-trigger poll does
  not pay its multi-kilobyte frame.
- copying_from_space_in_use_bytes() is O(1) between layout changes: the
  bytes outside Eden's current block are cached, keyed on the heap
  generation (every reset, detach, evacuation and survivor flip runs inside a
  HeapChange scope), and every move of an arena's current block goes through
  Arena::set_current, which invalidates the cache. Debug builds compare every
  cached answer with the block walk.
- gc_check_trigger evaluates the due trigger once instead of up to three
  times, reusing the answer unless it came from the #10169 one-shot leaf
  priority; the young cap reuses the old-gen pressure the due trigger already
  read (debug cross-checked) and tests the census-seeded flag first.
… reaches its cap

The tiny-parse guard prices forced collections on total arena in-use
(#9831), which a collection cannot lower below the live set. It never
asked the generational question of whether the young generation is at its
scavenge cap, and a loop of tiny JSON.parse calls allocates only inside
the parser's suppression window and at bounded inline-object births, so
nothing else arms the nursery safepoint for it. The young generation grew
to the guard's 48 MB floor before its first minor: small_record:parse
peaked at 80 MiB against Node's 59 with 0-9 permille of each collection
surviving, and object_1k:parse at 65 against 61.

The boundary now also collects when the young scavenge cap is due. A
minor lowers that quantity to the survivors, so unlike the absolute
in-use guard it cannot re-fire until the cap has been refilled.
… in native domains

The fast clone now redirects the accumulator into a promotable f64 alloca
and defers the counter's double storage to an i32 slot the Update lowering
advances alone, publishing both at a side-exit trampoline and on the
fall-through exit.
…ounter

IR census for the f64 accumulator redirect, the i32 counter, the side-exit
trampoline and the fall-through write-back; the carried-commit and side-exit
helpers count the trampoline spelling of an exit.
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 80fcd9cd-2be6-4722-8dac-34de0c9440c2

📥 Commits

Reviewing files that changed from the base of the PR and between 4945fc1 and 755bc44.

⛔ Files ignored due to path filters (22)
  • Cargo.lock is excluded by !**/*.lock
  • benchmarks/native_property_get/evidence/api-docs-drift.patch.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/clippy-logs.tar.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/fault-logs.tar.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/gc-ratchet-receipts.tar.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/gc-stress-receipts.tar.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/measurement-receipts.tar.gz is excluded by !**/*.gz
  • benchmarks/native_property_get/evidence/validation-logs.tar.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/api-docs-drift.patch.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-exec1.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-gc-ratchet.json.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-hoist.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-json.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-promises.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/baseline-workspace-warnings.log is excluded by !**/*.log
  • benchmarks/runtime_handle_scopes/evidence/fix-exec1.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/fix-gc-instrument-smoke.log is excluded by !**/*.log
  • benchmarks/runtime_handle_scopes/evidence/fix-gc-ratchet.json.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/fix-hoist.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/fix-json.folded.gz is excluded by !**/*.gz
  • benchmarks/runtime_handle_scopes/evidence/fix-lint-full.log is excluded by !**/*.log
  • benchmarks/runtime_handle_scopes/evidence/fix-promises.folded.gz is excluded by !**/*.gz
📒 Files selected for processing (118)
  • CLAUDE.md
  • Cargo.toml
  • benchmarks/native_property_get/README.md
  • benchmarks/native_property_get/REPORT.md
  • benchmarks/native_property_get/evidence/base-gate-comparison.json
  • benchmarks/native_property_get/evidence/baseline-build.json
  • benchmarks/native_property_get/evidence/baseline.json
  • benchmarks/native_property_get/evidence/candidate-build.json
  • benchmarks/native_property_get/evidence/candidate.json
  • benchmarks/native_property_get/evidence/clippy-diff.json
  • benchmarks/native_property_get/evidence/faults.json
  • benchmarks/native_property_get/evidence/gc-base-runtime.sha256
  • benchmarks/native_property_get/evidence/gc-ratchet-ab.json
  • benchmarks/native_property_get/evidence/gc-stress-base-http2.json
  • benchmarks/native_property_get/evidence/gc-stress.json
  • benchmarks/native_property_get/evidence/parity.stdout
  • benchmarks/native_property_get/evidence/runtime-source-sha256.json
  • benchmarks/native_property_get/evidence/validation.json
  • benchmarks/native_property_get/faults.py
  • benchmarks/native_property_get/iterator.ts
  • benchmarks/native_property_get/measure.py
  • benchmarks/native_property_get/reflect_own.ts
  • benchmarks/native_property_get/reflect_proto.ts
  • benchmarks/native_property_get/thenable.ts
  • benchmarks/native_property_get/tojson.ts
  • benchmarks/runtime_handle_scopes/README.md
  • benchmarks/runtime_handle_scopes/evidence/baseline-artifact-sha256.txt
  • benchmarks/runtime_handle_scopes/evidence/baseline-exec1.stat
  • benchmarks/runtime_handle_scopes/evidence/baseline-hoist.stat
  • benchmarks/runtime_handle_scopes/evidence/baseline-json.stat
  • benchmarks/runtime_handle_scopes/evidence/baseline-promises.stat
  • benchmarks/runtime_handle_scopes/evidence/fault-omit-last-root.txt
  • benchmarks/runtime_handle_scopes/evidence/fault-skip-cache-unpublish-direct.txt
  • benchmarks/runtime_handle_scopes/evidence/fault-skip-marking-barrier.txt
  • benchmarks/runtime_handle_scopes/evidence/fault-skip-throw-truncate.txt
  • benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-after-ext.txt
  • benchmarks/runtime_handle_scopes/evidence/fix-artifact-sha256-before-ext.txt
  • benchmarks/runtime_handle_scopes/evidence/fix-exec1.stat
  • benchmarks/runtime_handle_scopes/evidence/fix-gc-stress-coherent.json
  • benchmarks/runtime_handle_scopes/evidence/fix-hoist.stat
  • benchmarks/runtime_handle_scopes/evidence/fix-json.stat
  • benchmarks/runtime_handle_scopes/evidence/fix-promises.stat
  • benchmarks/runtime_handle_scopes/evidence/gc-ratchet-comparison.json
  • benchmarks/runtime_handle_scopes/evidence/gc-ratchet-pinned-failures.txt
  • benchmarks/runtime_handle_scopes/evidence/http2-baseline-comparison.json
  • benchmarks/runtime_handle_scopes/evidence/http2-netns-results.json
  • benchmarks/runtime_handle_scopes/exec1.ts
  • benchmarks/runtime_handle_scopes/fault-results.json
  • benchmarks/runtime_handle_scopes/hoist.ts
  • benchmarks/runtime_handle_scopes/instructions.json
  • benchmarks/runtime_handle_scopes/json.ts
  • benchmarks/runtime_handle_scopes/measure.py
  • benchmarks/runtime_handle_scopes/promises.ts
  • changelog.d/10245-prune-unused-reexports.md
  • changelog.d/10248-native-property-get.md
  • changelog.d/10249-json-clone-traversal-evidence.md
  • changelog.d/10250-class-prototype-symbol-props.md
  • changelog.d/10251-registry-address-index.md
  • changelog.d/10252-runtime-handle-scopes.md
  • changelog.d/10253-gc-due-check-fast-path.md
  • changelog.d/10254-tiny-parse-nursery-cap.md
  • changelog.d/10255-element-shape-native-domains.md
  • crates/perry-codegen/src/stmt/element_shape_fields_random_tests.rs
  • crates/perry-codegen/src/stmt/element_shape_loop.rs
  • crates/perry-codegen/src/stmt/element_shape_native.rs
  • crates/perry-codegen/src/stmt/element_shape_native_tests.rs
  • crates/perry-codegen/src/stmt/mod.rs
  • crates/perry-runtime/src/arena/block.rs
  • crates/perry-runtime/src/arena/from_space.rs
  • crates/perry-runtime/src/arena/mod.rs
  • crates/perry-runtime/src/arena/promote.rs
  • crates/perry-runtime/src/arena/quarantine.rs
  • crates/perry-runtime/src/arena/reset.rs
  • crates/perry-runtime/src/array/header.rs
  • crates/perry-runtime/src/array/mod.rs
  • crates/perry-runtime/src/array/refresh_traversal_evidence_tests.rs
  • crates/perry-runtime/src/gc/heap_generation.rs
  • crates/perry-runtime/src/gc/mod.rs
  • crates/perry-runtime/src/gc/policy.rs
  • crates/perry-runtime/src/gc/roots.rs
  • crates/perry-runtime/src/gc/roots/runtime_handles.rs
  • crates/perry-runtime/src/gc/roots/runtime_handles/stack.rs
  • crates/perry-runtime/src/gc/roots/runtime_handles/stack_os_tls.rs
  • crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs
  • crates/perry-runtime/src/gc/tenuring.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots.rs
  • crates/perry-runtime/src/gc/tests/runtime_roots/handle_stack.rs
  • crates/perry-runtime/src/gc/tests/tiny_parse_pressure.rs
  • crates/perry-runtime/src/gc/tests/young_leaf_route.rs
  • crates/perry-runtime/src/json/stringify.rs
  • crates/perry-runtime/src/json/traversal_feedback.rs
  • crates/perry-runtime/src/lib.rs
  • crates/perry-runtime/src/object/field_get_set/get_field_by_name.rs
  • crates/perry-runtime/src/object/field_get_set/has_property.rs
  • crates/perry-runtime/src/object/mod.rs
  • crates/perry-runtime/src/object/native_get.rs
  • crates/perry-runtime/src/object/native_get/tests.rs
  • crates/perry-runtime/src/promise/microtasks.rs
  • crates/perry-runtime/src/proxy/reflect.rs
  • crates/perry-runtime/src/regex/perex_runtime.rs
  • crates/perry-runtime/src/registry_latch.rs
  • crates/perry-runtime/src/symbol.rs
  • crates/perry-runtime/src/symbol/get.rs
  • crates/perry-runtime/src/tls_hot.rs
  • crates/perry-runtime/src/value/dynamic_object.rs
  • crates/perry/src/commands/compile/collect_modules.rs
  • crates/perry/src/commands/compile/collect_modules/reexport_prune.rs
  • crates/perry/src/commands/compile/collect_modules/reexport_prune/forwarding.rs
  • crates/perry/src/commands/compile/collect_modules/reexport_prune/purity.rs
  • crates/perry/src/commands/compile/collect_modules/reexport_prune/scan.rs
  • crates/perry/src/commands/compile/collect_modules/reexport_prune/side_effects.rs
  • crates/perry/tests/class_prototype_symbol_props.rs
  • crates/perry/tests/source_graph_export_regressions/issue_10180.rs
  • crates/perry/tests/source_graph_export_regressions/issue_10180/purity.rs
  • docs/src/cli/flags.md
  • scripts/gc_runtime_root_holders.json
  • test-files/test_gap_json_record_loop_clone.ts
  • test-files/test_gap_native_property_get.ts

📝 Walkthrough

Walkthrough

The pull request adds native property access, runtime handle-stack storage, GC and compiler fast paths, inferred re-export purity, declared-prototype symbol lookup, registry address indexing, benchmark evidence, documentation, tests, and a project version update.

Changes

Native property Get

Layer / File(s) Summary
Native Get implementation and callers
crates/perry-runtime/src/object/..., crates/perry-runtime/src/proxy/..., crates/perry-runtime/src/json/...
Adds guarded native data-property lookup and wires it into reflection, named-field access, dynamic objects, and JSON.stringify.
Native Get validation and benchmarks
test-files/test_gap_native_property_get.ts, crates/perry-runtime/src/object/native_get/tests.rs, benchmarks/native_property_get/*
Adds parity tests, fallback tests, mutation tests, benchmark probes, profiling scripts, and recorded evidence.

Runtime handle scopes

Layer / File(s) Summary
Handle stack storage and access
crates/perry-runtime/src/gc/roots/..., crates/perry-runtime/src/tls_hot.rs
Replaces callback-based thread-local vector access with direct stack references on native targets and OS-TLS-backed access on Android and HarmonyOS.
Handle stack and GC-root tests
crates/perry-runtime/src/gc/tests/runtime_roots/..., crates/perry-runtime/src/gc/roots/runtime_handles/tests.rs
Adds coverage for growth, relocation, truncation, FFI indices, marking, scanning, and teardown.
Handle-scope benchmark evidence
benchmarks/runtime_handle_scopes/*
Adds workloads, measurement tooling, reports, fault results, and performance evidence.

Element-shape native domains

Layer / File(s) Summary
Native loop-domain lowering and validation
crates/perry-codegen/src/stmt/element_shape_*, test-files/test_gap_json_record_loop_clone.ts
Redirects eligible accumulators to f64 storage and counters to i32 storage. Side exits publish both values before the slow clone runs. Tests cover IR shape, numeric behavior, and side exits.

GC and traversal fast paths

Layer / File(s) Summary
Cached occupancy and GC policy
crates/perry-runtime/src/arena/*, crates/perry-runtime/src/gc/*
Adds cached young-generation occupancy, invalidation through arena moves, repeatable trigger memoization, deferred debt snapshots, and nursery-cap scheduling at parse boundaries.
Traversal feedback and validation
crates/perry-runtime/src/array/*, crates/perry-runtime/src/json/traversal_feedback.rs, scripts/gc_runtime_root_holders.json
Records first lazy-array materialization as traversal evidence and updates tests and GC-root inventory data.

Re-export purity analysis

Layer / File(s) Summary
Purity contracts and graph analysis
crates/perry/src/commands/compile/collect_modules/..., docs/src/cli/flags.md
Combines side-effects contracts with AST purity inference and permits more forwarding forms when dependency graphs are pure and acyclic.
Purity and pruning regressions
crates/perry/tests/source_graph_export_regressions/*, changelog.d/10245-prune-unused-reexports.md
Adds regression coverage for contracts, cycles, symlinks, dynamic imports, evaluation order, and cached rebuilds.

Symbol lookup and registry indexing

Layer / File(s) Summary
Declared prototype symbols
crates/perry-runtime/src/symbol/*, crates/perry/tests/class_prototype_symbol_props.rs
Resolves symbol properties added to declared class prototypes and checks property presence without invoking getters.
Live registry address index
crates/perry-runtime/src/registry_latch.rs, changelog.d/10251-registry-address-index.md
Makes Bloom filter width configurable and adds a live-address index that refreshes after admissions and retirements.

Release metadata

Layer / File(s) Summary
Project version update
CLAUDE.md, Cargo.toml
Updates the project version from 0.5.1565 to 0.5.1566.

Estimated code review effort: 5 (Critical) | ~90 minutes

Change: Bug fix · Severity of issue fixed: Medium

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch train188r

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant