Typed-accessor invokers, built once per field with LambdaMetafactory.
protobuf-java's reflection API pays a FieldAccessorTable lookup on every setField/getField. When the prototype is a generated class, the typed accessors (setFooBar, getFooBar, hasFooBar) are right there — and a metafactory-generated BiFunction/Function calling one runs at direct-interop speed (measured ~3.5 ns/op, vs ~µs through reflection paths), including the primitive boxing bridge the instantiated method type declares.
Everything here is best-effort by construction, in the same spirit as the Java-class hint: derive protoc's accessor name, let findVirtual verify it exists with the expected signature, and return nil on ANY failure — including LambdaMetafactory itself being unavailable, which is what happens under native-image, where the codec silently keeps its reflection path. A wrong derivation is never wrong bytes, only a missed optimisation.
Typed-accessor invokers, built once per field with LambdaMetafactory. protobuf-java's reflection API pays a FieldAccessorTable lookup on every setField/getField. When the prototype is a generated class, the typed accessors (setFooBar, getFooBar, hasFooBar) are right there — and a metafactory-generated BiFunction/Function calling one runs at direct-interop speed (measured ~3.5 ns/op, vs ~µs through reflection paths), including the primitive boxing bridge the instantiated method type declares. Everything here is best-effort by construction, in the same spirit as the Java-class hint: derive protoc's accessor name, let findVirtual verify it exists with the expected signature, and return nil on ANY failure — including LambdaMetafactory itself being unavailable, which is what happens under native-image, where the codec silently keeps its reflection path. A wrong derivation is never wrong bytes, only a missed optimisation.
The one naming rule, shared with the emitter.
protoc-gen-clojure kebab-cases proto field names into record fields and map
keys with exactly this algorithm (its field-key-symbol). The runtime's
generic nested-map path must produce the same keys byte for byte, or records
built by generated code and maps built by the runtime stop being
interchangeable. Any change here is a wire-compatibility break with every
generated file in existence — don't.
Kebab-casing is lossy (STYLE_LEGACY files can mix conventions), which is why
the emitted rt/field lookups carry the exact proto name and this fn is used
only for the Clojure-side keys.
The one naming rule, shared with the emitter. protoc-gen-clojure kebab-cases proto field names into record fields and map keys with exactly this algorithm (its `field-key-symbol`). The runtime's generic nested-map path must produce the same keys byte for byte, or records built by generated code and maps built by the runtime stop being interchangeable. Any change here is a wire-compatibility break with every generated file in existence — don't. Kebab-casing is lossy (STYLE_LEGACY files can mix conventions), which is why the emitted `rt/field` lookups carry the exact proto name and this fn is used only for the Clojure-side keys.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |