Honor a JavaType's own RpcCodec instead of flattening it - #8743
Open
knutwannheden wants to merge 2 commits into
Open
Honor a JavaType's own RpcCodec instead of flattening it#8743knutwannheden wants to merge 2 commits into
knutwannheden wants to merge 2 commits into
Conversation
…ss subclass Verifying the RPC codec registries are open to a host (e.g. a moderne-cli addon) registering codecs for its own type-table proxy value_type surfaced two gaps on the Python *send* side. The receive side was already registry-driven (`_new_obj(value_type)` -> registered factory, `_get_codec` by class name), and needs no change; but the send side flattened any custom `JavaType.Class` subclass to the built-in `JavaType$Class` and never consulted the registered send codec, so a host could register codecs that never took effect. Two guarded hooks close that, both gated on "a send codec is registered for this object" — the Python analogue of the JVM's "instance is its own RpcCodec" signal. Built-in JavaTypes have no registered send codec, so both hooks are inert for them (verified: no built-in JavaType subclass has a registered send codec) and their serialization is unchanged: - RpcSendQueue._get_value_type: when the object has a registered send codec, honor its registered value_type discriminator instead of flattening it to a built-in JavaType name. - PythonRpcSender._visit_type: when a send codec is registered for the exact type, defer to it instead of the hardcoded isinstance serialization. Adds a pure-Python round-trip test proving a custom JavaType.Class subclass with registered codecs survives send->receive with its value_type preserved on the wire and both codecs invoked. Note: the rewrite-core (JVM) send side has the symmetric flattening (RpcSendQueue value-type computation collapses foreign-package JavaType subclasses to their superclass name); that cross-language change is deferred to a follow-up. The JVM *receive* side is already open (Class.forName + objenesis, RpcCodec.forInstance via DynamicDispatchRpcCodec ServiceLoader or the instance implementing RpcCodec). (cherry picked from commit 67762ce)
The previous commit opened the Python send side to a host-registered codec for its own JavaType.Class subclass and deferred the symmetric JVM change. This is that change, so a body-less type-table proxy survives the Java -> Python direction too. - RpcSendQueue: a JavaType subclass that implements RpcCodec keeps its real class name. Both "flatten a foreign-package subclass to its superclass" branches skip it, so the discriminator the remote has a factory registered for reaches the wire intact. - JavaTypeSender/JavaTypeReceiver: visitClass delegates to the instance's own codec rather than the structural ten-field walk, which would resolve a proxy's body — the thing a proxy exists to avoid. - JavaTypeProxyRpcSendTest covers the round trip. On the Python side, JavaType.Class gains an __init__ and read accessors so a body-less shell is fully formed; the list-valued accessors return [] for an unpopulated field, matching the Java getters, so iterating a shell never raises. Unrelated to the boundary work: Tree, Marker and Markers return None from `.id` when `_id` is unset, since the RPC receiver reads `.id` on an all-None placeholder before filling it in. (cherry picked from commit 94d9e87)
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.
A
JavaTypethat carries its ownRpcCodecnever reaches the wire as itself:Two branches in
RpcSendQueuereport a superclass name instead of the real class — one for aJavaTypesubclass declared outsideorg.openrewrite.java.tree, one for any tree subclass generated outsideorg.openrewrite. Both exist to keep a synthetic proxy the remote has no factory for off the wire, but they fire unconditionally, so they also erase the name of a subclass that does declare a wire form.JavaTypeSender.visitClassthen walks those ten fields regardless of the instance, among themgetSupertype(),getInterfaces(),getMembers()andgetMethods(). For a body-less type that resolves lazily from a type table, that is exactly the resolution it exists to avoid.What this does
Implementing
RpcCodecis the signal that a type describes its own wire form, so that is what the three changed branches key on:RpcSendQueue— both flattening branches skip a type that is its own codec, so the real class name survives as the discriminatorJavaTypeSender.visitClass— delegates to the instance'srpcSendJavaTypeReceiver.visitClass— delegates to the instance'srpcReceiveNo built-in
JavaTypeimplementsRpcCodec— the hierarchy does not reference it at all — so all three are inert for them and their serialization is unchanged. Only the send half needed opening; receiving was already registry-driven throughClass.forName, objenesis andRpcCodec.forInstance.Python gets the symmetric treatment in
RpcSendQueue._get_value_typeandPythonRpcSender._visit_type, gated on a registered send codec — the registry being Python's equivalent of "the instance is its own codec". Two supporting changes ride along:JavaType.Classgains an__init__and read accessors so a body-less shell is fully formed, the list-valued ones returning[]to match the Java gettersTree/Marker/MarkersreturnNonefrom.idfor an unset_id, because the RPC receiver reads.idon an all-None placeholder before filling it inVerification
JavaTypeProxyRpcSendTestuses aStubProxywhose body getters throw, so a structural send fails loudly rather than over-resolving silently. One test pins the discriminator and the exact payload; the other that the round trip rebuilds aStubProxyrather than a desynced plainClass.tests/python/test_rpc_type_proxy.pycovers the Python side including the negatives — a plainJavaType.Classwith no registered codec still flattens and still takes the hardcoded path.Why now
Extracted from the boundary-aware type-attribution branch, where these were the only generic RPC plumbing among five commits and carried no boundary concept, so they can be judged on their own and the boundary PR stays boundary work.