On Fri, 7 Jul 2023 06:45:46 GMT, Chen Liang <[email protected]> wrote:
>> As John Rose has pointed out in this issue, the current j.l.r.Proxy based
>> implementation of MethodHandleProxies.asInterface has a few issues:
>> 1. Exposes too much information via Proxy supertype (and WrapperInstance
>> interface)
>> 2. Does not allow future expansion to support SAM[^1] abstract classes
>> 3. Slow (in fact, very slow)
>>
>> This patch addresses all 3 problems:
>> 1. It updates the WrapperInstance methods to take an `Empty` to avoid method
>> clashes
>> 2. This patch obtains already generated classes from a ClassValue by the
>> requested interface type; the ClassValue can later be updated to compute
>> implementation generation for abstract classes as well.
>> 3. This patch's faster than old implementation in general.
>>
>> Benchmark for revision 17:
>>
>> Benchmark Mode Cnt
>> Score Error Units
>> MethodHandleProxiesAsIFInstance.baselineAllocCompute avgt 15
>> 1.503 ± 0.021 ns/op
>> MethodHandleProxiesAsIFInstance.baselineCompute avgt 15
>> 0.269 ± 0.005 ns/op
>> MethodHandleProxiesAsIFInstance.testCall avgt 15
>> 1.806 ± 0.018 ns/op
>> MethodHandleProxiesAsIFInstance.testCreate avgt 15
>> 17.332 ± 0.210 ns/op
>> MethodHandleProxiesAsIFInstance.testCreateCall avgt 15
>> 19.296 ± 1.371 ns/op
>> MethodHandleProxiesAsIFInstanceCall.callDoable avgt 5
>> 0.419 ± 0.004 ns/op
>> MethodHandleProxiesAsIFInstanceCall.callHandle avgt 5
>> 0.421 ± 0.004 ns/op
>> MethodHandleProxiesAsIFInstanceCall.callInterfaceInstance avgt 5
>> 1.731 ± 0.018 ns/op
>> MethodHandleProxiesAsIFInstanceCall.callLambda avgt 5
>> 0.418 ± 0.003 ns/op
>> MethodHandleProxiesAsIFInstanceCall.constantDoable avgt 5
>> 0.263 ± 0.003 ns/op
>> MethodHandleProxiesAsIFInstanceCall.constantHandle avgt 5
>> 0.262 ± 0.002 ns/op
>> MethodHandleProxiesAsIFInstanceCall.constantInterfaceInstance avgt 5
>> 0.262 ± 0.002 ns/op
>> MethodHandleProxiesAsIFInstanceCall.constantLambda avgt 5
>> 0.267 ± 0.019 ns/op
>> MethodHandleProxiesAsIFInstanceCall.direct avgt 5
>> 0.266 ± 0.013 ns/op
>> MethodHandleProxiesAsIFInstanceCreate.createCallInterfaceInstance avgt 5
>> 18.057 ± 0.182 ...
>
> Chen Liang has refreshed the contents of this pull request, and previous
> commits have been removed. The incremental views will show differences
> compared to the previous content of the PR. The pull request contains one new
> commit since the last revision:
>
> Fix broken null behaviors
Looks good in general. Can you please add a test to verify that the hidden
class is unloaded and then call `asInterfaceInstace` again on the same
interface to spin a new class.
src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java line 185:
> 183: *
> 184: * The shared-class implementation is also closer in behavior to the
> original
> 185: * proxy-backed implementation. We might add another API for
> super-customized instances.
The implementor note mainly specifies that there is no guarantee on a stable
mapping of the SAM interface to the implementation class.
I see this new note you added is to document the current implementation and
alternatives. I would move this closer to the code (see below). I made some
suggested edits. I avoid using the term "super-customized" since it's not
clear what it means unless reading JBS comments. I also avoid referring to
Project Leyden but instead describes it with some reasonable details say "more
friendly for pre-generation....".
src/java.base/share/classes/java/lang/invoke/MethodHandleProxies.java line 209:
> 207: mh = target;
> 208: }
> 209:
What about this comment rephrased from the new note you added:
// Define one hidden class for each interface. Create an instance of
// the hidden class for a given target method handle which will be
// accessed via getfield. Multiple instances may be created for a
// hidden class. This approach allows the generated hidden classes
// more shareable.
//
// An alternative approach is to define one hidden class with the
// target method handle as class data and the target method handle
// is loaded via ldc/condy. If more than one target method handles
// are used, the extra classes will pollute the same type profiles.
// In addition, hidden classes without class data is more friendly
// for pre-generation (shifting the dynamic class generation from
// runtime to an earlier phrase).
//
test/jdk/java/lang/invoke/MethodHandleProxies/BasicTest.java line 183:
> 181:
> 182: @Test
> 183: public void testModule() throws Throwable {
This test case belongs more to `ProxyForMethodHandleTest` test, which verifies
if it's a dynamic module. We can move the package exports tests to
`ProxyForMethodHandleTest`.
test/jdk/java/lang/invoke/MethodHandleProxies/ProxyForMethodHandleTest.java
line 61:
> 59:
> 60: public static void assertDynamicModule(Module m, ClassLoader ld,
> Class<?> proxyClass) {
> 61: if (!m.isNamed() || !m.getName().startsWith("jdk.MHProxy")) {
This can also check if the package of the proxy class is not unconditionally
exported.
test/jdk/java/lang/invoke/MethodHandleProxies/WithSecurityManagerTest.java line
54:
> 52: } catch (Throwable ex) {
> 53: throw new AssertionError("Test failed for " + cl, ex);
> 54: }
Nit: formatting - try block inside the for-loop
Suggestion:
for (Class<?> cl : List.of(Runnable.class, Client.class,
NestedInterface.class)) {
try {
Object o = MethodHandleProxies.asInterfaceInstance(cl,
originalMh);
testWrapperInstanceTarget(o, originalMh);
testWrapperInstanceType(o, cl);
} catch (Throwable ex) {
throw new AssertionError("Test failed for " + cl, ex);
}
}
-------------
PR Review: https://git.openjdk.org/jdk/pull/13197#pullrequestreview-1519328472
PR Review Comment: https://git.openjdk.org/jdk/pull/13197#discussion_r1258671075
PR Review Comment: https://git.openjdk.org/jdk/pull/13197#discussion_r1258671964
PR Review Comment: https://git.openjdk.org/jdk/pull/13197#discussion_r1256560775
PR Review Comment: https://git.openjdk.org/jdk/pull/13197#discussion_r1256562501
PR Review Comment: https://git.openjdk.org/jdk/pull/13197#discussion_r1256158328