Skip to content

Commit d918ca9

Browse files
Introduce VMAccess API
The `VMAccess` API can be used to retrieve JVMCI and Graal compiler providers that reflect the state of a JVM. `VMAccess.Builder` can be used to create such a JVM based on a class path, module path etc. This commit adds 2 implementations of `VMAccess.Builder` that can be looked up via service loaders: * A "host" VMAccess which reflects on the host JVM similar to how the native-image builder works today. * A "espresso-context" VMAccess which creates and reflects on an espresso context. The implementation of the espresso context vm access is partial and is done as a refactoring of Espresso's existing "internal" JVMCI implementation. * On the JVMCI side, the main espresso JVMCI implementation classes are turned into abstract classes that are extended by 2 sublasses: one for internal JVMCI which works as before via substitutions of native methods; and one for the new external JVMCI which is implemented via polyglot interop. * In espresso, some common code is moved from the existing substitution classes to some *Util classes. External JVMCI works over interop so some exisiting classes now implement interop and a number of new interop wrappers to util objects are introduced.
1 parent f509af8 commit d918ca9

File tree

74 files changed

+9581
-2408
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+9581
-2408
lines changed

compiler/mx.compiler/suite.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,53 @@
321321
"workingSets" : "Graal,HotSpot",
322322
},
323323

324+
"com.oracle.graal.vmaccess": {
325+
"subDir": "src",
326+
"sourceDirs": ["src"],
327+
"dependencies": [
328+
"jdk.graal.compiler",
329+
],
330+
"requires": [
331+
"jdk.internal.vm.ci",
332+
],
333+
"requiresConcealed": {
334+
"jdk.internal.vm.ci": [
335+
"jdk.vm.ci.meta",
336+
"jdk.vm.ci.code",
337+
],
338+
"java.base": [
339+
"jdk.internal.module",
340+
],
341+
},
342+
"javaCompliance": "21+",
343+
"checkstyle" : "jdk.graal.compiler",
344+
},
345+
346+
"com.oracle.graal.hostvmaccess": {
347+
"subDir": "src",
348+
"sourceDirs": ["src"],
349+
"dependencies": [
350+
"com.oracle.graal.vmaccess",
351+
],
352+
"requires": [
353+
"jdk.internal.vm.ci",
354+
],
355+
"requiresConcealed": {
356+
"java.base": [
357+
"jdk.internal.access",
358+
"jdk.internal.loader",
359+
"jdk.internal.module",
360+
],
361+
"jdk.internal.vm.ci": [
362+
"jdk.vm.ci.meta",
363+
"jdk.vm.ci.runtime",
364+
"jdk.vm.ci.code",
365+
],
366+
},
367+
"javaCompliance": "21+",
368+
"checkstyle" : "jdk.graal.compiler",
369+
},
370+
324371
"jdk.graal.compiler.microbenchmarks" : {
325372
"subDir" : "src",
326373
"sourceDirs" : ["src"],
@@ -636,6 +683,81 @@
636683
},
637684
},
638685

686+
"VMACCESS": {
687+
"moduleInfo": {
688+
"name": "jdk.graal.compiler.vmaccess",
689+
"requires": [
690+
"jdk.internal.vm.ci",
691+
"jdk.graal.compiler",
692+
],
693+
"exports": [
694+
"com.oracle.graal.vmaccess",
695+
],
696+
"requiresConcealed": {
697+
"jdk.internal.vm.ci": [
698+
"jdk.vm.ci.meta",
699+
"jdk.vm.ci.code",
700+
],
701+
"jdk.graal.compiler": [
702+
"jdk.graal.compiler.phases.util",
703+
]
704+
},
705+
"uses": [
706+
"com.oracle.graal.vmaccess.VMAccess",
707+
],
708+
},
709+
"subDir": "src",
710+
"dependencies": [
711+
"com.oracle.graal.vmaccess",
712+
],
713+
"distDependencies": [
714+
"GRAAL",
715+
],
716+
"useModulePath": True,
717+
"maven": False,
718+
},
719+
720+
"HOSTVMACCESS": {
721+
"moduleInfo": {
722+
"name": "jdk.graal.compiler.hostvmaccess",
723+
"requires": [
724+
"jdk.graal.compiler",
725+
"jdk.graal.compiler.vmaccess",
726+
"jdk.internal.vm.ci",
727+
],
728+
"exports": [
729+
"com.oracle.graal.hostvmaccess",
730+
],
731+
"requiresConcealed": {
732+
"java.base": [
733+
"jdk.internal.access",
734+
"jdk.internal.loader",
735+
"jdk.internal.module",
736+
],
737+
"jdk.internal.vm.ci": [
738+
"jdk.vm.ci.meta",
739+
"jdk.vm.ci.runtime",
740+
],
741+
"jdk.graal.compiler": [
742+
"jdk.graal.compiler.api.replacements",
743+
"jdk.graal.compiler.api.runtime",
744+
"jdk.graal.compiler.core.target",
745+
"jdk.graal.compiler.phases.util",
746+
"jdk.graal.compiler.runtime",
747+
]
748+
},
749+
},
750+
"subDir": "src",
751+
"dependencies": [
752+
"com.oracle.graal.hostvmaccess",
753+
],
754+
"distDependencies": [
755+
"VMACCESS",
756+
],
757+
"useModulePath": True,
758+
"maven": False,
759+
},
760+
639761
"LIBGRAAL_LOADER" : {
640762
"subDir": "src",
641763
"dependencies" : [
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
com.oracle.graal.hostvmaccess.HostVMAccessBuilder
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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 com.oracle.graal.hostvmaccess;
26+
27+
import java.lang.reflect.Constructor;
28+
import java.lang.reflect.Executable;
29+
import java.lang.reflect.InvocationTargetException;
30+
import java.lang.reflect.Method;
31+
import java.lang.reflect.Modifier;
32+
33+
import com.oracle.graal.vmaccess.InvocationException;
34+
import com.oracle.graal.vmaccess.VMAccess;
35+
36+
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
37+
import jdk.graal.compiler.api.runtime.GraalJVMCICompiler;
38+
import jdk.graal.compiler.api.runtime.GraalRuntime;
39+
import jdk.graal.compiler.core.target.Backend;
40+
import jdk.graal.compiler.phases.util.Providers;
41+
import jdk.graal.compiler.runtime.RuntimeProvider;
42+
import jdk.vm.ci.meta.JavaConstant;
43+
import jdk.vm.ci.meta.JavaKind;
44+
import jdk.vm.ci.meta.ResolvedJavaMethod;
45+
import jdk.vm.ci.meta.ResolvedJavaType;
46+
import jdk.vm.ci.meta.Signature;
47+
import jdk.vm.ci.runtime.JVMCI;
48+
49+
/**
50+
* An implementation of {@link VMAccess} that reflects on the JVM it's currently running inside.
51+
* There is no isolation between the current JVM and the JVM being accessed through this
52+
* implementation, it is the same JVM.
53+
* <p>
54+
* Note that each instance of this VM access creates a dedicated class loader and module layer that
55+
* it uses to implement {@link VMAccess#lookupAppClassLoaderType} instead of using the host JVM's
56+
* {@linkplain ClassLoader#getSystemClassLoader system/app classloader}.
57+
*/
58+
final class HostVMAccess implements VMAccess {
59+
private final ClassLoader appClassLoader;
60+
private final Providers providers;
61+
62+
HostVMAccess(ClassLoader appClassLoader) {
63+
this.appClassLoader = appClassLoader;
64+
GraalRuntime graalRuntime = ((GraalJVMCICompiler) JVMCI.getRuntime().getCompiler()).getGraalRuntime();
65+
Backend hostBackend = graalRuntime.getCapability(RuntimeProvider.class).getHostBackend();
66+
providers = hostBackend.getProviders();
67+
}
68+
69+
@Override
70+
public Providers getProviders() {
71+
return providers;
72+
}
73+
74+
@Override
75+
public JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, JavaConstant... arguments) {
76+
SnippetReflectionProvider snippetReflection = providers.getSnippetReflection();
77+
Executable executable = snippetReflection.originalMethod(method);
78+
executable.setAccessible(true);
79+
boolean isConstructor = executable instanceof Constructor;
80+
Class<?>[] parameterTypes = executable.getParameterTypes();
81+
if (Modifier.isStatic(executable.getModifiers()) || isConstructor) {
82+
if (receiver != null) {
83+
throw new IllegalArgumentException("For static methods or constructor, the receiver argument must be null");
84+
}
85+
} else if (receiver == null) {
86+
throw new NullPointerException("For instance methods, the receiver argument must not be null");
87+
} else if (receiver.isNull()) {
88+
throw new IllegalArgumentException("For instance methods, the receiver argument must not represent a null constant");
89+
}
90+
if (parameterTypes.length != arguments.length) {
91+
throw new IllegalArgumentException("Wrong number of arguments: expected " + parameterTypes.length + " but got " + arguments.length);
92+
}
93+
Signature signature = method.getSignature();
94+
Object[] unboxedArguments = new Object[parameterTypes.length];
95+
for (int i = 0; i < unboxedArguments.length; i++) {
96+
JavaKind parameterKind = signature.getParameterKind(i);
97+
JavaConstant argument = arguments[i];
98+
if (parameterKind.isObject()) {
99+
unboxedArguments[i] = snippetReflection.asObject(parameterTypes[i], argument);
100+
} else {
101+
assert parameterKind.isPrimitive();
102+
unboxedArguments[i] = argument.asBoxedPrimitive();
103+
}
104+
}
105+
try {
106+
if (isConstructor) {
107+
Constructor<?> constructor = (Constructor<?>) executable;
108+
return snippetReflection.forObject(constructor.newInstance(unboxedArguments));
109+
} else {
110+
Method reflectionMethod = (Method) executable;
111+
Object unboxedReceiver;
112+
if (Modifier.isStatic(reflectionMethod.getModifiers())) {
113+
unboxedReceiver = null;
114+
} else {
115+
unboxedReceiver = snippetReflection.asObject(reflectionMethod.getDeclaringClass(), receiver);
116+
}
117+
JavaKind returnKind = method.getSignature().getReturnKind();
118+
Object result = reflectionMethod.invoke(unboxedReceiver, unboxedArguments);
119+
if (returnKind == JavaKind.Void) {
120+
return null;
121+
}
122+
if (returnKind.isObject()) {
123+
return snippetReflection.forObject(result);
124+
} else {
125+
return snippetReflection.forBoxed(returnKind, result);
126+
}
127+
}
128+
} catch (InstantiationException e) {
129+
throw new IllegalArgumentException(e);
130+
} catch (InvocationTargetException e) {
131+
throw new InvocationException(snippetReflection.forObject(e.getCause()), e);
132+
} catch (IllegalAccessException e) {
133+
throw new RuntimeException(e);
134+
}
135+
}
136+
137+
@Override
138+
public ResolvedJavaType lookupBootClassLoaderType(String name) {
139+
return lookupType(name, null);
140+
}
141+
142+
@Override
143+
public ResolvedJavaType lookupPlatformClassLoaderType(String name) {
144+
return lookupType(name, ClassLoader.getPlatformClassLoader());
145+
}
146+
147+
@Override
148+
public ResolvedJavaType lookupAppClassLoaderType(String name) {
149+
return lookupType(name, appClassLoader);
150+
}
151+
152+
private ResolvedJavaType lookupType(String name, ClassLoader loader) {
153+
try {
154+
Class<?> cls = Class.forName(name, false, loader);
155+
return providers.getMetaAccess().lookupJavaType(cls);
156+
} catch (ClassNotFoundException e) {
157+
return null;
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)