Skip to content

Commit 89019f3

Browse files
committed
Test compilation proxies.
1 parent 409216a commit 89019f3

23 files changed

+224
-79
lines changed
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.hotspot.replaycomp.test;
26+
27+
import static org.junit.Assert.assertArrayEquals;
28+
import static org.junit.Assert.assertEquals;
29+
import static org.junit.Assert.assertSame;
30+
import static org.junit.Assert.assertTrue;
31+
32+
import java.lang.reflect.InvocationTargetException;
33+
import java.lang.reflect.Method;
34+
import java.lang.reflect.Modifier;
35+
import java.util.ArrayList;
36+
import java.util.Arrays;
37+
import java.util.List;
38+
import java.util.Set;
39+
40+
import org.junit.Assert;
41+
import org.junit.Test;
42+
43+
import jdk.graal.compiler.hotspot.replaycomp.CompilerInterfaceDeclarations;
44+
import jdk.graal.compiler.hotspot.replaycomp.proxy.CompilationProxy;
45+
import jdk.graal.compiler.hotspot.replaycomp.proxy.CompilationProxyBase;
46+
import jdk.graal.compiler.hotspot.replaycomp.proxy.HotSpotConstantProxy;
47+
import jdk.graal.compiler.hotspot.replaycomp.proxy.HotSpotResolvedJavaTypeProxy;
48+
import jdk.graal.compiler.hotspot.replaycomp.proxy.HotSpotVMConfigAccessProxy;
49+
import jdk.graal.compiler.util.CollectionsUtil;
50+
51+
/**
52+
* Checks that the implementations of {@link CompilationProxy} follow the expected schema. The check
53+
* is performed by reflectively invoking non-private instance methods of every proxy and verifying
54+
* that the implementation forwards the calls to the handler with the expected arguments.
55+
*/
56+
public class CompilationProxyTest {
57+
/**
58+
* Methods whose implementation is not checked.
59+
*/
60+
private static final Set<Method> skippedMethods;
61+
62+
static {
63+
try {
64+
skippedMethods = CollectionsUtil.setOf(
65+
HotSpotVMConfigAccessProxy.class.getDeclaredMethod("getStore"),
66+
CompilationProxyBase.class.getDeclaredMethod("handle", CompilationProxy.SymbolicMethod.class, CompilationProxy.InvokableMethod.class, Object[].class),
67+
HotSpotResolvedJavaTypeProxy.class.getDeclaredMethod("handle", CompilationProxy.SymbolicMethod.class, CompilationProxy.InvokableMethod.class, Object[].class));
68+
} catch (NoSuchMethodException e) {
69+
throw new RuntimeException(e);
70+
}
71+
}
72+
73+
@Test
74+
public void checkProxyMethods() throws IllegalAccessException, InvocationTargetException {
75+
CompilerInterfaceDeclarations declarations = CompilerInterfaceDeclarations.build();
76+
for (CompilerInterfaceDeclarations.Registration registration : declarations.getRegistrations()) {
77+
Class<?> clazz = registration.clazz();
78+
CompilationProxy sampleProxy = CompilationProxy.newProxyInstance(clazz, (_, _, _, _) -> null);
79+
for (Method method : getProxyMethods(sampleProxy)) {
80+
if (method.isSynthetic() || Modifier.isStatic(method.getModifiers()) || Modifier.isPrivate(method.getModifiers()) || skippedMethods.contains(method)) {
81+
continue;
82+
}
83+
84+
Object[] args = createValues(method.getParameterTypes());
85+
86+
if (Modifier.isProtected(method.getModifiers())) {
87+
method.setAccessible(true);
88+
try {
89+
method.invoke(sampleProxy, args);
90+
} catch (InvocationTargetException e) {
91+
assertTrue("protected method should throw UnsupportedOperationException", e.getTargetException() instanceof UnsupportedOperationException);
92+
continue;
93+
}
94+
Assert.fail("expected the protected method to throw");
95+
}
96+
97+
Object returnValue = createValue(method.getReturnType());
98+
99+
CompilationProxy[] instance = new CompilationProxy[2];
100+
boolean[] invoked = new boolean[2];
101+
102+
// We will directly invoke a method on the first instance.
103+
instance[0] = CompilationProxy.newProxyInstance(clazz, (proxy, symbolicMethod, invokableMethod, actualArgs) -> {
104+
checkHandlerInvocation(method, proxy, symbolicMethod, actualArgs, instance[0], args);
105+
// Invoke the second handler using the invokable method.
106+
invokableMethod.invoke(instance[1], actualArgs);
107+
invoked[0] = true;
108+
return returnValue;
109+
});
110+
111+
/*
112+
* The second proxy instance is used to test the InvokableMethod passed to the first
113+
* invocation handler.
114+
*/
115+
instance[1] = CompilationProxy.newProxyInstance(clazz, (proxy, symbolicMethod, _, actualArgs) -> {
116+
checkHandlerInvocation(method, proxy, symbolicMethod, actualArgs, instance[1], args);
117+
invoked[1] = true;
118+
return returnValue;
119+
});
120+
121+
// Invoke the first handler, which should invoke the second handler.
122+
Object actualReturnValue = method.invoke(instance[0], args);
123+
124+
assertEquals("the proxy implementation should forward the return value", returnValue, actualReturnValue);
125+
assertTrue("the first invocation handler should have been invoked", invoked[0]);
126+
assertTrue("the second invocation handler should have been invoked", invoked[1]);
127+
}
128+
}
129+
}
130+
131+
/**
132+
* Gets the proxy methods implemented by the given proxy instance.
133+
*/
134+
private static List<Method> getProxyMethods(CompilationProxy proxyInstance) {
135+
List<Method> results = new ArrayList<>(Arrays.asList(proxyInstance.getClass().getDeclaredMethods()));
136+
if (proxyInstance instanceof CompilationProxyBase.CompilationProxyAnnotatedBase) {
137+
results.addAll(Arrays.asList(CompilationProxyBase.CompilationProxyAnnotatedBase.class.getDeclaredMethods()));
138+
}
139+
if (proxyInstance instanceof CompilationProxyBase) {
140+
results.addAll(Arrays.asList(CompilationProxyBase.class.getDeclaredMethods()));
141+
}
142+
if (proxyInstance instanceof HotSpotConstantProxy) {
143+
results.addAll(Arrays.asList(HotSpotConstantProxy.class.getDeclaredMethods()));
144+
}
145+
return results;
146+
}
147+
148+
/**
149+
* Creates an array of values of the given types.
150+
*/
151+
private static Object[] createValues(Class<?>[] types) {
152+
if (types.length == 0) {
153+
return null;
154+
}
155+
Object[] values = new Object[types.length];
156+
for (int i = 0; i < values.length; i++) {
157+
values[i] = createValue(types[i]);
158+
}
159+
return values;
160+
}
161+
162+
/**
163+
* Creates a value of the given type.
164+
*/
165+
private static Object createValue(Class<?> type) {
166+
if (type == int.class) {
167+
return 189349;
168+
} else if (type == boolean.class) {
169+
return true;
170+
} else if (type == byte.class) {
171+
return (byte) 177;
172+
} else if (type == char.class) {
173+
return 'a';
174+
} else if (type == short.class) {
175+
return (short) -1235;
176+
} else if (type == long.class) {
177+
return 118239233L;
178+
} else if (type == float.class) {
179+
return -234.55923f;
180+
} else if (type == double.class) {
181+
return 1.28349932d;
182+
} else if (type == String.class) {
183+
return "test";
184+
} else {
185+
return null;
186+
}
187+
}
188+
189+
/**
190+
* Checks that the arguments passed to an invocation handler match the expected arguments.
191+
*/
192+
private static void checkHandlerInvocation(Method method, Object proxy, CompilationProxy.SymbolicMethod symbolicMethod, Object[] actualArgs, CompilationProxy instance, Object[] args) {
193+
assertSame("the proxy implementation should pass this object as the proxy", instance, proxy);
194+
assertEquals("the symbolic method should match the invoked method", asSymbolicMethod(method), symbolicMethod);
195+
assertArrayEquals("the arguments passed to the handler should match the invocation arguments", args, actualArgs);
196+
}
197+
198+
/**
199+
* Returns a symbolic method for the given method.
200+
*/
201+
private static CompilationProxy.SymbolicMethod asSymbolicMethod(Method method) {
202+
return new CompilationProxy.SymbolicMethod(method.getName(), method.getParameterTypes());
203+
}
204+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/CompilationProxy.java

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@
5353
import jdk.vm.ci.meta.Signature;
5454
import jdk.vm.ci.meta.SpeculationLog;
5555

56-
//JaCoCo Exclude
57-
5856
/**
5957
* A proxy object for a compiler-interface class.
6058
* <p>
@@ -138,46 +136,31 @@ private static String[] toArray(String methodName, Class<?>[] params) {
138136
}
139137

140138
/**
141-
* Creates a new symbolic method instance from a list receiver classes, method name, and
142-
* parameter types. At least one of the receiver classes must declare a method with the
143-
* given signature.
139+
* Creates a new symbolic method from a receiver class, method name, and parameter types.
140+
* The receiver class must declare a method with the given signature.
144141
*
145-
* @param receiverClasses the receiver classes
142+
* @param receiverClass the receiver class
146143
* @param methodName the method name
147144
* @param params the parameter types
148145
*/
149-
public SymbolicMethod(Class<?>[] receiverClasses, String methodName, Class<?>... params) {
146+
public SymbolicMethod(Class<?> receiverClass, String methodName, Class<?>... params) {
150147
this(methodName, params);
151148
if (!LibGraalSupport.inLibGraalRuntime()) {
152149
// Omit the check in the image to avoid increasing image size.
153-
for (Class<?> receiverClass : receiverClasses) {
154-
try {
155-
receiverClass.getDeclaredMethod(methodName, params);
156-
return;
157-
} catch (NoSuchMethodException ignored) {
158-
}
159-
try {
160-
receiverClass.getMethod(methodName, params);
161-
return;
162-
} catch (NoSuchMethodException ignored) {
163-
}
150+
try {
151+
receiverClass.getDeclaredMethod(methodName, params);
152+
return;
153+
} catch (NoSuchMethodException ignored) {
154+
}
155+
try {
156+
receiverClass.getMethod(methodName, params);
157+
return;
158+
} catch (NoSuchMethodException ignored) {
164159
}
165160
throw new GraalError("Method " + methodName + " not found");
166161
}
167162
}
168163

169-
/**
170-
* Creates a new symbolic method from a receiver class, method name, and parameter types.
171-
* The receiver class must declare a method with the given signature.
172-
*
173-
* @param receiverClass the receiver class
174-
* @param methodName the method name
175-
* @param params the parameter types
176-
*/
177-
public SymbolicMethod(Class<?> receiverClass, String methodName, Class<?>... params) {
178-
this(new Class<?>[]{receiverClass}, methodName, params);
179-
}
180-
181164
@Override
182165
public boolean equals(Object o) {
183166
if (o instanceof SymbolicMethod that) {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/CompilationProxyBase.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
import java.lang.annotation.Annotation;
2828
import java.lang.reflect.AnnotatedElement;
2929

30-
//JaCoCo Exclude
31-
3230
/**
3331
* A base class for compilation proxies providing implementations of shared methods.
3432
*/

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/CompilerProfilerProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
import jdk.graal.compiler.core.common.CompilerProfiler;
2828
import jdk.vm.ci.meta.ResolvedJavaMethod;
2929

30-
//JaCoCo Exclude
31-
3230
public final class CompilerProfilerProxy extends CompilationProxyBase implements CompilerProfiler {
3331
CompilerProfilerProxy(InvocationHandler handler) {
3432
super(handler);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/ConstantPoolProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434

3535
import java.util.List;
3636

37-
//JaCoCo Exclude
38-
3937
public final class ConstantPoolProxy extends CompilationProxyBase implements ConstantPool {
4038
ConstantPoolProxy(InvocationHandler handler) {
4139
super(handler);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotCodeCacheProviderProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
import jdk.vm.ci.meta.ResolvedJavaMethod;
4444
import jdk.vm.ci.meta.SpeculationLog;
4545

46-
//JaCoCo Exclude
47-
4846
public final class HotSpotCodeCacheProviderProxy extends HotSpotCodeCacheProvider implements CompilationProxy {
4947
private final InvocationHandler handler;
5048

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotConstantProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
import jdk.vm.ci.hotspot.HotSpotConstant;
2828
import jdk.vm.ci.meta.Constant;
2929

30-
//JaCoCo Exclude
31-
3230
public sealed class HotSpotConstantProxy extends CompilationProxyBase implements HotSpotConstant permits HotSpotMetaspaceConstantProxy, HotSpotObjectConstantProxy {
3331
HotSpotConstantProxy(InvocationHandler handler) {
3432
super(handler);

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotConstantReflectionProviderProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@
4141
import jdk.vm.ci.meta.ResolvedJavaField;
4242
import jdk.vm.ci.meta.ResolvedJavaType;
4343

44-
//JaCoCo Exclude
45-
4644
public final class HotSpotConstantReflectionProviderProxy extends HotSpotConstantReflectionProvider implements CompilationProxy {
4745
private final InvocationHandler handler;
4846

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotMemoryAccessProviderProxy.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,7 @@
2929
import jdk.vm.ci.meta.JavaConstant;
3030
import jdk.vm.ci.meta.JavaKind;
3131

32-
//JaCoCo Exclude
33-
34-
final class HotSpotMemoryAccessProviderProxy extends CompilationProxyBase implements HotSpotMemoryAccessProvider {
32+
public final class HotSpotMemoryAccessProviderProxy extends CompilationProxyBase implements HotSpotMemoryAccessProvider {
3533
HotSpotMemoryAccessProviderProxy(InvocationHandler handler) {
3634
super(handler);
3735
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/proxy/HotSpotMetaspaceConstantProxy.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
2929
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
3030

31-
//JaCoCo Exclude
32-
3331
public final class HotSpotMetaspaceConstantProxy extends HotSpotConstantProxy implements HotSpotMetaspaceConstant {
3432
HotSpotMetaspaceConstantProxy(InvocationHandler handler) {
3533
super(handler);

0 commit comments

Comments
 (0)