On Sun, 17 Apr 2022 16:17:30 GMT, liach <[email protected]> wrote:
> Convert dynamic proxies to hidden classes. Modifies the serialization of
> proxies (requires change in "Java Object Serialization Specification"). Makes
> the proxies hidden in stack traces. Removes duplicate logic in proxy building.
>
> The main compatibility changes and their rationales are:
> 1. Modification to the serialization specification: In the "An instance of
> the class is allocated... The contents restored appropriately" section, I
> propose explicitly state that handling of proxies are unspecified as to allow
> implementation freedom, though I've seen deliberate attempts for proxies to
> implement interfaces with `readResolve` in order to control their
> serialization behavior.
> - This is for the existing generated constructor accessor is
> bytecode-based, which cannot refer to hidden classes.
> - An alternative is to preserve the behavior, where the serialization
> constructor calls `invokespecial` on the closest serializable superclass'
> no-arg constructor, like in #1830 by @DasBrain.
> - My rationale against preservation is such super calls are unsafe and
> should be discouraged in the long term. Calling the existing constructor with
> a dummy argument, as in my implementation, would be more safe.
> 2. The disappearance of proxies in stack traces.
> - Same behavior exists in lambda expressions: For instance, in
> `((Runnable) () -> { throw new Error(); }).run();`, the `run` method,
> implemented by the lambda, will not appear in the stack trace, and isn't too
> problematic.
>
> A summary of the rest of the changes:
> 1. Merged the two passes of determining module and package of the proxy into
> one. This reduced a lot of code and allowed anchor class (for hidden class
> creation) selection be done together as well.
> 2. Exposed internal API for obtaining a full-privileged lookup to the rest of
> `java.base`. This API is intended for implementation of legacy (pre
> `MethodHandles.Lookup`) caller sensitive public APIs so they don't need more
> complex tricks to obtain proper permissions as lookups.
> 3. Implements [8229959](https://bugs.openjdk.java.net/browse/JDK-8229959):
> passes methods computed by proxy generator as class data to the hidden proxy
> class to reduce generated proxy class size and improve performance.
>
> In addition, since this change is somewhat large, should we keep the old
> proxy generator as well and have it toggled through a command-line flag (like
> the old v49 proxy generator or the old reflection implementation)?
>
> Please feel free to comment or review. This change definitely requires a CSR,
> but I have yet to determine what specifications should be changed.
About deserializing Proxies, this is currently done in 3 steps:
1. Finding the class
2. Allocating a new instance and invoke the constructor of the **first
non-serializable superclass**
3. Setting the fields of the instance
Only step 2 is problematic here:
1. For a proxy, the class is serialized using a TC_PROXYCLASSDESC. when
deserialized it invokes `Proxy.getProxyClass`
(https://github.com/openjdk/jdk/blob/13fb1eed52f1a9152242119969a9d4a0c0627513/src/java.base/share/classes/java/io/ObjectInputStream.java#L891).
For this step, it doesn't matter if `Proxy.getProxyClass` returns a normal
class or a hidden class.
2. Allocating and calling the constructor:
This part is currently implemented by spinning a class. The generated
bytecode looks more or less like this:
anew ProxyClass
invokespecial Object.<init>()
The big lie here is that methods and constructors are different - but
constructors are just methods, and the verifier makes sure that a constructor
is called only once, exactly once, and that uninitialized instances don't
escape.
This doesn't work for hidden classes - as the hidden class can not be named
in an other class.
But MethodHandles can do this. Here is one of my old prototypes, based on a
prototype for implementing reflection using MethodHandles from Mandy Chung:
https://github.com/dasbrain/jdk/compare/ae45c5de7fc635df4dc86b764405158c245d2765...fbb0a63436f696a85e7039c0e109c379dfa1edce
3. Setting the fields uses deep reflection.
But the hidden class themself doesn't have any fields - just it's
superclass `java.lang.reflect.Proxy.h`.
This is not a problem if the class can be instantiated at all (see step 2).
-------------
PR: https://git.openjdk.java.net/jdk/pull/8278