Skip to content

feat: generate record deconstructors alongside instantiators#299

Merged
zantvoort merged 1 commit into
mainfrom
feat/record-deconstructors
Jul 20, 2026
Merged

feat: generate record deconstructors alongside instantiators#299
zantvoort merged 1 commit into
mainfrom
feat/record-deconstructors

Conversation

@zantvoort

Copy link
Copy Markdown
Collaborator

Closes #295

What

The metamodel generators emit an Instantiator per record type, so record construction runs as generated code — but reading a record's components back out still went through reflective accessors. This completes the pair:

  • Instantiator#deconstruct(T) — returns the canonical constructor arguments in declaration order as a freshly allocated array the caller may adjust and pass back to instantiate. The default returns null ("no generated deconstructor"), so instantiators generated before this change and types without generated support keep working unchanged through the reflective fallback.
  • Both generators emit it: the Java annotation processor reads the record accessors, the KSP processor reads the data-class properties (backtick-quoted, so any property name works).
  • Write-set rebuilds use it: key propagation and generated-key assignment (withComponent) now run the full deconstruct → adjust → instantiate round trip as generated code.

Why

  • Native images: component reads as generated code are closed-world analyzable — no accessor reachability metadata to register, and no method-handle machinery in the write path.
  • Cold start: generated reads start at full speed; the reflective path costs several hundred µs per write-set operation until the JIT catches up, which matters for short-lived processes.
  • Warmed-JIT throughput is unchanged (measured); this is stated in Generate record deconstructors alongside instantiators for reflection-free rebuilds #295 so it is not mistaken for a performance item.

Testing

  • storm-core: 2307 green after a clean rebuild — 45 test records regenerate with deconstructors and the write-set integration tests exercise the generated round trip.
  • storm-h2: 158 green (write-set suite included).
  • KSP verified end to end: the Kotlin benchmark project regenerates its instantiators with deconstructors, compiles, and runs the write-set graph insert with its key-count guards passing.
  • Full reactor test-compile.

The metamodel generators emit an Instantiator per record type so
construction runs as generated code, but reading a record's components back
out still went through reflective accessors. Both generators now also emit a
deconstructor that reads the components directly, and write-set rebuilds
(key propagation and generated-key assignment) run the full
deconstruct-adjust-instantiate round trip as generated code.

Instantiator gains a deconstruct method whose default returns null,
signalling that no generated deconstructor is available; callers fall back
to reflective component access, so instantiators generated before this
change and types without generated support keep working unchanged.

Component reads as generated code need no accessor registration for native
images and start at full speed in short-lived processes; warmed-JIT
throughput is unchanged.

Closes #295
@zantvoort zantvoort added this to the 1.13.0 milestone Jul 20, 2026
@zantvoort
zantvoort merged commit 14b57c2 into main Jul 20, 2026
7 checks passed
@zantvoort
zantvoort deleted the feat/record-deconstructors branch July 20, 2026 19:47
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.

Generate record deconstructors alongside instantiators for reflection-free rebuilds

1 participant