diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py
index 8a2fd4b32570..04736490b923 100644
--- a/compiler/mx.compiler/suite.py
+++ b/compiler/mx.compiler/suite.py
@@ -321,6 +321,55 @@
"workingSets" : "Graal,HotSpot",
},
+ "com.oracle.graal.vmaccess": {
+ "subDir": "src",
+ "sourceDirs": ["src"],
+ "dependencies": [
+ "jdk.graal.compiler",
+ ],
+ "requires": [
+ "jdk.internal.vm.ci",
+ ],
+ "requiresConcealed": {
+ "jdk.internal.vm.ci": [
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.code",
+ ],
+ "java.base": [
+ "jdk.internal.module",
+ ],
+ },
+ "javaCompliance": "21+",
+ "checkstyle" : "jdk.graal.compiler",
+ "graalCompilerSourceEdition": "ignore",
+ },
+
+ "com.oracle.graal.hostvmaccess": {
+ "subDir": "src",
+ "sourceDirs": ["src"],
+ "dependencies": [
+ "com.oracle.graal.vmaccess",
+ ],
+ "requires": [
+ "jdk.internal.vm.ci",
+ ],
+ "requiresConcealed": {
+ "java.base": [
+ "jdk.internal.access",
+ "jdk.internal.loader",
+ "jdk.internal.module",
+ ],
+ "jdk.internal.vm.ci": [
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.runtime",
+ "jdk.vm.ci.code",
+ ],
+ },
+ "javaCompliance": "21+",
+ "checkstyle" : "jdk.graal.compiler",
+ "graalCompilerSourceEdition": "ignore",
+ },
+
"jdk.graal.compiler.microbenchmarks" : {
"subDir" : "src",
"sourceDirs" : ["src"],
@@ -636,6 +685,83 @@
},
},
+ "VMACCESS": {
+ "moduleInfo": {
+ "name": "jdk.graal.compiler.vmaccess",
+ "requires": [
+ "jdk.internal.vm.ci",
+ "jdk.graal.compiler",
+ ],
+ "exports": [
+ "com.oracle.graal.vmaccess",
+ ],
+ "requiresConcealed": {
+ "jdk.internal.vm.ci": [
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.code",
+ ],
+ "jdk.graal.compiler": [
+ "jdk.graal.compiler.phases.util",
+ ]
+ },
+ "uses": [
+ "com.oracle.graal.vmaccess.VMAccess",
+ ],
+ },
+ "subDir": "src",
+ "dependencies": [
+ "com.oracle.graal.vmaccess",
+ ],
+ "distDependencies": [
+ "GRAAL",
+ ],
+ "useModulePath": True,
+ "maven": False,
+ "graalCompilerSourceEdition": "ignore",
+ },
+
+ "HOSTVMACCESS": {
+ "moduleInfo": {
+ "name": "jdk.graal.compiler.hostvmaccess",
+ "requires": [
+ "jdk.graal.compiler",
+ "jdk.graal.compiler.vmaccess",
+ "jdk.internal.vm.ci",
+ ],
+ "exports": [
+ "com.oracle.graal.hostvmaccess",
+ ],
+ "requiresConcealed": {
+ "java.base": [
+ "jdk.internal.access",
+ "jdk.internal.loader",
+ "jdk.internal.module",
+ ],
+ "jdk.internal.vm.ci": [
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.runtime",
+ ],
+ "jdk.graal.compiler": [
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.api.runtime",
+ "jdk.graal.compiler.core.target",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.runtime",
+ ]
+ },
+ },
+ "subDir": "src",
+ "dependencies": [
+ "com.oracle.graal.hostvmaccess",
+ ],
+ "distDependencies": [
+ "VMACCESS",
+ ],
+ "useModulePath": True,
+ "maven": False,
+ "graalCompilerSourceEdition": "ignore",
+ },
+
"LIBGRAAL_LOADER" : {
"subDir": "src",
"dependencies" : [
diff --git a/compiler/src/com.oracle.graal.hostvmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder b/compiler/src/com.oracle.graal.hostvmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder
new file mode 100644
index 000000000000..cc5273315a61
--- /dev/null
+++ b/compiler/src/com.oracle.graal.hostvmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder
@@ -0,0 +1 @@
+com.oracle.graal.hostvmaccess.HostVMAccessBuilder
\ No newline at end of file
diff --git a/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccess.java b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccess.java
new file mode 100644
index 000000000000..4444d37fec1a
--- /dev/null
+++ b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccess.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hostvmaccess;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import com.oracle.graal.vmaccess.InvocationException;
+import com.oracle.graal.vmaccess.VMAccess;
+
+import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
+import jdk.graal.compiler.api.runtime.GraalJVMCICompiler;
+import jdk.graal.compiler.api.runtime.GraalRuntime;
+import jdk.graal.compiler.core.target.Backend;
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.graal.compiler.runtime.RuntimeProvider;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.Signature;
+import jdk.vm.ci.runtime.JVMCI;
+
+/**
+ * An implementation of {@link VMAccess} that reflects on the JVM it's currently running inside.
+ * There is no isolation between the current JVM and the JVM being accessed through this
+ * implementation, it is the same JVM.
+ *
+ * Note that each instance of this VM access creates a dedicated class loader and module layer that
+ * it uses to implement {@link VMAccess#lookupAppClassLoaderType} instead of using the host JVM's
+ * {@linkplain ClassLoader#getSystemClassLoader system/app classloader}.
+ */
+final class HostVMAccess implements VMAccess {
+ private final ClassLoader appClassLoader;
+ private final Providers providers;
+
+ HostVMAccess(ClassLoader appClassLoader) {
+ this.appClassLoader = appClassLoader;
+ GraalRuntime graalRuntime = ((GraalJVMCICompiler) JVMCI.getRuntime().getCompiler()).getGraalRuntime();
+ Backend hostBackend = graalRuntime.getCapability(RuntimeProvider.class).getHostBackend();
+ providers = hostBackend.getProviders();
+ }
+
+ @Override
+ public Providers getProviders() {
+ return providers;
+ }
+
+ @Override
+ public JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, JavaConstant... arguments) {
+ SnippetReflectionProvider snippetReflection = providers.getSnippetReflection();
+ Executable executable = snippetReflection.originalMethod(method);
+ executable.setAccessible(true);
+ boolean isConstructor = executable instanceof Constructor;
+ Class>[] parameterTypes = executable.getParameterTypes();
+ if (Modifier.isStatic(executable.getModifiers()) || isConstructor) {
+ if (receiver != null) {
+ throw new IllegalArgumentException("For static methods or constructor, the receiver argument must be null");
+ }
+ } else if (receiver == null) {
+ throw new NullPointerException("For instance methods, the receiver argument must not be null");
+ } else if (receiver.isNull()) {
+ throw new IllegalArgumentException("For instance methods, the receiver argument must not represent a null constant");
+ }
+ if (parameterTypes.length != arguments.length) {
+ throw new IllegalArgumentException("Wrong number of arguments: expected " + parameterTypes.length + " but got " + arguments.length);
+ }
+ Signature signature = method.getSignature();
+ Object[] unboxedArguments = new Object[parameterTypes.length];
+ for (int i = 0; i < unboxedArguments.length; i++) {
+ JavaKind parameterKind = signature.getParameterKind(i);
+ JavaConstant argument = arguments[i];
+ if (parameterKind.isObject()) {
+ unboxedArguments[i] = snippetReflection.asObject(parameterTypes[i], argument);
+ } else {
+ assert parameterKind.isPrimitive();
+ unboxedArguments[i] = argument.asBoxedPrimitive();
+ }
+ }
+ try {
+ if (isConstructor) {
+ Constructor> constructor = (Constructor>) executable;
+ return snippetReflection.forObject(constructor.newInstance(unboxedArguments));
+ } else {
+ Method reflectionMethod = (Method) executable;
+ Object unboxedReceiver;
+ if (Modifier.isStatic(reflectionMethod.getModifiers())) {
+ unboxedReceiver = null;
+ } else {
+ unboxedReceiver = snippetReflection.asObject(reflectionMethod.getDeclaringClass(), receiver);
+ }
+ JavaKind returnKind = method.getSignature().getReturnKind();
+ Object result = reflectionMethod.invoke(unboxedReceiver, unboxedArguments);
+ if (returnKind == JavaKind.Void) {
+ return null;
+ }
+ if (returnKind.isObject()) {
+ return snippetReflection.forObject(result);
+ } else {
+ return snippetReflection.forBoxed(returnKind, result);
+ }
+ }
+ } catch (InstantiationException e) {
+ throw new IllegalArgumentException(e);
+ } catch (InvocationTargetException e) {
+ throw new InvocationException(snippetReflection.forObject(e.getCause()), e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public ResolvedJavaType lookupBootClassLoaderType(String name) {
+ return lookupType(name, null);
+ }
+
+ @Override
+ public ResolvedJavaType lookupPlatformClassLoaderType(String name) {
+ return lookupType(name, ClassLoader.getPlatformClassLoader());
+ }
+
+ @Override
+ public ResolvedJavaType lookupAppClassLoaderType(String name) {
+ return lookupType(name, appClassLoader);
+ }
+
+ private ResolvedJavaType lookupType(String name, ClassLoader loader) {
+ try {
+ Class> cls = Class.forName(name, false, loader);
+ return providers.getMetaAccess().lookupJavaType(cls);
+ } catch (ClassNotFoundException e) {
+ return null;
+ }
+ }
+}
diff --git a/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessBuilder.java b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessBuilder.java
new file mode 100644
index 000000000000..35f5a13e89c4
--- /dev/null
+++ b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessBuilder.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hostvmaccess;
+
+import java.io.File;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.oracle.graal.vmaccess.ModuleSupport;
+import com.oracle.graal.vmaccess.VMAccess;
+
+public final class HostVMAccessBuilder implements VMAccess.Builder {
+ private List classpath;
+ private List modulepath;
+ private List addModules;
+ private boolean enableAssertions;
+ private Map systemProperties;
+
+ @Override
+ public String getVMAccessName() {
+ return "host";
+ }
+
+ @Override
+ public VMAccess.Builder classPath(List paths) {
+ this.classpath = paths;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder modulePath(List paths) {
+ this.modulepath = paths;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder addModules(List modules) {
+ this.addModules = modules;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder enableAssertions(boolean assertionStatus) {
+ this.enableAssertions = assertionStatus;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder enableSystemAssertions(boolean assertionStatus) {
+ // ignored
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder systemProperty(String name, String value) {
+ if (systemProperties == null) {
+ // Checkstyle: stop stable iteration order check
+ systemProperties = new HashMap<>();
+ // Checkstyle: resume stable iteration order check
+ }
+ systemProperties.put(name, value);
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder vmOption(String option) {
+ // ignored
+ return this;
+ }
+
+ @Override
+ public VMAccess build() {
+ ModuleAccess.ensureModuleAccess();
+ List classPath = classpath.stream().map(Path::of).toList();
+ ModuleFinder upgradeAndSystemModuleFinder = createUpgradeAndSystemModuleFinder();
+ Path[] modulePath = modulepath.stream().map(Path::of).toArray(Path[]::new);
+ ModuleFinder modulePathsFinder = ModuleFinder.of(modulePath);
+ Set moduleNames = modulePathsFinder.findAll().stream() //
+ .map(moduleReference -> moduleReference.descriptor().name()) //
+ .collect(Collectors.toCollection(HashSet::new));
+ moduleNames.addAll(addModules);
+ Configuration configuration = ModuleLayer.boot().configuration().resolve(modulePathsFinder, upgradeAndSystemModuleFinder, moduleNames);
+
+ HostVMAccessClassLoader classLoader = new HostVMAccessClassLoader(classPath, configuration, ClassLoader.getSystemClassLoader());
+ classLoader.setDefaultAssertionStatus(enableAssertions);
+ return new HostVMAccess(classLoader);
+ }
+
+ private ModuleFinder createUpgradeAndSystemModuleFinder() {
+ ModuleFinder finder = ModuleFinder.ofSystem();
+ ModuleFinder upgradeModulePath = finderFor("jdk.module.upgrade.path");
+ if (upgradeModulePath != null) {
+ finder = ModuleFinder.compose(upgradeModulePath, finder);
+ }
+ return finder;
+ }
+
+ private ModuleFinder finderFor(String prop) {
+ String s = systemProperties.get(prop);
+ if (s == null || s.isEmpty()) {
+ return null;
+ } else {
+ String[] dirs = s.split(File.pathSeparator);
+ Path[] paths = new Path[dirs.length];
+ int i = 0;
+ for (String dir : dirs) {
+ paths[i++] = Path.of(dir);
+ }
+ return ModuleFinder.of(paths);
+ }
+ }
+
+ private static final class ModuleAccess {
+ static {
+ ModuleSupport.addExports("jdk.graal.compiler.hostvmaccess", "java.base",
+ "jdk.internal.access",
+ "jdk.internal.loader",
+ "jdk.internal.module");
+ ModuleSupport.addExports("jdk.graal.compiler.hostvmaccess", "jdk.internal.vm.ci",
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.runtime");
+ ModuleSupport.addExports("jdk.graal.compiler.hostvmaccess", "jdk.graal.compiler",
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.api.runtime",
+ "jdk.graal.compiler.core.target",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.runtime");
+ }
+
+ static void ensureModuleAccess() {
+ }
+ }
+}
diff --git a/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessClassLoader.java b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessClassLoader.java
new file mode 100644
index 000000000000..18b508292a3a
--- /dev/null
+++ b/compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessClassLoader.java
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hostvmaccess;
+
+import java.io.IOException;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.file.Path;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.SecureClassLoader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.loader.ClassLoaders;
+import jdk.internal.loader.Resource;
+import jdk.internal.loader.URLClassPath;
+import jdk.internal.module.Resources;
+
+/**
+ * This copy of {@code com.oracle.svm.hosted.NativeImageClassLoader} is used in the
+ * {@link HostVMAccess} to simulate the "app" class loader given the class- and module-path provided
+ * through {@link com.oracle.graal.vmaccess.VMAccess.Builder#classPath} and
+ * {@link com.oracle.graal.vmaccess.VMAccess.Builder#modulePath}.
+ *
+ * This custom class loader is used by the image builder to load the application classes that should
+ * be built into a native-image. It can load classes from a user-provided application module- and
+ * class-path. This is different from the existing classloaders that the JDK provides. While
+ * {@code ModuleLayer.defineModulesWith} methods only allow loading modules at runtime,
+ * {@code URLClassLoader} only allows loading classes on classpath at runtime. This is insufficient
+ * for the image builder as it needs to be able to load from both, module- and class-path, with the
+ * same loader so that classes on the given class-path are able to access classes from the given
+ * module-path.
+ *
+ *
+ * This loader is heavily inspired by {@code jdk.internal.loader.Loader} and {@code URLClassLoader}.
+ * Documentation in this class only mentions where methods diverge from their respective behaviour
+ * in {@code jdk.internal.loader.Loader} and {@code URLClassLoader}. More documentation is available
+ * in the original classes.
+ */
+final class HostVMAccessClassLoader extends SecureClassLoader {
+
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
+ private final ClassLoader parent;
+
+ /* Unmodifiable maps used by this loader */
+ private final Map localNameToModule;
+ private final Map localPackageToModule;
+ private final Map remotePackageToLoader;
+
+ /* Modifiable map used by this loader */
+ private final ConcurrentHashMap moduleToReader;
+
+ private final URLClassPath ucp;
+
+ /**
+ * See {@code jdk.internal.loader.Loader.LoadedModule}.
+ */
+ private static class LoadedModule {
+ private final ModuleReference mref;
+ private final URL url; // may be null
+ private final CodeSource cs;
+
+ LoadedModule(ModuleReference mref) {
+ URL urlVal = null;
+ if (mref.location().isPresent()) {
+ try {
+ urlVal = mref.location().get().toURL();
+ } catch (MalformedURLException | IllegalArgumentException e) {
+ }
+ }
+ this.mref = mref;
+ this.url = urlVal;
+ this.cs = new CodeSource(urlVal, (CodeSigner[]) null);
+ }
+
+ ModuleReference mref() {
+ return mref;
+ }
+
+ String name() {
+ return mref.descriptor().name();
+ }
+
+ @SuppressWarnings("unused")
+ URL location() {
+ return url;
+ }
+
+ CodeSource codeSource() {
+ return cs;
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#Loader} and
+ * {@code java.net.URLClassLoader#URLClassLoader}.
+ */
+ HostVMAccessClassLoader(List classpath, Configuration configuration, ClassLoader parent) {
+ super(parent);
+
+ Objects.requireNonNull(parent);
+ this.parent = parent;
+
+ // Checkstyle: stop stable iteration order check
+ Map nameToModule = new HashMap<>();
+ Map packageToModule = new HashMap<>();
+ // Checkstyle: resume stable iteration order check
+ for (ResolvedModule resolvedModule : configuration.modules()) {
+ ModuleReference mref = resolvedModule.reference();
+ ModuleDescriptor descriptor = mref.descriptor();
+ nameToModule.put(descriptor.name(), mref);
+ descriptor.packages().forEach(pn -> {
+ LoadedModule lm = new LoadedModule(mref);
+ if (packageToModule.put(pn, lm) != null) {
+ throw new IllegalArgumentException("Package " + pn + " in more than one module");
+ }
+ });
+ }
+ localNameToModule = Collections.unmodifiableMap(nameToModule);
+ localPackageToModule = Collections.unmodifiableMap(packageToModule);
+ /*
+ * Unlike {@code jdk.internal.loader.Loader}, we initialize remotePackageToLoader here which
+ * allows us to use an unmodifiable map instead of a ConcurrentHashMap.
+ */
+ remotePackageToLoader = initRemotePackageMap(configuration, List.of(ModuleLayer.boot()));
+
+ /* The only map that gets updated concurrently during the lifetime of this loader. */
+ moduleToReader = new ConcurrentHashMap<>();
+
+ /* Initialize URLClassPath that is used to lookup classes from class-path. */
+ ucp = new URLClassPath(classpath.stream().map(HostVMAccessClassLoader::toURL).toArray(URL[]::new), null);
+
+ }
+
+ private static URL toURL(Path p) {
+ return toURL(p.toUri());
+ }
+
+ private static URL toURL(URI uri) {
+ try {
+ return uri.toURL();
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Given URI '" + uri + "' cannot be expressed as URL.", e);
+ }
+ }
+
+ /**
+ * See {@link jdk.internal.loader.Loader#initRemotePackageMap}.
+ */
+ private Map initRemotePackageMap(Configuration cf, List parentModuleLayers) {
+ // Checkstyle: stop stable iteration order check
+ Map remotePackageMap = new HashMap<>();
+ // Checkstyle: resume stable iteration order check
+
+ for (String name : localNameToModule.keySet()) {
+ ResolvedModule resolvedModule = cf.findModule(name).get();
+ assert resolvedModule.configuration() == cf;
+
+ for (ResolvedModule other : resolvedModule.reads()) {
+ String mn = other.name();
+ ClassLoader loader;
+
+ if (other.configuration() == cf) {
+ assert localNameToModule.containsKey(mn);
+ continue;
+ } else {
+ ModuleLayer layer = parentModuleLayers.stream() //
+ .map(parentLayer -> findModuleLayer(parentLayer, other.configuration())) //
+ .flatMap(Optional::stream) //
+ .findAny() //
+ .orElseThrow(() -> new InternalError("Unable to find parent layer"));
+
+ assert layer.findModule(mn).isPresent();
+ loader = layer.findLoader(mn);
+ if (loader == null) {
+ loader = ClassLoaders.platformClassLoader();
+ }
+ }
+
+ ModuleDescriptor descriptor = other.reference().descriptor();
+ if (descriptor.isAutomatic()) {
+ ClassLoader l = loader;
+ descriptor.packages().forEach(pn -> remotePackage(remotePackageMap, pn, l));
+ } else {
+ for (ModuleDescriptor.Exports e : descriptor.exports()) {
+ if (!e.isQualified()) {
+ remotePackage(remotePackageMap, e.source(), loader);
+ }
+ }
+ }
+ }
+ }
+
+ return Collections.unmodifiableMap(remotePackageMap);
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#remotePackage}.
+ */
+ private static void remotePackage(Map map, String pn, ClassLoader loader) {
+ ClassLoader l = map.putIfAbsent(pn, loader);
+ if (l != null && l != loader) {
+ throw new IllegalStateException("Package " + pn + " cannot be imported from multiple loaders");
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findModuleLayer}.
+ */
+ private static Optional findModuleLayer(ModuleLayer moduleLayer, Configuration cf) {
+ return SharedSecrets.getJavaLangAccess().layers(moduleLayer) //
+ .filter(l -> l.configuration() == cf) //
+ .findAny();
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findResource(String mn, String name)}.
+ */
+ @Override
+ protected URL findResource(String mn, String name) throws IOException {
+ /* For unnamed module, search for resource in class-path */
+ if (mn == null) {
+ return ucp.findResource(name);
+ }
+
+ /* otherwise search in specific module */
+ ModuleReference mref = localNameToModule.get(mn);
+ if (mref == null) {
+ return null;
+ }
+
+ URL url = null;
+ Optional ouri = moduleReaderFor(mref).find(name);
+ if (ouri.isPresent()) {
+ try {
+ url = ouri.get().toURL();
+ } catch (MalformedURLException | IllegalArgumentException e) {
+ }
+ }
+
+ return url;
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findResource(String name)}.
+ */
+ @Override
+ public URL findResource(String name) {
+ String pn = Resources.toPackageName(name);
+
+ /* Search for resource in class-path ... */
+ URL urlOnClasspath = ucp.findResource(name);
+ if (urlOnClasspath != null) {
+ return urlOnClasspath;
+ }
+
+ /* ... and in module-path */
+ LoadedModule module = localPackageToModule.get(pn);
+ if (module != null) {
+ try {
+ URL url = findResource(module.name(), name);
+ if (url != null && (name.endsWith(".class") || url.toString().endsWith("/") || isOpen(module.mref(), pn))) {
+ return url;
+ }
+ } catch (IOException unused) {
+ // ignore
+ }
+
+ } else {
+ for (ModuleReference mref : localNameToModule.values()) {
+ try {
+ URL url = findResource(mref.descriptor().name(), name);
+ if (url != null) {
+ return url;
+ }
+ } catch (IOException unused) {
+ // ignore
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findResources}.
+ */
+ @Override
+ public Enumeration findResources(String name) throws IOException {
+ return Collections.enumeration(findResourcesAsList(name));
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#getResource}.
+ */
+ @Override
+ public URL getResource(String name) {
+ Objects.requireNonNull(name);
+
+ URL url = findResource(name);
+ if (url == null) {
+ url = parent.getResource(name);
+ }
+ return url;
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#getResources}.
+ */
+ @Override
+ public Enumeration getResources(String name) throws IOException {
+ Objects.requireNonNull(name);
+
+ List urls = findResourcesAsList(name);
+ Enumeration e = parent.getResources(name);
+
+ return new Enumeration<>() {
+ final Iterator iterator = urls.iterator();
+
+ @Override
+ public boolean hasMoreElements() {
+ return (iterator.hasNext() || e.hasMoreElements());
+ }
+
+ @Override
+ public URL nextElement() {
+ if (iterator.hasNext()) {
+ return iterator.next();
+ } else {
+ return e.nextElement();
+ }
+ }
+ };
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findResourcesAsList}.
+ */
+ private List findResourcesAsList(String name) throws IOException {
+ String pn = Resources.toPackageName(name);
+
+ List urls = new ArrayList<>();
+
+ /* Search for resource in class-path ... */
+ Enumeration classPathResources = ucp.findResources(name);
+ while (classPathResources.hasMoreElements()) {
+ urls.add(classPathResources.nextElement());
+ }
+
+ /* ... and in module-path */
+ LoadedModule module = localPackageToModule.get(pn);
+ if (module != null) {
+ URL url = findResource(module.name(), name);
+ if (url != null && (name.endsWith(".class") || url.toString().endsWith("/") || isOpen(module.mref(), pn))) {
+ urls.add(url);
+ }
+ } else {
+ for (ModuleReference mref : localNameToModule.values()) {
+ URL url = findResource(mref.descriptor().name(), name);
+ if (url != null) {
+ urls.add(url);
+ }
+ }
+ }
+ return urls;
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findClass(String cn)}.
+ */
+ @Override
+ protected Class> findClass(String cn) throws ClassNotFoundException {
+ Class> c;
+ LoadedModule loadedModule = findLoadedModule(cn);
+ if (loadedModule != null) {
+ c = findClassInModuleOrNull(loadedModule, cn);
+ } else {
+ /* Not found in modules of this loader, try class-path instead */
+ c = findClassViaClassPath(cn);
+ }
+ if (c == null) {
+ throw new ClassNotFoundException(cn);
+ }
+ return c;
+ }
+
+ /**
+ * See {@code java.net.URLClassLoader#findClass(java.lang.String)}.
+ */
+ private Class> findClassViaClassPath(String name) throws ClassNotFoundException {
+ Class> result;
+ String path = name.replace('.', '/').concat(".class");
+ Resource res = ucp.getResource(path);
+ if (res != null) {
+ try {
+ result = defineClass(name, res);
+ } catch (IOException e) {
+ throw new ClassNotFoundException(name, e);
+ } catch (ClassFormatError e2) {
+ if (res.getDataError() != null) {
+ e2.addSuppressed(res.getDataError());
+ }
+ throw e2;
+ }
+ } else {
+ return null;
+ }
+
+ return result;
+ }
+
+ /**
+ * See {@code java.net.URLClassLoader#defineClass}.
+ */
+ private Class> defineClass(String name, Resource res) throws IOException {
+ int i = name.lastIndexOf('.');
+ URL url = res.getCodeSourceURL();
+ if (i != -1) {
+ String pkgname = name.substring(0, i);
+ Manifest man = res.getManifest();
+ if (getAndVerifyPackage(pkgname, man, url) == null) {
+ try {
+ if (man != null) {
+ definePackage(pkgname, man, url);
+ } else {
+ definePackage(pkgname, null, null, null, null, null, null, null);
+ }
+ } catch (IllegalArgumentException iae) {
+ if (getAndVerifyPackage(pkgname, man, url) == null) {
+ throw new AssertionError("Cannot find package " + pkgname);
+ }
+ }
+ }
+ }
+ ByteBuffer bb = res.getByteBuffer();
+ if (bb != null) {
+ CodeSigner[] signers = res.getCodeSigners();
+ CodeSource cs = new CodeSource(url, signers);
+ return defineClass(name, bb, cs);
+ } else {
+ byte[] b = res.getBytes();
+ CodeSigner[] signers = res.getCodeSigners();
+ CodeSource cs = new CodeSource(url, signers);
+ return defineClass(name, b, 0, b.length, cs);
+ }
+ }
+
+ /**
+ * See {@code java.net.URLClassLoader#getAndVerifyPackage}.
+ */
+ private Package getAndVerifyPackage(String pkgname, Manifest man, URL url) {
+ Package pkg = getDefinedPackage(pkgname);
+ if (pkg != null) {
+ if (pkg.isSealed()) {
+ if (!pkg.isSealed(url)) {
+ throw new SecurityException("Sealing violation: package " + pkgname + " is sealed");
+ }
+ } else {
+ if ((man != null) && isSealed(pkgname, man)) {
+ throw new SecurityException("Sealing violation: can't seal package " + pkgname + ": already loaded");
+ }
+ }
+ }
+ return pkg;
+ }
+
+ /**
+ * See {@code java.net.URLClassLoader#definePackage}.
+ */
+ private Package definePackage(String name, Manifest man, URL url) {
+ String specTitle = null;
+ String specVersion = null;
+ String specVendor = null;
+ String implTitle = null;
+ String implVersion = null;
+ String implVendor = null;
+ String sealed = null;
+ URL sealBase = null;
+
+ Attributes attr = SharedSecrets.javaUtilJarAccess() //
+ .getTrustedAttributes(man, name.replace('.', '/').concat("/"));
+ if (attr != null) {
+ specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
+ specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
+ specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
+ implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
+ implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+ implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ attr = man.getMainAttributes();
+ if (attr != null) {
+ if (specTitle == null) {
+ specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
+ }
+ if (specVersion == null) {
+ specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
+ }
+ if (specVendor == null) {
+ specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
+ }
+ if (implTitle == null) {
+ implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
+ }
+ if (implVersion == null) {
+ implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+ }
+ if (implVendor == null) {
+ implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
+ }
+ if (sealed == null) {
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ }
+ if ("true".equalsIgnoreCase(sealed)) {
+ sealBase = url;
+ }
+ return definePackage(name, specTitle, specVersion, specVendor,
+ implTitle, implVersion, implVendor, sealBase);
+ }
+
+ /**
+ * See {@code java.net.URLClassLoader#isSealed}.
+ */
+ private static boolean isSealed(String name, Manifest man) {
+ Attributes attr = SharedSecrets.javaUtilJarAccess() //
+ .getTrustedAttributes(man, name.replace('.', '/').concat("/"));
+ String sealed = null;
+ if (attr != null) {
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ if (sealed == null) {
+ if ((attr = man.getMainAttributes()) != null) {
+ sealed = attr.getValue(Attributes.Name.SEALED);
+ }
+ }
+ return "true".equalsIgnoreCase(sealed);
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findClass(java.lang.String, java.lang.String)}.
+ */
+ @Override
+ protected Class> findClass(String mn, String cn) {
+ Class> c = null;
+ LoadedModule loadedModule = findLoadedModule(cn);
+ if (loadedModule != null && loadedModule.name().equals(mn)) {
+ c = findClassInModuleOrNull(loadedModule, cn);
+ } else {
+ /* Not found in modules of this loader, try class-path instead */
+ try {
+ c = findClassViaClassPath(cn);
+ } catch (ClassNotFoundException ex) {
+ /* Ignored, return null. */
+ }
+ }
+ return c;
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#loadClass(java.lang.String, boolean)}.
+ */
+ @Override
+ protected Class> loadClass(String cn, boolean resolve) throws ClassNotFoundException {
+ synchronized (getClassLoadingLock(cn)) {
+ Class> c = findLoadedClass(cn);
+
+ if (c == null) {
+ try {
+ c = parent.loadClass(cn);
+ } catch (ClassNotFoundException ignore) {
+ /* Ignore. */
+ }
+ }
+
+ if (c == null) {
+ LoadedModule loadedModule = findLoadedModule(cn);
+
+ if (loadedModule != null) {
+ c = findClassInModuleOrNull(loadedModule, cn);
+ } else {
+ /* Not found in modules of this loader, try class-path instead */
+ c = findClassViaClassPath(cn);
+
+ if (c == null) {
+ String pn = packageName(cn);
+ ClassLoader loader = remotePackageToLoader.get(pn);
+ if (loader == null) {
+ loader = parent;
+ }
+ c = loader.loadClass(cn);
+ }
+ }
+ }
+
+ if (c == null) {
+ throw new ClassNotFoundException(cn);
+ }
+
+ if (resolve) {
+ resolveClass(c);
+ }
+
+ return c;
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findClassInModuleOrNull}.
+ */
+ private Class> findClassInModuleOrNull(LoadedModule loadedModule, String cn) {
+ return defineClass(cn, loadedModule);
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#defineClass}.
+ */
+ private Class> defineClass(String cn, LoadedModule loadedModule) {
+ ModuleReader reader = moduleReaderFor(loadedModule.mref());
+
+ try {
+ String rn = cn.replace('.', '/').concat(".class");
+ ByteBuffer bb = reader.read(rn).orElse(null);
+ if (bb == null) {
+ return null;
+ }
+
+ try {
+ return defineClass(cn, bb, loadedModule.codeSource());
+ } finally {
+ reader.release(bb);
+ }
+
+ } catch (IOException ioe) {
+ return null;
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#findLoadedModule}.
+ */
+ private LoadedModule findLoadedModule(String cn) {
+ String pn = packageName(cn);
+ return pn.isEmpty() ? null : localPackageToModule.get(pn);
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#packageName}.
+ */
+ private static String packageName(String cn) {
+ int pos = cn.lastIndexOf('.');
+ return (pos < 0) ? "" : cn.substring(0, pos);
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#moduleReaderFor}.
+ */
+ private ModuleReader moduleReaderFor(ModuleReference mref) {
+ return moduleToReader.computeIfAbsent(mref, unused -> createModuleReader(mref));
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#createModuleReader}.
+ */
+ private static ModuleReader createModuleReader(ModuleReference mref) {
+ try {
+ return mref.open();
+ } catch (IOException e) {
+ return new NullModuleReader();
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#NullModuleReader}.
+ */
+ private static final class NullModuleReader implements ModuleReader {
+ @Override
+ public Optional find(String name) {
+ return Optional.empty();
+ }
+
+ @Override
+ public Stream list() {
+ return Stream.empty();
+ }
+
+ @Override
+ public void close() {
+ throw new InternalError("Should not get here");
+ }
+ }
+
+ /**
+ * See {@code jdk.internal.loader.Loader#isOpen}.
+ */
+ private static boolean isOpen(ModuleReference mref, String pn) {
+ ModuleDescriptor descriptor = mref.descriptor();
+ if (descriptor.isOpen() || descriptor.isAutomatic()) {
+ return true;
+ }
+ for (ModuleDescriptor.Opens opens : descriptor.opens()) {
+ String source = opens.source();
+ if (!opens.isQualified() && source.equals(pn)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/InvocationException.java b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/InvocationException.java
new file mode 100644
index 000000000000..72ade40800fe
--- /dev/null
+++ b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/InvocationException.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.vmaccess;
+
+import jdk.vm.ci.meta.JavaConstant;
+
+/**
+ * Exception thrown when a method invoked through {@link VMAccess#invoke} throws an exception.
+ *
+ * The thrown exception can be retrieved with {@link #getExceptionObject()}.
+ */
+@SuppressWarnings("serial")
+public class InvocationException extends RuntimeException {
+ private final JavaConstant exceptionObject;
+
+ /**
+ * Constructs an {@link InvocationException} for the given exception object.
+ *
+ * @param exceptionObject a {@link JavaConstant} representing a non-null exception object.
+ */
+ public InvocationException(JavaConstant exceptionObject) {
+ if (exceptionObject.isNull() || !exceptionObject.getJavaKind().isObject()) {
+ throw new IllegalArgumentException("The exception object must be a non-null object");
+ }
+ this.exceptionObject = exceptionObject;
+ }
+
+ /**
+ * Constructs an {@link InvocationException} for the given exception object and cause.
+ *
+ * @param exceptionObject a {@link JavaConstant} representing a non-null exception object.
+ * @param cause an exception that was involved in the exception handling of
+ * {@code exceptionObject}.
+ */
+ public InvocationException(JavaConstant exceptionObject, Throwable cause) {
+ super(cause);
+ if (exceptionObject.isNull() || !exceptionObject.getJavaKind().isObject()) {
+ throw new IllegalArgumentException("The exception object must be a non-null object");
+ }
+ this.exceptionObject = exceptionObject;
+ }
+
+ /**
+ * Returns a {@link JavaConstant} representing the exception object that was thrown.
+ */
+ public JavaConstant getExceptionObject() {
+ return exceptionObject;
+ }
+}
diff --git a/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/ModuleSupport.java b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/ModuleSupport.java
new file mode 100644
index 000000000000..0ff8e67ce611
--- /dev/null
+++ b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/ModuleSupport.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.vmaccess;
+
+import java.util.Optional;
+
+import jdk.internal.module.Modules;
+
+/**
+ * This class can be used to programmatically ensure that modules such a JVMCI or the graal compiler
+ * are exported to {@link VMAccess} implementations.
+ *
+ * This class requires {@code java.base/jdk.internal.module} to be exported to this module
+ * ({@code jdk.graal.compiler.vmaccess}).
+ */
+public final class ModuleSupport {
+ static {
+ ModuleSupport.addExports(VMAccess.class, "jdk.internal.vm.ci",
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.code");
+ ModuleSupport.addExports(VMAccess.class, "jdk.graal.compiler",
+ "jdk.graal.compiler.phases.util");
+ }
+
+ private ModuleSupport() {
+ }
+
+ public static void addExports(Class> accessingModuleClass, String targetModuleName, String... packageNames) {
+ addExports(accessingModuleClass.getModule(), targetModuleName, packageNames);
+ }
+
+ public static void addExports(String accessingModuleName, String targetModuleName, String... packageNames) {
+ Optional maybeModule = ModuleLayer.boot().findModule(accessingModuleName);
+ if (maybeModule.isEmpty()) {
+ throw new IllegalStateException("Could not find module " + accessingModuleName + " in the boot layer");
+ }
+ addExports(maybeModule.get(), targetModuleName, packageNames);
+ }
+
+ public static void addExports(Module accessingModule, String targetModuleName, String... packageNames) {
+ Optional maybeModule = ModuleLayer.boot().findModule(targetModuleName);
+ if (maybeModule.isEmpty()) {
+ throw new IllegalStateException("Could not find module " + targetModuleName + " in the boot layer");
+ }
+ addExports(accessingModule, maybeModule.get(), packageNames);
+ }
+
+ public static void addExports(Module accessingModule, Module targetModule, String... packageNames) {
+ for (String packageName : packageNames) {
+ Modules.addExports(targetModule, packageName, accessingModule);
+ }
+ }
+}
diff --git a/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/VMAccess.java b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/VMAccess.java
new file mode 100644
index 000000000000..043fd4992e27
--- /dev/null
+++ b/compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/VMAccess.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.vmaccess;
+
+import java.util.List;
+
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+/**
+ * This provides access to a JVM that can be reflected upon and manipulated using the JVMCI and
+ * Graal compiler APIs.
+ */
+public interface VMAccess {
+ /**
+ * Returns the Graal compiler {@link Providers} which can be used to reflect upon and manipulate
+ * the observed JVM.
+ */
+ Providers getProviders();
+
+ /**
+ * Invokes the provided method.
+ *
+ * - For instance methods (not {@linkplain ResolvedJavaMethod#isStatic() static} and not
+ * {@linkplain ResolvedJavaMethod#isConstructor() constructor}), a receiver of a type compatible
+ * with this method's {@linkplain ResolvedJavaMethod#getDeclaringClass() declaring class} must
+ * be passed as the first argument.
+ * - For {@linkplain ResolvedJavaMethod#isStatic() static} methods, only the plain arguments
+ * need to be passed, no null or class argument needs to be prepended.
+ * - For {@linkplain ResolvedJavaMethod#isConstructor() constructors}, only the plain,
+ * language-level arguments need to be passed. An instance of the
+ * {@linkplain ResolvedJavaMethod#getDeclaringClass() declaring class} will be created and
+ * doesn't need to be prepended.
+ *
+ *
+ * @return the result as a {@link JavaConstant} or null if the method has a void return type.
+ * @param method the method to invoke.
+ * @param receiver for non-static, non-constructor methods, the receiver of the invocation
+ * passed a {@link JavaConstant}. This must be {@code null} for static or constructor
+ * methods.
+ * @param args Arguments of types matching the {@linkplain ResolvedJavaMethod#getSignature()
+ * signature} passed as {@link JavaConstant} objects. The arguments are subject to
+ * conversions as described in the Java Language Specifications' strict invocation
+ * context (5.3).
+ * @throws InvocationException if the invoked method throws an exception, it is wrapped in an
+ * {@link InvocationException}.
+ */
+ JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, JavaConstant... args);
+
+ /**
+ * Lookup a type by name in the {@linkplain ClassLoader#getSystemClassLoader() system/app} class
+ * loader. This performs the usual class loader delegation and behaves as if the following was
+ * called: {@code Class.forName(name, false, ClassLoader.getSystemClassLoader())}.
+ *
+ * Note: this could in theory all be done by using JVMCI and {@link #invoke} to call
+ * {@code Class.forName}. The reason why this method is part of this interface is to allow for
+ * the degenerate case of a "host" VM access where this method doesn't actually load from the
+ * system class loader but from a specially prepared class loader.
+ */
+ ResolvedJavaType lookupAppClassLoaderType(String name);
+
+ /**
+ * Lookup a type by name in the {@linkplain ClassLoader#getPlatformClassLoader() platform} class
+ * loader. This performs the usual class loader delegation and behaves as if the following was
+ * called: {@code Class.forName(name, false, ClassLoader.getPlatformClassLoader())}.
+ */
+ ResolvedJavaType lookupPlatformClassLoaderType(String name);
+
+ /**
+ * Lookup a type by name in the boot ({@code null}) class loader. This performs the usual class
+ * loader delegation and behaves as if the following was called:
+ * {@code Class.forName(name, false, null)}.
+ */
+ ResolvedJavaType lookupBootClassLoaderType(String name);
+
+ /**
+ * A builder can be used to set a JVM context up and observe it through a {@link VMAccess}.
+ *
+ * The {@link java.util.ServiceLoader} API can be used to locate such a builder. Implementations
+ * can be distinguished by their {@linkplain #getVMAccessName() name}.
+ */
+ interface Builder {
+ String getVMAccessName();
+
+ Builder classPath(List paths);
+
+ Builder modulePath(List paths);
+
+ Builder addModules(List modules);
+
+ Builder enableAssertions(boolean assertionStatus);
+
+ Builder enableSystemAssertions(boolean assertionStatus);
+
+ Builder systemProperty(String name, String value);
+
+ Builder vmOption(String option);
+
+ VMAccess build();
+ }
+}
diff --git a/espresso-compiler-stub/mx.espresso-compiler-stub/suite.py b/espresso-compiler-stub/mx.espresso-compiler-stub/suite.py
index ebb307ef22b6..64477fd26fbe 100644
--- a/espresso-compiler-stub/mx.espresso-compiler-stub/suite.py
+++ b/espresso-compiler-stub/mx.espresso-compiler-stub/suite.py
@@ -32,7 +32,7 @@
"name": "GraalVM Development",
"email": "graalvm-dev@oss.oracle.com",
"organization": "Oracle Corporation",
- "organizationUrl": "http://www.graalvm.org/",
+ "organizationUrl": "https://www.graalvm.org/",
},
"scm": {
"url": "https://github.com/oracle/graal/tree/master/truffle",
@@ -86,6 +86,40 @@
# causes spotbugs analysis to fail due to missing classes
"spotbugs": "false"
},
+
+ "com.oracle.truffle.espresso.vmaccess": {
+ "subDir": "src",
+ "sourceDirs": ["src"],
+ "dependencies": [
+ "compiler:GRAAL",
+ "compiler:VMACCESS",
+ "espresso:ESPRESSO_JVMCI",
+ "sdk:POLYGLOT",
+ "com.oracle.truffle.espresso.graal",
+ ],
+ "requires": [
+ "jdk.internal.vm.ci",
+ ],
+ "requiresConcealed": {
+ "jdk.internal.vm.ci": [
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.meta.annotation",
+ "jdk.vm.ci.code",
+ "jdk.vm.ci.code.site",
+ "jdk.vm.ci.code.stack",
+ "jdk.vm.ci.common",
+ "jdk.vm.ci.amd64",
+ "jdk.vm.ci.aarch64",
+ "jdk.vm.ci.services",
+ "jdk.vm.ci.runtime",
+ ],
+ },
+ "javaCompliance": "21+",
+ "checkstyle": "com.oracle.truffle.espresso",
+ # Reference to jdk.vm.ci.meta.annotation
+ # causes spotbugs analysis to fail due to missing classes
+ "spotbugs": "false"
+ },
},
# ------------- distributions
@@ -106,7 +140,39 @@
"ESPRESSO_GRAAL": {
"subDir": "src",
"moduleInfo": {
- "name": "jdk.graal.compiler.espresso"
+ "name": "jdk.graal.compiler.espresso",
+ "exports": [
+ "com.oracle.truffle.espresso.graal to jdk.graal.compiler.espresso.vmaccess",
+ ],
+ "requiresConcealed": {
+ "jdk.graal.compiler": [
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.api.runtime",
+ "jdk.graal.compiler.bytecode",
+ "jdk.graal.compiler.code",
+ "jdk.graal.compiler.core.common",
+ "jdk.graal.compiler.core.common.alloc",
+ "jdk.graal.compiler.core.common.memory",
+ "jdk.graal.compiler.core.common.spi",
+ "jdk.graal.compiler.core.common.type",
+ "jdk.graal.compiler.core.target",
+ "jdk.graal.compiler.debug",
+ "jdk.graal.compiler.graph",
+ "jdk.graal.compiler.nodes",
+ "jdk.graal.compiler.nodes.gc",
+ "jdk.graal.compiler.nodes.graphbuilderconf",
+ "jdk.graal.compiler.nodes.loop",
+ "jdk.graal.compiler.nodes.memory",
+ "jdk.graal.compiler.nodes.memory.address",
+ "jdk.graal.compiler.nodes.spi",
+ "jdk.graal.compiler.options",
+ "jdk.graal.compiler.phases.tiers",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.replacements",
+ "jdk.graal.compiler.runtime",
+ "jdk.graal.compiler.word",
+ ],
+ },
},
"dependencies": [
"com.oracle.truffle.espresso.graal",
@@ -115,8 +181,48 @@
"compiler:GRAAL",
"espresso:ESPRESSO_JVMCI",
],
+ "useModulePath": True,
"description": "A dummy GraalJVMCICompiler implementation for Espresso",
"maven": False,
},
+
+ "ESPRESSO_VMACCESS": {
+ "subDir": "src",
+ "moduleInfo": {
+ "name": "jdk.graal.compiler.espresso.vmaccess",
+ "requires": [
+ "jdk.internal.vm.ci",
+ "jdk.graal.compiler",
+ "jdk.graal.compiler.vmaccess",
+ "transitive org.graalvm.polyglot",
+ ],
+ "exports": [
+ "com.oracle.truffle.espresso.vmaccess",
+ ],
+ "requiresConcealed": {
+ "jdk.graal.compiler": [
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.core.common.spi",
+ "jdk.graal.compiler.debug",
+ "jdk.graal.compiler.nodes.loop",
+ "jdk.graal.compiler.nodes.spi",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.word",
+ ],
+ },
+ },
+ "dependencies": [
+ "com.oracle.truffle.espresso.vmaccess",
+ ],
+ "distDependencies": [
+ "sdk:POLYGLOT",
+ "compiler:GRAAL",
+ "compiler:VMACCESS",
+ "espresso:ESPRESSO_JVMCI",
+ "ESPRESSO_GRAAL",
+ ],
+ "useModulePath": True,
+ "maven": False,
+ },
}
}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoConstantFieldProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoConstantFieldProvider.java
index bbb62f18e371..c2d0511e0378 100644
--- a/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoConstantFieldProvider.java
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoConstantFieldProvider.java
@@ -22,9 +22,10 @@
*/
package com.oracle.truffle.espresso.graal;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaField;
+
import jdk.graal.compiler.core.common.spi.JavaConstantFieldProvider;
import jdk.graal.compiler.options.OptionValues;
-import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaField;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
@@ -39,8 +40,7 @@ protected boolean isStableField(ResolvedJavaField field, ConstantFieldTool> to
if (field.isStatic() && !isStaticFieldConstant(field, tool.getOptions())) {
return false;
}
-
- if (((EspressoResolvedJavaField) field).isStable()) {
+ if (((AbstractEspressoResolvedJavaField) field).isStable()) {
return true;
}
return super.isStableField(field, tool);
@@ -51,7 +51,6 @@ protected boolean isFinalField(ResolvedJavaField field, ConstantFieldTool> too
if (field.isStatic() && !isStaticFieldConstant(field, tool.getOptions())) {
return false;
}
-
return super.isFinalField(field, tool);
}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoMetaAccessExtensionProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoMetaAccessExtensionProvider.java
index 6517bcbcadfd..4523b5464423 100644
--- a/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoMetaAccessExtensionProvider.java
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.graal/src/com/oracle/truffle/espresso/graal/EspressoMetaAccessExtensionProvider.java
@@ -22,10 +22,10 @@
*/
package com.oracle.truffle.espresso.graal;
+import com.oracle.truffle.espresso.jvmci.meta.ConstantReflectionProviderWithStaticsBase;
+
import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
import jdk.graal.compiler.debug.GraalError;
-import com.oracle.truffle.espresso.jvmci.meta.EspressoConstantReflectionProvider;
-import com.oracle.truffle.espresso.jvmci.meta.EspressoObjectConstant;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
@@ -34,9 +34,9 @@
import jdk.vm.ci.meta.ResolvedJavaType;
public final class EspressoMetaAccessExtensionProvider implements MetaAccessExtensionProvider {
- private final EspressoConstantReflectionProvider constantReflection;
+ private final ConstantReflectionProviderWithStaticsBase constantReflection;
- public EspressoMetaAccessExtensionProvider(EspressoConstantReflectionProvider constantReflection) {
+ public EspressoMetaAccessExtensionProvider(ConstantReflectionProviderWithStaticsBase constantReflection) {
this.constantReflection = constantReflection;
}
@@ -65,10 +65,7 @@ public ResolvedJavaField getStaticFieldForAccess(JavaConstant base, long offset,
if (accessKind.getSlotCount() <= 0) {
throw new IllegalArgumentException("Unexpected access kind: " + accessKind);
}
- if (!(base instanceof EspressoObjectConstant)) {
- return null;
- }
- ResolvedJavaType type = constantReflection.getTypeForStaticBase((EspressoObjectConstant) base);
+ ResolvedJavaType type = constantReflection.getTypeForStaticBase(base);
if (type == null) {
return null;
}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder
new file mode 100644
index 000000000000..aa480634f86c
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder
@@ -0,0 +1 @@
+com.oracle.truffle.espresso.vmaccess.EspressoExternalVMAccessBuilder
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantPool.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantPool.java
new file mode 100644
index 000000000000..ab3b323eac38
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantPool.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import static com.oracle.truffle.espresso.vmaccess.EspressoExternalVMAccess.throwHostException;
+
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoConstantPool;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaField;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaMethod;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoBootstrapMethodInvocation;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+final class EspressoExternalConstantPool extends AbstractEspressoConstantPool {
+ private final EspressoExternalResolvedInstanceType holder;
+ private final Value cpMirror;
+
+ EspressoExternalConstantPool(EspressoExternalResolvedInstanceType holder) {
+ this.holder = holder;
+ this.cpMirror = holder.getAccess().invokeJVMCIHelper("getConstantPool", holder.getMetaObject());
+ }
+
+ @Override
+ protected boolean loadReferencedType0(int cpi, int opcode) {
+ try {
+ return cpMirror.invokeMember("loadReferencedType", cpi, opcode).asBoolean();
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ protected AbstractEspressoResolvedJavaField lookupResolvedField(int cpi, AbstractEspressoResolvedJavaMethod method, int opcode) {
+ Value methodMirror = null;
+ if (method != null) {
+ methodMirror = ((EspressoExternalResolvedJavaMethod) method).getMirror();
+ }
+ Value resolved = cpMirror.invokeMember("lookupResolvedField", cpi, opcode, methodMirror);
+ if (resolved.isNull()) {
+ return null;
+ }
+ return new EspressoExternalResolvedJavaField(holder, resolved);
+ }
+
+ @Override
+ protected ResolvedJavaType getMethodHandleType() {
+ return (ResolvedJavaType) holder.getAccess().lookupType("Ljava/lang/invoke/MethodHandle;", holder.getAccess().getJavaLangObject(), true);
+ }
+
+ @Override
+ protected JavaType lookupFieldType(int cpi, AbstractEspressoResolvedInstanceType accessingType) {
+ String typeDescriptor = lookupDescriptor(cpi);
+ return holder.getAccess().lookupType(typeDescriptor, accessingType, false);
+ }
+
+ @Override
+ protected String lookupDescriptor(int cpi) {
+ try {
+ return cpMirror.invokeMember("lookupDescriptor", cpi).asString();
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ protected String lookupName(int cpi) {
+ return cpMirror.invokeMember("lookupName", cpi).asString();
+ }
+
+ @Override
+ protected AbstractEspressoResolvedJavaMethod lookupResolvedMethod(int cpi, int opcode, AbstractEspressoResolvedJavaMethod caller) {
+ Value callerMirror = null;
+ if (caller != null) {
+ callerMirror = ((EspressoExternalResolvedJavaMethod) caller).getMirror();
+ }
+ Value resolved = cpMirror.invokeMember("lookupResolvedMethod", cpi, opcode, callerMirror);
+ if (resolved.isNull()) {
+ return null;
+ }
+ EspressoExternalResolvedInstanceType methodHolder;
+ Value methodHolderMeta = resolved.getMember("holder");
+ if (methodHolderMeta.equals(holder.getMetaObject())) {
+ methodHolder = holder;
+ } else {
+ methodHolder = new EspressoExternalResolvedInstanceType(holder.getAccess(), methodHolderMeta);
+ }
+ return new EspressoExternalResolvedJavaMethod(methodHolder, resolved);
+ }
+
+ @Override
+ protected byte getTagByteAt(int cpi) {
+ try {
+ return cpMirror.invokeMember("getTagByteAt", cpi).asByte();
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ public int length() {
+ return cpMirror.getMember("length").asInt();
+ }
+
+ @Override
+ public JavaType lookupReferencedType(int rawIndex, int opcode) {
+ try {
+ Value result = cpMirror.invokeMember("lookupReferencedType", rawIndex, opcode);
+ return holder.getAccess().toJavaType(result);
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ public JavaType lookupType(int cpi, int opcode) {
+ try {
+ Value result = cpMirror.invokeMember("lookupType", cpi, opcode);
+ return holder.getAccess().toJavaType(result);
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ public String lookupUtf8(int cpi) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public Object lookupConstant(int cpi, boolean resolve) {
+ try {
+ return switch (getTagByteAt(cpi)) {
+ case CONSTANT_Integer -> JavaConstant.forInt(cpMirror.invokeMember("lookupConstant", cpi).asInt());
+ case CONSTANT_Long -> JavaConstant.forLong(cpMirror.invokeMember("lookupConstant", cpi).asLong());
+ case CONSTANT_Float -> JavaConstant.forFloat(cpMirror.invokeMember("lookupConstant", cpi).asFloat());
+ case CONSTANT_Double -> JavaConstant.forDouble(cpMirror.invokeMember("lookupConstant", cpi).asDouble());
+ case CONSTANT_Class -> lookupType(cpi, 0);
+ case CONSTANT_String, CONSTANT_MethodHandle, CONSTANT_MethodType -> new EspressoExternalObjectConstant(holder.getAccess(), cpMirror.invokeMember("lookupConstant", cpi));
+ case CONSTANT_Dynamic -> switch (cpMirror.invokeMember("lookupDynamicKind", cpi).asInt()) {
+ case 'Z' -> JavaConstant.forBoolean(cpMirror.invokeMember("lookupConstant", cpi).asBoolean());
+ case 'B' -> JavaConstant.forByte(cpMirror.invokeMember("lookupConstant", cpi).asByte());
+ case 'C' -> JavaConstant.forChar((char) cpMirror.invokeMember("lookupConstant", cpi).asInt());
+ case 'S' -> JavaConstant.forShort(cpMirror.invokeMember("lookupConstant", cpi).asShort());
+ case 'I' -> JavaConstant.forInt(cpMirror.invokeMember("lookupConstant", cpi).asInt());
+ case 'J' -> JavaConstant.forLong(cpMirror.invokeMember("lookupConstant", cpi).asLong());
+ case 'F' -> JavaConstant.forFloat(cpMirror.invokeMember("lookupConstant", cpi).asFloat());
+ case 'D' -> JavaConstant.forDouble(cpMirror.invokeMember("lookupConstant", cpi).asDouble());
+ case 'L' -> new EspressoExternalObjectConstant(holder.getAccess(), cpMirror.invokeMember("lookupConstant", cpi));
+ default -> throw JVMCIError.shouldNotReachHere(cpMirror.invokeMember("lookupDynamicKind", cpi).toString());
+ };
+ default -> throw new IllegalArgumentException("Unsupported tag: " + getTagByteAt(cpi) + " (" + getTagByteAt(cpi) + ")");
+ };
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ }
+
+ @Override
+ public JavaConstant lookupAppendix(int rawIndex, int opcode) {
+ Value value;
+ try {
+ value = cpMirror.invokeMember("lookupAppendix", rawIndex, opcode);
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ if (value.isNull()) {
+ return null;
+ }
+ return new EspressoExternalObjectConstant(holder.getAccess(), value);
+ }
+
+ @Override
+ protected int getNumIndyEntries() {
+ return cpMirror.getMember("numIndyEntries").asInt();
+ }
+
+ @Override
+ protected EspressoBootstrapMethodInvocation lookupIndyBootstrapMethodInvocation(int siteIndex) {
+ Value value;
+ try {
+ value = cpMirror.invokeMember("lookupIndyBootstrapMethodInvocation", siteIndex);
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ assert !value.isNull();
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public BootstrapMethodInvocation lookupBootstrapMethodInvocation(int index, int opcode) {
+ Value value;
+ try {
+ value = cpMirror.invokeMember("lookupBootstrapMethodInvocation", index, opcode);
+ } catch (PolyglotException e) {
+ throw throwHostException(e);
+ }
+ if (value.isNull()) {
+ return null;
+ }
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected EspressoExternalSignature getSignature(String rawSignature) {
+ return new EspressoExternalSignature(holder.getAccess(), rawSignature);
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantReflectionProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantReflectionProvider.java
new file mode 100644
index 000000000000..d24d2ebf140c
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantReflectionProvider.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType;
+import com.oracle.truffle.espresso.jvmci.meta.ConstantReflectionProviderWithStaticsBase;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaType;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedObjectType;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedPrimitiveType;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.Constant;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.MemoryAccessProvider;
+import jdk.vm.ci.meta.MethodHandleAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+final class EspressoExternalConstantReflectionProvider implements ConstantReflectionProviderWithStaticsBase {
+ private final EspressoExternalVMAccess access;
+
+ EspressoExternalConstantReflectionProvider(EspressoExternalVMAccess access) {
+ this.access = access;
+ }
+
+ @Override
+ public Boolean constantEquals(Constant x, Constant y) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public Integer readArrayLength(JavaConstant array) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant readArrayElement(JavaConstant array, int index) {
+ throw JVMCIError.unimplemented();
+ }
+
+ private static Class> safeGetClass(Object o) {
+ if (o == null) {
+ return null;
+ }
+ return o.getClass();
+ }
+
+ @Override
+ public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) {
+ if (receiver != null && !(receiver instanceof EspressoExternalObjectConstant)) {
+ throw new IllegalArgumentException("expected an espresso receiver, got a " + receiver.getClass());
+ }
+ EspressoExternalObjectConstant espressoReceiver = (EspressoExternalObjectConstant) receiver;
+ if (!(field instanceof EspressoExternalResolvedJavaField espressoField)) {
+ throw new IllegalArgumentException("expected an espresso field, got a " + safeGetClass(field));
+ }
+ Value receiverValue;
+ if (field.isStatic()) {
+ EspressoExternalResolvedInstanceType declaringClass = (EspressoExternalResolvedInstanceType) espressoField.getDeclaringClass();
+ if (!declaringClass.isInitialized()) {
+ return null;
+ }
+ receiverValue = declaringClass.getMetaObject();
+ } else {
+ if (receiver == null || !espressoField.getDeclaringClass().isAssignableFrom(espressoReceiver.getType())) {
+ return null;
+ }
+ receiverValue = espressoReceiver.getValue();
+ }
+ Value value;
+ if (field.isPublic()) {
+ // use a full descriptor with the type?
+ value = receiverValue.getMember(espressoField.getName());
+ } else {
+ throw JVMCIError.unimplemented();
+ }
+ return asJavaConstant(value, espressoField.getJavaKind(), access);
+ }
+
+ static JavaConstant asJavaConstant(Value value, JavaKind kind, EspressoExternalVMAccess access) {
+ return switch (kind) {
+ case Boolean -> JavaConstant.forBoolean(value.asBoolean());
+ case Byte -> JavaConstant.forByte(value.asByte());
+ case Short -> JavaConstant.forShort(value.asShort());
+ case Char -> JavaConstant.forChar(value.as(Character.class));
+ case Int -> JavaConstant.forInt(value.asInt());
+ case Long -> JavaConstant.forLong(value.asLong());
+ case Float -> JavaConstant.forFloat(value.asFloat());
+ case Double -> JavaConstant.forDouble(value.asDouble());
+ case Object -> new EspressoExternalObjectConstant(access, value);
+ default -> throw JVMCIError.shouldNotReachHere(kind.toString());
+ };
+ }
+
+ @Override
+ public JavaConstant boxPrimitive(JavaConstant source) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant unboxPrimitive(JavaConstant source) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant forString(String value) {
+ Value guestString = access.invokeJVMCIHelper("toGuestString", value);
+ return new EspressoExternalObjectConstant(access, guestString);
+ }
+
+ @Override
+ public ResolvedJavaType asJavaType(Constant constant) {
+ if (constant instanceof EspressoExternalObjectConstant espressoConstant) {
+ // j.l.Class?
+ Value value = espressoConstant.getValue();
+ if ("java.lang.Class".equals(value.getMetaObject().getMetaQualifiedName())) {
+ return classAsType(value, access);
+ }
+ return null;
+ }
+ if (constant instanceof KlassConstant klassConstant) {
+ return klassConstant.getType();
+ }
+ throw new IllegalArgumentException(constant.getClass().toString());
+ }
+
+ static EspressoResolvedJavaType classAsType(Value value, EspressoExternalVMAccess access) {
+ if (value.invokeMember("isArray").asBoolean()) {
+ Value elemental = value;
+ int dimensions = 0;
+ do {
+ dimensions++;
+ elemental = elemental.invokeMember("getComponentType");
+ } while (elemental.invokeMember("isArray").asBoolean());
+ return new EspressoExternalResolvedArrayType(getNonArrayType(elemental, access), dimensions, access);
+ }
+ return getNonArrayType(value, access);
+ }
+
+ private static EspressoResolvedJavaType getNonArrayType(Value value, EspressoExternalVMAccess access) {
+ if (value.invokeMember("isPrimitive").asBoolean()) {
+ return getPrimitiveType(value.getMember("static").getMetaQualifiedName(), access);
+ }
+ assert !value.invokeMember("isArray").asBoolean();
+ return new EspressoExternalResolvedInstanceType(access, value.getMember("static"));
+ }
+
+ private static EspressoExternalResolvedPrimitiveType getPrimitiveType(String name, EspressoExternalVMAccess access) {
+ JavaKind kind = switch (name) {
+ case "boolean" -> JavaKind.Boolean;
+ case "byte" -> JavaKind.Byte;
+ case "short" -> JavaKind.Short;
+ case "char" -> JavaKind.Char;
+ case "int" -> JavaKind.Int;
+ case "long" -> JavaKind.Long;
+ case "float" -> JavaKind.Float;
+ case "double" -> JavaKind.Double;
+ case "void" -> JavaKind.Void;
+ default -> throw JVMCIError.shouldNotReachHere(name);
+ };
+ assert kind.getJavaName().equals(name);
+ return access.forPrimitiveKind(kind);
+ }
+
+ @Override
+ public MethodHandleAccessProvider getMethodHandleAccess() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public MemoryAccessProvider getMemoryAccessProvider() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant asJavaClass(ResolvedJavaType type) {
+ Value clazz = switch (type) {
+ case EspressoExternalResolvedInstanceType espressoType -> espressoType.getMetaObject().getMember("class");
+ case EspressoResolvedPrimitiveType primitiveType -> access.getPrimitiveClass(primitiveType.getJavaKind());
+ default -> throw new IllegalArgumentException("expected an espresso type, got a " + type.getClass());
+ };
+ return new EspressoExternalObjectConstant(access, clazz);
+ }
+
+ @Override
+ public Constant asObjectHub(ResolvedJavaType type) {
+ if (!(type instanceof EspressoResolvedObjectType espressoType)) {
+ throw new IllegalArgumentException("expected an espresso object type, got a " + type.getClass());
+ }
+ return new KlassConstant(espressoType);
+ }
+
+ @Override
+ public AbstractEspressoResolvedInstanceType getTypeForStaticBase(JavaConstant staticBase) {
+ if (!(staticBase instanceof EspressoExternalObjectConstant)) {
+ return null;
+ }
+ throw JVMCIError.unimplemented();
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalIdentityHashCodeProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalIdentityHashCodeProvider.java
new file mode 100644
index 000000000000..39701432d73f
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalIdentityHashCodeProvider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider;
+import jdk.vm.ci.meta.JavaConstant;
+
+final class EspressoExternalIdentityHashCodeProvider implements IdentityHashCodeProvider {
+ @Override
+ public Integer identityHashCode(JavaConstant constant) {
+ if (!(constant instanceof EspressoExternalObjectConstant objectConstant)) {
+ return null;
+ }
+ return objectConstant.guestHashCode();
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalMetaAccessProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalMetaAccessProvider.java
new file mode 100644
index 000000000000..e92557da0dd7
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalMetaAccessProvider.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaType;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.DeoptimizationAction;
+import jdk.vm.ci.meta.DeoptimizationReason;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.MetaAccessProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.Signature;
+import jdk.vm.ci.meta.SpeculationLog;
+
+final class EspressoExternalMetaAccessProvider implements MetaAccessProvider {
+ private static final ClassLoader PLATFORM_CLASS_LOADER = ClassLoader.getPlatformClassLoader();
+ private static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
+ private final EspressoExternalVMAccess access;
+
+ EspressoExternalMetaAccessProvider(EspressoExternalVMAccess access) {
+ this.access = access;
+ }
+
+ private static boolean isKnownLoader(ClassLoader loader) {
+ return loader == null || loader == PLATFORM_CLASS_LOADER || loader == SYSTEM_CLASS_LOADER;
+ }
+
+ @Override
+ public EspressoResolvedJavaType lookupJavaType(Class> clazz) {
+ if (!isKnownLoader(clazz.getClassLoader())) {
+ throw new IllegalArgumentException("Cannot lookup types with unknown class loader");
+ }
+ if (clazz.isArray()) {
+ int dims = 0;
+ Class> elemental = clazz;
+ do {
+ dims++;
+ elemental = elemental.getComponentType();
+ } while (elemental.isArray());
+ return new EspressoExternalResolvedArrayType(lookupNonArrayType(elemental), dims, access);
+ }
+ return lookupNonArrayType(clazz);
+ }
+
+ private EspressoResolvedJavaType lookupNonArrayType(Class> clazz) {
+ assert !clazz.isArray() : clazz;
+ if (clazz.isPrimitive()) {
+ return access.forPrimitiveKind(JavaKind.fromJavaClass(clazz));
+ }
+ Value value = access.lookupMetaObject(clazz.getName());
+ if (value.isNull()) {
+ throw new NoClassDefFoundError(clazz.getName());
+ }
+ return new EspressoExternalResolvedInstanceType(access, value);
+ }
+
+ @Override
+ public ResolvedJavaMethod lookupJavaMethod(Executable reflectionMethod) {
+ EspressoResolvedJavaType declaringType = lookupJavaType(reflectionMethod.getDeclaringClass());
+ ResolvedJavaMethod[] methods;
+ String name;
+ EspressoExternalSignature signature = lookupSignature(reflectionMethod);
+ if (reflectionMethod instanceof Constructor) {
+ methods = declaringType.getDeclaredConstructors();
+ name = "";
+ } else {
+ assert reflectionMethod instanceof Method : reflectionMethod;
+ methods = declaringType.getDeclaredMethods();
+ name = reflectionMethod.getName();
+ }
+ ResolvedJavaMethod result = findMethod(methods, name, signature);
+ if (result == null) {
+ throw new NoSuchMethodError(reflectionMethod.toString());
+ }
+ return result;
+ }
+
+ private EspressoExternalSignature lookupSignature(Executable reflectionExecutable) {
+ StringBuilder sb = new StringBuilder("(");
+ for (Parameter p : reflectionExecutable.getParameters()) {
+ appendType(sb, p.getType());
+ }
+ sb.append(')');
+ if (reflectionExecutable instanceof Method reflectionMethod) {
+ appendType(sb, reflectionMethod.getReturnType());
+ } else {
+ sb.append('V');
+ }
+ return new EspressoExternalSignature(access, sb.toString());
+ }
+
+ private static void appendType(StringBuilder sb, Class> clazz) {
+ if (clazz.isArray()) {
+ Class> t = clazz;
+ do {
+ sb.append('[');
+ t = t.getComponentType();
+ } while (t.isArray());
+ appendNonArrayType(sb, clazz);
+ } else {
+ appendNonArrayType(sb, clazz);
+ }
+ }
+
+ private static void appendNonArrayType(StringBuilder sb, Class> clazz) {
+ assert !clazz.isArray() : clazz;
+ if (clazz.isPrimitive()) {
+ sb.append(JavaKind.fromJavaClass(clazz).getTypeChar());
+ } else {
+ sb.append('L');
+ String name = clazz.getName();
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ if (c == '.') {
+ sb.append('/');
+ } else {
+ sb.append(c);
+ }
+ }
+ sb.append(';');
+ }
+ }
+
+ private static ResolvedJavaMethod findMethod(ResolvedJavaMethod[] methods, String name, Signature signature) {
+ for (ResolvedJavaMethod method : methods) {
+ if (method.getName().equals(name) && signature.equals(method.getSignature())) {
+ return method;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ResolvedJavaField lookupJavaField(Field reflectionField) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaType lookupJavaType(JavaConstant constant) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public Signature parseMethodDescriptor(String methodDescriptor) {
+ return new EspressoExternalSignature(access, methodDescriptor);
+ }
+
+ @Override
+ public long getMemorySize(JavaConstant constant) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant encodeDeoptActionAndReason(DeoptimizationAction action, DeoptimizationReason reason, int debugId) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public JavaConstant encodeSpeculation(SpeculationLog.Speculation speculation) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public SpeculationLog.Speculation decodeSpeculation(JavaConstant constant, SpeculationLog speculationLog) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public DeoptimizationReason decodeDeoptReason(JavaConstant constant) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public DeoptimizationAction decodeDeoptAction(JavaConstant constant) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public int decodeDebugId(JavaConstant constant) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public int getArrayBaseOffset(JavaKind elementKind) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public int getArrayIndexScale(JavaKind elementKind) {
+ throw JVMCIError.unimplemented();
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalObjectConstant.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalObjectConstant.java
new file mode 100644
index 000000000000..e444608a6448
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalObjectConstant.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedObjectType;
+
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+
+final class EspressoExternalObjectConstant implements JavaConstant {
+ private final EspressoExternalVMAccess access;
+ private final org.graalvm.polyglot.Value value;
+
+ EspressoExternalObjectConstant(EspressoExternalVMAccess access, org.graalvm.polyglot.Value value) {
+ this.access = access;
+ this.value = value;
+ }
+
+ @Override
+ public JavaKind getJavaKind() {
+ return JavaKind.Object;
+ }
+
+ @Override
+ public boolean isNull() {
+ return false;
+ }
+
+ @Override
+ public boolean isDefaultForKind() {
+ return false;
+ }
+
+ @Override
+ public Object asBoxedPrimitive() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public int asInt() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public boolean asBoolean() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public long asLong() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public float asFloat() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public double asDouble() {
+ throw new IllegalArgumentException();
+ }
+
+ @Override
+ public String toValueString() {
+ return "Instance<" + getType().toJavaName() + ">";
+ }
+
+ public EspressoResolvedObjectType getType() {
+ Value cls = value.getMetaObject().getMember("class");
+ return (EspressoResolvedObjectType) EspressoExternalConstantReflectionProvider.classAsType(cls, access);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ EspressoExternalObjectConstant that = (EspressoExternalObjectConstant) o;
+ return value.equals(that.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return guestHashCode();
+ }
+
+ int guestHashCode() {
+ return value.hashCode();
+ }
+
+ org.graalvm.polyglot.Value getValue() {
+ return value;
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedArrayType.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedArrayType.java
new file mode 100644
index 000000000000..92c8d8140de1
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedArrayType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedArrayType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaType;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedObjectType;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaConstant;
+
+final class EspressoExternalResolvedArrayType extends AbstractEspressoResolvedArrayType {
+ private final EspressoExternalVMAccess access;
+
+ EspressoExternalResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, EspressoExternalVMAccess access) {
+ super(elementalType, dimensions);
+ this.access = access;
+ }
+
+ EspressoExternalResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, EspressoResolvedJavaType componentType, EspressoExternalVMAccess access) {
+ super(elementalType, dimensions, componentType);
+ this.access = access;
+ }
+
+ @Override
+ protected AbstractEspressoResolvedArrayType withNewElementalType(EspressoResolvedJavaType resolvedElementalType) {
+ return new EspressoExternalResolvedArrayType(resolvedElementalType, dimensions, access);
+ }
+
+ @Override
+ protected AbstractEspressoResolvedArrayType getArrayComponentType0() {
+ return new EspressoExternalResolvedArrayType(elementalType, dimensions - 1, access);
+ }
+
+ @Override
+ protected AbstractEspressoResolvedArrayType getArrayClass0() {
+ return new EspressoExternalResolvedArrayType(elementalType, dimensions + 1, this, access);
+ }
+
+ @Override
+ protected Class> getMirror0() {
+ throw JVMCIError.shouldNotReachHere("Mirrors cannot be accessed for external JVMCI");
+ }
+
+ @Override
+ protected AbstractEspressoResolvedInstanceType getJavaLangObject() {
+ return access.getJavaLangObject();
+ }
+
+ @Override
+ protected AbstractEspressoResolvedInstanceType[] getArrayInterfaces() {
+ return access.getArrayInterfaces();
+ }
+
+ @Override
+ protected EspressoResolvedObjectType getObjectType(JavaConstant obj) {
+ return ((EspressoExternalObjectConstant) obj).getType();
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedInstanceType.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedInstanceType.java
new file mode 100644
index 000000000000..4e7fe725d26e
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedInstanceType.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.util.List;
+
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedArrayType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaField;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaMethod;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaRecordComponent;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedObjectType;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+final class EspressoExternalResolvedInstanceType extends AbstractEspressoResolvedInstanceType {
+ private final EspressoExternalVMAccess access;
+ /**
+ * A handle to an espresso Klass.
+ */
+ private final Value metaObject;
+ private final int flags;
+ private EspressoExternalConstantPool constantPool;
+
+ EspressoExternalResolvedInstanceType(EspressoExternalVMAccess access, Value metaObject) {
+ assert metaObject.isMetaObject();
+ this.metaObject = metaObject;
+ this.flags = access.invokeJVMCIHelper("getFlags", metaObject).asInt();
+ this.access = access;
+ }
+
+ EspressoExternalVMAccess getAccess() {
+ return access;
+ }
+
+ Value getMetaObject() {
+ return metaObject;
+ }
+
+ @Override
+ protected int getFlags() {
+ return flags;
+ }
+
+ @Override
+ protected EspressoExternalResolvedInstanceType[] getArrayInterfaces() {
+ return access.getArrayInterfaces();
+ }
+
+ @Override
+ protected boolean isAssignableFrom(AbstractEspressoResolvedInstanceType other) {
+ return access.invokeJVMCIHelper("isAssignableFrom", this.getMetaObject(), ((EspressoExternalResolvedInstanceType) other).getMetaObject()).asBoolean();
+ }
+
+ @Override
+ protected EspressoExternalResolvedInstanceType getJavaLangObject() {
+ return access.getJavaLangObject();
+ }
+
+ @Override
+ protected EspressoExternalResolvedInstanceType getSuperclass0() {
+ Value value = metaObject.getMember("super");
+ assert value != null : this;
+ return new EspressoExternalResolvedInstanceType(access, value);
+ }
+
+ @Override
+ protected EspressoExternalResolvedInstanceType[] getInterfaces0() {
+ Value value = access.invokeJVMCIHelper("getInterfaces", getMetaObject());
+ return translateInstanceTypeArray(value);
+ }
+
+ @Override
+ protected AbstractEspressoResolvedJavaRecordComponent[] getRecordComponents0() {
+ throw JVMCIError.unimplemented();
+ }
+
+ private EspressoExternalResolvedInstanceType[] translateInstanceTypeArray(Value value) {
+ if (value.isNull()) {
+ return null;
+ }
+ int size = Math.toIntExact(value.getArraySize());
+ EspressoExternalResolvedInstanceType[] result = new EspressoExternalResolvedInstanceType[size];
+ for (int i = 0; i < size; i++) {
+ result[i] = new EspressoExternalResolvedInstanceType(access, value.getArrayElement(i));
+ }
+ return result;
+ }
+
+ @Override
+ protected EspressoExternalResolvedInstanceType espressoSingleImplementor() {
+ Value result = access.invokeJVMCIHelper("espressoSingleImplementor", getMetaObject());
+ if (result.isNull()) {
+ return null;
+ }
+ return new EspressoExternalResolvedInstanceType(access, result);
+ }
+
+ @Override
+ protected boolean isLeafClass() {
+ return access.invokeJVMCIHelper("isLeafClass", getMetaObject()).asBoolean();
+ }
+
+ @Override
+ protected String getName0() {
+ return access.invokeJVMCIHelper("getName", getMetaObject()).asString();
+ }
+
+ @Override
+ protected boolean hasSameClassLoader(AbstractEspressoResolvedInstanceType otherMirror) {
+ return access.invokeJVMCIHelper("hasSameClassLoader", getMetaObject(), ((EspressoExternalResolvedInstanceType) otherMirror).getMetaObject()).asBoolean();
+ }
+
+ @Override
+ protected EspressoExternalResolvedJavaMethod resolveMethod0(AbstractEspressoResolvedJavaMethod method, AbstractEspressoResolvedInstanceType callerType) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected AbstractEspressoResolvedJavaField[] getStaticFields0() {
+ Value value = access.invokeJVMCIHelper("getStaticFields", metaObject);
+ return translateFieldArray(value);
+ }
+
+ @Override
+ protected AbstractEspressoResolvedJavaField[] getInstanceFields0() {
+ Value value = access.invokeJVMCIHelper("getInstanceFields", metaObject);
+ return translateFieldArray(value);
+ }
+
+ private EspressoExternalResolvedJavaField[] translateFieldArray(Value value) {
+ assert value.hasArrayElements();
+ int size = Math.toIntExact(value.getArraySize());
+ EspressoExternalResolvedJavaField[] result = new EspressoExternalResolvedJavaField[size];
+ for (int i = 0; i < size; i++) {
+ Value fieldMirror = value.getArrayElement(i);
+ result[i] = new EspressoExternalResolvedJavaField(this, fieldMirror);
+ }
+ return result;
+ }
+
+ @Override
+ protected EspressoExternalResolvedJavaMethod[] getDeclaredConstructors0() {
+ Value value = access.invokeJVMCIHelper("getDeclaredConstructors", getMetaObject());
+ return translateDeclaredMethodArray(value);
+ }
+
+ @Override
+ protected EspressoExternalResolvedJavaMethod[] getDeclaredMethods0() {
+ Value value = access.invokeJVMCIHelper("getDeclaredMethods", getMetaObject());
+ return translateDeclaredMethodArray(value);
+ }
+
+ @Override
+ protected EspressoExternalResolvedJavaMethod[] getAllMethods0() {
+ Value value = access.invokeJVMCIHelper("getAllMethods", getMetaObject());
+ return translateMethodArray(value);
+ }
+
+ private EspressoExternalResolvedJavaMethod[] translateDeclaredMethodArray(Value value) {
+ if (value.isNull()) {
+ return EspressoExternalResolvedJavaMethod.EMPTY_ARRAY;
+ }
+ assert value.hasArrayElements();
+ int size = Math.toIntExact(value.getArraySize());
+ EspressoExternalResolvedJavaMethod[] result = new EspressoExternalResolvedJavaMethod[size];
+ for (int i = 0; i < size; i++) {
+ assert value.getArrayElement(i).getMember("holder").equals(getMetaObject());
+ result[i] = new EspressoExternalResolvedJavaMethod(this, value.getArrayElement(i));
+ }
+ return result;
+ }
+
+ private EspressoExternalResolvedJavaMethod[] translateMethodArray(Value value) {
+ if (value.isNull()) {
+ return EspressoExternalResolvedJavaMethod.EMPTY_ARRAY;
+ }
+ assert value.hasArrayElements();
+ int size = Math.toIntExact(value.getArraySize());
+ EspressoExternalResolvedJavaMethod[] result = new EspressoExternalResolvedJavaMethod[size];
+ for (int i = 0; i < size; i++) {
+ Value methodMeta = value.getArrayElement(i);
+ EspressoExternalResolvedInstanceType methodHolder;
+ Value methodHolderMeta = methodMeta.invokeMember("holder");
+ if (metaObject.equals(methodHolderMeta)) {
+ methodHolder = this;
+ } else {
+ methodHolder = new EspressoExternalResolvedInstanceType(getAccess(), methodHolderMeta);
+ }
+ result[i] = new EspressoExternalResolvedJavaMethod(methodHolder, methodMeta);
+ }
+ return result;
+ }
+
+ @Override
+ protected JavaType lookupType(String typeName, AbstractEspressoResolvedInstanceType accessingType, boolean resolve) {
+ return access.lookupType(typeName, accessingType, resolve);
+ }
+
+ @Override
+ public EspressoExternalConstantPool getConstantPool() {
+ if (constantPool == null) {
+ constantPool = new EspressoExternalConstantPool(this);
+ }
+ return constantPool;
+ }
+
+ @Override
+ protected boolean equals0(AbstractEspressoResolvedInstanceType that) {
+ if (that instanceof EspressoExternalResolvedInstanceType thatInstanceType) {
+ return metaObject.equals(thatInstanceType.metaObject);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return metaObject.hashCode();
+ }
+
+ @Override
+ protected Class> getMirror0() {
+ throw JVMCIError.shouldNotReachHere("Mirrors cannot be accessed for external JVMCI");
+ }
+
+ @Override
+ protected AbstractEspressoResolvedArrayType getArrayClass0() {
+ return new EspressoExternalResolvedArrayType(this, 1, this, access);
+ }
+
+ @Override
+ public boolean isInitialized() {
+ return access.invokeJVMCIHelper("isInitialized", getMetaObject()).asBoolean();
+ }
+
+ @Override
+ public void initialize() {
+ access.invokeJVMCIHelper("initialize", metaObject);
+ }
+
+ @Override
+ public boolean isLinked() {
+ return access.invokeJVMCIHelper("isLinked", getMetaObject()).asBoolean();
+ }
+
+ @Override
+ public void link() {
+ access.invokeJVMCIHelper("link", metaObject);
+ }
+
+ @Override
+ public boolean declaresDefaultMethods() {
+ return access.invokeJVMCIHelper("declaresDefaultMethods", getMetaObject()).asBoolean();
+ }
+
+ @Override
+ public boolean isHidden() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public List extends JavaType> getPermittedSubclasses() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public boolean isRecord() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public boolean hasDefaultMethods() {
+ return access.invokeJVMCIHelper("hasDefaultMethods", getMetaObject()).asBoolean();
+ }
+
+ @Override
+ public String getSourceFileName() {
+ return access.invokeJVMCIHelper("getSourceFileName", metaObject).asString();
+ }
+
+ @Override
+ public boolean isLocal() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public boolean isMember() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaType[] getDeclaredTypes() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaType getEnclosingType() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaMethod getEnclosingMethod() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaMethod getClassInitializer() {
+ Value value = access.invokeJVMCIHelper("getClassInitializer", getMetaObject());
+ if (value.isNull()) {
+ return null;
+ }
+ return new EspressoExternalResolvedJavaMethod(this, value);
+ }
+
+ @Override
+ protected byte[] getRawAnnotationBytes(int category) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected int getVtableLength() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected EspressoResolvedObjectType getObjectType(JavaConstant obj) {
+ return ((EspressoExternalObjectConstant) obj).getType();
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java
new file mode 100644
index 000000000000..6c14a8b33f9c
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import org.graalvm.polyglot.Value;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaField;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+
+final class EspressoExternalResolvedJavaField extends AbstractEspressoResolvedJavaField {
+ private final Value fieldMirror;
+ private final int flags;
+
+ EspressoExternalResolvedJavaField(EspressoExternalResolvedInstanceType holder, Value fieldMirror) {
+ super(holder);
+ this.fieldMirror = fieldMirror;
+ this.flags = fieldMirror.getMember("flags").asInt();
+ }
+
+ private EspressoExternalVMAccess getAccess() {
+ return ((EspressoExternalResolvedInstanceType) getDeclaringClass()).getAccess();
+ }
+
+ @Override
+ protected int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public int getOffset() {
+ return fieldMirror.getMember("offset").asInt();
+ }
+
+ @Override
+ protected String getName0() {
+ return fieldMirror.getMember("name").asString();
+ }
+
+ @Override
+ protected JavaType getType0(UnresolvedJavaType unresolved) {
+ String name = fieldMirror.getMember("type").asString();
+ return getAccess().lookupType(name, getDeclaringClass(), false);
+ }
+
+ @Override
+ protected int getConstantValueIndex() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected byte[] getRawAnnotationBytes(int category) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected boolean equals0(AbstractEspressoResolvedJavaField that) {
+ if (that instanceof EspressoExternalResolvedJavaField espressoField) {
+ return fieldMirror.equals(espressoField.fieldMirror);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return fieldMirror.hashCode();
+ }
+
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java
new file mode 100644
index 000000000000..f7de65c358fe
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.lang.reflect.Type;
+
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Value;
+
+import com.oracle.graal.vmaccess.InvocationException;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedJavaMethod;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoSignature;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.ExceptionHandler;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.LineNumberTable;
+import jdk.vm.ci.meta.Local;
+import jdk.vm.ci.meta.LocalVariableTable;
+
+final class EspressoExternalResolvedJavaMethod extends AbstractEspressoResolvedJavaMethod {
+ static final EspressoExternalResolvedJavaMethod[] EMPTY_ARRAY = new EspressoExternalResolvedJavaMethod[0];
+ private static final ExceptionHandler[] NO_HANDLERS = new ExceptionHandler[0];
+ private static final LocalVariableTable EMPTY_LVT = new LocalVariableTable(new Local[0]);
+
+ private final Value methodMirror;
+ private final int flags;
+
+ EspressoExternalResolvedJavaMethod(EspressoExternalResolvedInstanceType holder, Value methodMirror) {
+ super(holder, methodMirror.getMember("hasPoison").asBoolean());
+ this.methodMirror = methodMirror;
+ this.flags = methodMirror.getMember("flags").asInt();
+ }
+
+ Value getMirror() {
+ return methodMirror;
+ }
+
+ private EspressoExternalVMAccess getAccess() {
+ return ((EspressoExternalResolvedInstanceType) getDeclaringClass()).getAccess();
+ }
+
+ @Override
+ protected byte[] getCode0() {
+ Value value = methodMirror.getMember("code");
+ assert !value.isNull() : this;
+ assert value.hasBufferElements() : this + " " + value;
+ int size = Math.toIntExact(value.getBufferSize());
+ byte[] buf = new byte[size];
+ value.readBuffer(0, buf, 0, size);
+ return buf;
+ }
+
+ @Override
+ protected int getCodeSize0() {
+ return methodMirror.getMember("codeSize").asInt();
+ }
+
+ @Override
+ protected String getName0() {
+ return methodMirror.getMember("name").asString();
+ }
+
+ @Override
+ protected AbstractEspressoSignature getSignature0() {
+ return new EspressoExternalSignature(getAccess(), methodMirror.getMember("rawSignature").asString());
+ }
+
+ @Override
+ protected boolean isForceInline() {
+ return false;
+ }
+
+ @Override
+ protected int getVtableIndexForInterfaceMethod(EspressoResolvedInstanceType resolved) {
+ return 0;
+ }
+
+ @Override
+ protected int getVtableIndex() {
+ return 0;
+ }
+
+ @Override
+ public int getMaxLocals() {
+ return methodMirror.getMember("maxLocals").asInt();
+ }
+
+ @Override
+ public int getMaxStackSize() {
+ return methodMirror.getMember("maxStackSize").asInt();
+ }
+
+ @Override
+ public ExceptionHandler[] getExceptionHandlers() {
+ Value handlers = methodMirror.getMember("exceptionHandlers");
+ if (handlers.isNull()) {
+ return NO_HANDLERS;
+ }
+ assert handlers.hasArrayElements();
+ int size = Math.toIntExact(handlers.getArraySize());
+ ExceptionHandler[] result = new ExceptionHandler[size];
+ for (int i = 0; i < size; i++) {
+ Value handler = handlers.getArrayElement(i);
+ int startBCI = handler.getMember("startBCI").asInt();
+ int endBCI = handler.getMember("endBCI").asInt();
+ int handlerBCI = handler.getMember("handlerBCI").asInt();
+ int catchTypeCPI = handler.getMember("catchTypeCPI").asInt();
+ String catchTypeName = handler.getMember("catchType").asString();
+ JavaType catchType = getAccess().lookupType(catchTypeName, getDeclaringClass(), false);
+ result[i] = new ExceptionHandler(startBCI, endBCI, handlerBCI, catchTypeCPI, catchType);
+ }
+ return result;
+ }
+
+ @Override
+ public StackTraceElement asStackTraceElement(int bci) {
+ int line;
+ if (isNative()) {
+ line = -2;
+ } else {
+ LineNumberTable lineNumberTable = getLineNumberTable();
+ if (lineNumberTable == null) {
+ line = -1;
+ } else {
+ line = lineNumberTable.getLineNumber(bci);
+ }
+ }
+ // Currently missing: module and class loader names
+ return new StackTraceElement(getDeclaringClass().getName(), getName(), getDeclaringClass().getSourceFileName(), line);
+ }
+
+ @Override
+ public Type[] getGenericParameterTypes() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public boolean hasNeverInlineDirective() {
+ return methodMirror.getMember("neverInline").asBoolean();
+ }
+
+ @Override
+ public LineNumberTable getLineNumberTable() {
+ Value rawData = methodMirror.getMember("lineNumberTable");
+ if (rawData.isNull()) {
+ return null;
+ }
+ assert rawData.hasArrayElements() && rawData.getArraySize() % 2 == 0;
+ int size = Math.toIntExact(rawData.getArraySize() / 2);
+ int[] lineNumbers = new int[size];
+ int[] bcis = new int[size];
+ assert size * 2L + 1L < Integer.MAX_VALUE;
+ for (int i = 0; i < size; i++) {
+ lineNumbers[i] = rawData.getArrayElement(i * 2L).asInt();
+ bcis[i] = rawData.getArrayElement(i * 2L + 1L).asInt();
+ }
+ return new LineNumberTable(lineNumbers, bcis);
+ }
+
+ @Override
+ public LocalVariableTable getLocalVariableTable() {
+ Value table = methodMirror.getMember("localVariableTable");
+ if (table.isNull()) {
+ return EMPTY_LVT;
+ }
+ assert table.hasArrayElements();
+ int size = Math.toIntExact(table.getArraySize());
+ Local[] result = new Local[size];
+ for (int i = 0; i < size; i++) {
+ Value handler = table.getArrayElement(i);
+ String name = handler.getMember("name").asString();
+ int startBCI = handler.getMember("startBCI").asInt();
+ int endBCI = handler.getMember("endBCI").asInt();
+ int slot = handler.getMember("slot").asInt();
+ String typeName = handler.getMember("catchType").asString();
+ JavaType type = getAccess().lookupType(typeName, getDeclaringClass(), false);
+ result[i] = new Local(name, type, startBCI, endBCI, slot);
+ }
+ return new LocalVariableTable(result);
+ }
+
+ @Override
+ protected int getFlags() {
+ return flags;
+ }
+
+ @Override
+ public Parameter[] getParameters() {
+ if (getSignature().getParameterCount(false) == 0) {
+ return NO_PARAMETERS;
+ }
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public boolean isLeafMethod() {
+ return methodMirror.getMember("leafMethod").asBoolean();
+ }
+
+ @Override
+ protected byte[] getRawAnnotationBytes(int category) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected boolean hasAnnotations() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ protected boolean equals0(AbstractEspressoResolvedJavaMethod that) {
+ if (that instanceof EspressoExternalResolvedJavaMethod espressoMethod) {
+ return this.methodMirror.equals(espressoMethod.methodMirror);
+ }
+ return false;
+ }
+
+ @Override
+ protected int hashCode0() {
+ return methodMirror.hashCode();
+ }
+
+ JavaConstant invoke(JavaConstant receiver, JavaConstant... arguments) {
+ if (isStatic() || isConstructor()) {
+ if (receiver != null) {
+ throw new IllegalArgumentException("For static methods or constructors, the receiver argument must be null");
+ }
+ } else if (receiver == null) {
+ throw new NullPointerException("For instance methods, the receiver argument must not be null");
+ } else if (receiver.isNull()) {
+ throw new IllegalArgumentException("For instance methods, the receiver argument must not represent a null constant");
+ }
+ AbstractEspressoSignature signature = getSignature();
+ int parameterCount = signature.getParameterCount(false);
+ if (parameterCount != arguments.length) {
+ throw new IllegalArgumentException("Expected " + parameterCount + " arguments, got " + arguments.length);
+ }
+ Object[] args = new Object[arguments.length + (isConstructor() || !isStatic() ? 1 : 0)];
+ int outputArgumentOffset = 0;
+ EspressoExternalVMAccess access = getAccess();
+ if (isConstructor()) {
+ EspressoExternalResolvedInstanceType type = (EspressoExternalResolvedInstanceType) getDeclaringClass();
+ args[0] = access.unsafeAllocateInstance(type).getValue();
+ outputArgumentOffset = 1;
+ } else if (!isStatic()) {
+ if (!(receiver instanceof EspressoExternalObjectConstant objectConstant)) {
+ throw new IllegalArgumentException("Bad receiver: expected Object, got " + arguments[0].getJavaKind());
+ }
+ args[0] = objectConstant.getValue();
+ outputArgumentOffset = 1;
+ }
+ for (int i = 0; i < parameterCount; i++) {
+ JavaConstant argument = arguments[i];
+ JavaKind argumentKind = argument.getJavaKind();
+ /*
+ * Perform widening primitive conversions (JLS 5.1.2) in order to implement strict
+ * method invocation conversions (JLS 5.3). Also promote to stack kind.
+ */
+ args[i + outputArgumentOffset] = switch (signature.getParameterKind(i)) {
+ case Boolean -> switch (argumentKind) {
+ case Boolean -> argument.asBoolean() ? 1 : 0;
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Boolean, got " + argumentKind);
+ };
+ case Byte -> switch (argumentKind) {
+ case Byte -> argument.asInt();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Byte, got " + argumentKind);
+ };
+ case Char -> switch (argumentKind) {
+ case Char -> argument.asInt();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Char, got " + argumentKind);
+ };
+ case Short -> switch (argumentKind) {
+ case Short, Byte -> argument.asInt();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Short, got " + argumentKind);
+ };
+ case Int -> switch (argumentKind) {
+ case Int, Char, Short, Byte -> argument.asInt();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Int, got " + argumentKind);
+ };
+ case Long -> switch (argumentKind) {
+ case Long, Int, Char, Short, Byte -> argument.asLong();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Long, got " + argumentKind);
+ };
+ case Float -> switch (argumentKind) {
+ case Long, Int, Char, Short, Byte -> (float) argument.asLong();
+ case Float -> argument.asFloat();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Float, got " + argumentKind);
+ };
+ case Double -> switch (argumentKind) {
+ case Long, Int, Char, Short, Byte -> (double) argument.asLong();
+ case Float -> (double) argument.asFloat();
+ case Double -> argument.asDouble();
+ default ->
+ throw new IllegalArgumentException("Bad argument kind at index " + i + ": expected Double, got " + argumentKind);
+ };
+ case Object -> {
+ if (argument.isNull()) {
+ yield null;
+ }
+ if (!(argument instanceof EspressoExternalObjectConstant objectConstant)) {
+ throw new IllegalArgumentException(
+ "Bad argument kind at index " + i + ": expected Object, got " + argumentKind + " wrapped in a " + argument.getClass().getName());
+ }
+ yield objectConstant.getValue();
+ }
+ default -> JVMCIError.shouldNotReachHere(signature.getParameterKind(i).toString());
+ };
+ }
+ Value result;
+ try {
+ result = methodMirror.execute(args);
+ } catch (PolyglotException e) {
+ throw new InvocationException(new EspressoExternalObjectConstant(access, e.getGuestObject()), e);
+ }
+ if (isConstructor()) {
+ return new EspressoExternalObjectConstant(access, (Value) args[0]);
+ }
+ JavaKind returnKind = signature.getReturnKind();
+ if (returnKind == JavaKind.Void) {
+ return null;
+ }
+ return EspressoExternalConstantReflectionProvider.asJavaConstant(result, returnKind, access);
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedPrimitiveType.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedPrimitiveType.java
new file mode 100644
index 000000000000..c0303e5d460a
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedPrimitiveType.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedArrayType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedPrimitiveType;
+
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+
+final class EspressoExternalResolvedPrimitiveType extends AbstractEspressoResolvedPrimitiveType {
+ private final EspressoExternalVMAccess access;
+
+ EspressoExternalResolvedPrimitiveType(EspressoExternalVMAccess access, JavaKind kind) {
+ super(kind);
+ this.access = access;
+ }
+
+ @Override
+ protected AbstractEspressoResolvedArrayType getArrayClass0() {
+ return new EspressoExternalResolvedArrayType(this, 1, this, access);
+ }
+
+ @Override
+ public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
+ JavaType javaType = access.lookupType(unresolvedJavaType.getName(), access.getJavaLangObject(), resolve);
+ if (javaType instanceof ResolvedJavaType resolved) {
+ return resolved;
+ }
+ return null;
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSignature.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSignature.java
new file mode 100644
index 000000000000..dd61f0655d96
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSignature.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedPrimitiveType;
+import com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoSignature;
+
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+
+final class EspressoExternalSignature extends AbstractEspressoSignature {
+ private final EspressoExternalVMAccess access;
+
+ EspressoExternalSignature(EspressoExternalVMAccess access, String rawSignature) {
+ super(rawSignature);
+ this.access = access;
+ }
+
+ @Override
+ protected JavaType lookupType0(String descriptor, AbstractEspressoResolvedInstanceType accessingClass) {
+ return access.lookupType(descriptor, accessingClass, false);
+ }
+
+ @Override
+ protected AbstractEspressoResolvedPrimitiveType lookupPrimitiveType0(JavaKind kind) {
+ return access.forPrimitiveKind(kind);
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSnippetReflectionProvider.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSnippetReflectionProvider.java
new file mode 100644
index 000000000000..57e195f7a4d4
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSnippetReflectionProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.util.Objects;
+
+import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
+import jdk.graal.compiler.debug.GraalError;
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+
+final class EspressoExternalSnippetReflectionProvider implements SnippetReflectionProvider {
+ @Override
+ public JavaConstant forObject(Object object) {
+ throw JVMCIError.shouldNotReachHere("Cannot create JavaConstant for external JVMCI");
+ }
+
+ @Override
+ public T asObject(Class type, JavaConstant constant) {
+ if (constant.isNull()) {
+ return null;
+ }
+ throw JVMCIError.shouldNotReachHere("Cannot extract object for external JVMCI");
+ }
+
+ @Override
+ public T getInjectedNodeIntrinsicParameter(Class type) {
+ throw GraalError.unimplementedOverride();
+ }
+
+ @Override
+ public Class> originalClass(ResolvedJavaType type) {
+ Objects.requireNonNull(type);
+ throw JVMCIError.shouldNotReachHere("Cannot extract class for external JVMCI");
+ }
+
+ @Override
+ public Executable originalMethod(ResolvedJavaMethod method) {
+ Objects.requireNonNull(method);
+ throw JVMCIError.shouldNotReachHere("Cannot extract method for external JVMCI");
+ }
+
+ @Override
+ public Field originalField(ResolvedJavaField field) {
+ Objects.requireNonNull(field);
+ throw JVMCIError.shouldNotReachHere("Cannot extract field for external JVMCI");
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccess.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccess.java
new file mode 100644
index 000000000000..fd95d3279b25
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccess.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.util.Objects;
+
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Value;
+
+import com.oracle.graal.vmaccess.InvocationException;
+import com.oracle.graal.vmaccess.VMAccess;
+import com.oracle.truffle.espresso.graal.DummyForeignCallsProvider;
+import com.oracle.truffle.espresso.graal.DummyLoweringProvider;
+import com.oracle.truffle.espresso.graal.DummyPlatformConfigurationProvider;
+import com.oracle.truffle.espresso.graal.DummyReplacements;
+import com.oracle.truffle.espresso.graal.DummyStampProvider;
+import com.oracle.truffle.espresso.graal.EspressoConstantFieldProvider;
+import com.oracle.truffle.espresso.graal.EspressoMetaAccessExtensionProvider;
+import com.oracle.truffle.espresso.jvmci.DummyCodeCacheProvider;
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaType;
+
+import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
+import jdk.graal.compiler.core.common.spi.ConstantFieldProvider;
+import jdk.graal.compiler.core.common.spi.ForeignCallsProvider;
+import jdk.graal.compiler.core.common.spi.MetaAccessExtensionProvider;
+import jdk.graal.compiler.nodes.loop.LoopsDataProviderImpl;
+import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider;
+import jdk.graal.compiler.nodes.spi.LoopsDataProvider;
+import jdk.graal.compiler.nodes.spi.LoweringProvider;
+import jdk.graal.compiler.nodes.spi.PlatformConfigurationProvider;
+import jdk.graal.compiler.nodes.spi.Replacements;
+import jdk.graal.compiler.nodes.spi.StampProvider;
+import jdk.graal.compiler.phases.util.Providers;
+import jdk.graal.compiler.word.WordTypes;
+import jdk.vm.ci.code.CodeCacheProvider;
+import jdk.vm.ci.code.TargetDescription;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.Signature;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+
+/**
+ * A {@link VMAccess} implementation that reflects on the state of an espresso VM in a Polyglot
+ * {@link Context}.
+ */
+final class EspressoExternalVMAccess implements VMAccess {
+ private final Context context;
+ private final EspressoExternalResolvedPrimitiveType[] primitives;
+ private final EspressoExternalMetaAccessProvider metaAccess;
+ private final EspressoExternalConstantReflectionProvider constantReflection;
+ private final MetaAccessExtensionProvider metaAccessExtensionProvider;
+ private final Value jvmciHelper;
+ private final EspressoExternalResolvedInstanceType javaLangObject;
+ private final EspressoExternalResolvedInstanceType[] arrayInterfaces;
+ private final Providers providers;
+ private final JavaConstant platformClassLoader;
+ private final ResolvedJavaMethod forName;
+ private final ResolvedJavaMethod unsafeAllocateInstance;
+ private final JavaConstant unsafe;
+ private final ResolvedJavaType classNotFoundExceptionType;
+ private JavaConstant systemClassLoader;
+
+ @SuppressWarnings("this-escape")
+ EspressoExternalVMAccess(Context context) {
+ this.context = context;
+ Value bindings = context.getBindings("java");
+ jvmciHelper = bindings.getMember("");
+ bindings.removeMember("");
+ metaAccess = new EspressoExternalMetaAccessProvider(this);
+ constantReflection = new EspressoExternalConstantReflectionProvider(this);
+ metaAccessExtensionProvider = new EspressoMetaAccessExtensionProvider(constantReflection);
+ primitives = createPrimitiveTypes();
+ javaLangObject = new EspressoExternalResolvedInstanceType(this, lookupMetaObject(context, "java.lang.Object"));
+ arrayInterfaces = new EspressoExternalResolvedInstanceType[]{
+ new EspressoExternalResolvedInstanceType(this, lookupMetaObject(context, "java.io.Serializable")),
+ new EspressoExternalResolvedInstanceType(this, lookupMetaObject(context, "java.lang.Cloneable")),
+ };
+ providers = createProviders();
+
+ ResolvedJavaType classLoaderType = providers.getMetaAccess().lookupJavaType(ClassLoader.class);
+ Signature classLoaderGetterSignature = providers.getMetaAccess().parseMethodDescriptor("()Ljava/lang/ClassLoader;");
+ ResolvedJavaMethod getPlatformClassLoader = classLoaderType.findMethod("getPlatformClassLoader", classLoaderGetterSignature);
+ platformClassLoader = invoke(getPlatformClassLoader, null);
+
+ ResolvedJavaType classType = providers.getMetaAccess().lookupJavaType(Class.class);
+ Signature forNameSignature = providers.getMetaAccess().parseMethodDescriptor("(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
+ forName = classType.findMethod("forName", forNameSignature);
+ classNotFoundExceptionType = providers.getMetaAccess().lookupJavaType(ClassNotFoundException.class);
+
+ ResolvedJavaType unsafeType = lookupBootClassLoaderType("jdk.internal.misc.Unsafe");
+ unsafeAllocateInstance = unsafeType.findMethod("allocateInstance", providers.getMetaAccess().parseMethodDescriptor("(Ljava/lang/Class;)Ljava/lang/Object;"));
+ assert unsafeAllocateInstance != null;
+ ResolvedJavaMethod unsafeGetter = unsafeType.findMethod("getUnsafe", providers.getMetaAccess().parseMethodDescriptor("()Ljdk/internal/misc/Unsafe;"));
+ unsafe = invoke(unsafeGetter, null);
+ }
+
+ private EspressoExternalResolvedPrimitiveType[] createPrimitiveTypes() {
+ EspressoExternalResolvedPrimitiveType[] prims = new EspressoExternalResolvedPrimitiveType[JavaKind.Void.getBasicType() + 1];
+ prims[JavaKind.Boolean.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Boolean);
+ prims[JavaKind.Byte.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Byte);
+ prims[JavaKind.Short.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Short);
+ prims[JavaKind.Char.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Char);
+ prims[JavaKind.Int.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Int);
+ prims[JavaKind.Float.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Float);
+ prims[JavaKind.Long.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Long);
+ prims[JavaKind.Double.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Double);
+ prims[JavaKind.Void.getBasicType()] = new EspressoExternalResolvedPrimitiveType(this, JavaKind.Void);
+ return prims;
+ }
+
+ private Providers createProviders() {
+ TargetDescription target = DummyCodeCacheProvider.getHostTarget();
+ CodeCacheProvider codeCache = new DummyCodeCacheProvider(target);
+ ConstantFieldProvider constantFieldProvider = new EspressoConstantFieldProvider(metaAccess);
+ ForeignCallsProvider foreignCalls = new DummyForeignCallsProvider();
+ LoweringProvider lowerer = new DummyLoweringProvider(target);
+ StampProvider stampProvider = new DummyStampProvider();
+ PlatformConfigurationProvider platformConfigurationProvider = new DummyPlatformConfigurationProvider();
+ SnippetReflectionProvider snippetReflection = new EspressoExternalSnippetReflectionProvider();
+ WordTypes wordTypes = new WordTypes(metaAccess, target.wordJavaKind);
+ LoopsDataProvider loopsDataProvider = new LoopsDataProviderImpl();
+ IdentityHashCodeProvider identityHashCodeProvider = new EspressoExternalIdentityHashCodeProvider();
+ Providers newProviders = new Providers(metaAccess, codeCache, constantReflection, constantFieldProvider, foreignCalls,
+ lowerer, null, stampProvider, platformConfigurationProvider, metaAccessExtensionProvider, snippetReflection,
+ wordTypes, loopsDataProvider, identityHashCodeProvider);
+ Replacements replacements = new DummyReplacements(newProviders);
+ return (Providers) replacements.getProviders();
+ }
+
+ @Override
+ public ResolvedJavaType lookupAppClassLoaderType(String name) {
+ if (systemClassLoader == null) {
+ ResolvedJavaType classLoaderType = providers.getMetaAccess().lookupJavaType(ClassLoader.class);
+ Signature classLoaderGetterSignature = providers.getMetaAccess().parseMethodDescriptor("()Ljava/lang/ClassLoader;");
+ ResolvedJavaMethod getSystemClassLoader = classLoaderType.findMethod("getSystemClassLoader", classLoaderGetterSignature);
+ systemClassLoader = invoke(getSystemClassLoader, null);
+ }
+ return lookupType(name, systemClassLoader);
+ }
+
+ @Override
+ public ResolvedJavaType lookupPlatformClassLoaderType(String name) {
+ return lookupType(name, platformClassLoader);
+ }
+
+ @Override
+ public ResolvedJavaType lookupBootClassLoaderType(String name) {
+ return lookupType(name, JavaConstant.NULL_POINTER);
+ }
+
+ private ResolvedJavaType lookupType(String name, JavaConstant classLoader) {
+ JavaConstant nameConstant = constantReflection.forString(name);
+ JavaConstant cls;
+ try {
+ cls = invoke(forName, null, nameConstant, JavaConstant.FALSE, classLoader);
+ assert !cls.isNull();
+ } catch (InvocationException e) {
+ JavaConstant exceptionObject = e.getExceptionObject();
+ if (classNotFoundExceptionType.isInstance(exceptionObject)) {
+ return null;
+ }
+ throw e;
+ }
+ return constantReflection.asJavaType(cls);
+ }
+
+ @Override
+ public Providers getProviders() {
+ return providers;
+ }
+
+ @Override
+ public JavaConstant invoke(ResolvedJavaMethod method, JavaConstant receiver, JavaConstant... arguments) {
+ if (!(method instanceof EspressoExternalResolvedJavaMethod espressoMethod)) {
+ throw new IllegalArgumentException("Expected an EspressoExternalResolvedJavaMethod");
+ }
+ return espressoMethod.invoke(receiver, arguments);
+ }
+
+ static RuntimeException throwHostException(PolyglotException e) {
+ if (!e.isGuestException()) {
+ throw e;
+ }
+ Value guestException = e.getGuestObject();
+ if (guestException == null || guestException.isNull()) {
+ throw e;
+ }
+ Value guestExceptionMetaobject = guestException.getMetaObject();
+ if (guestExceptionMetaobject == null || guestExceptionMetaobject.isNull()) {
+ throw e;
+ }
+ String guestExceptionQualifiedType = guestExceptionMetaobject.getMetaQualifiedName();
+ Throwable t = switch (guestExceptionQualifiedType) {
+ case "java.lang.IndexOutOfBoundsException" -> new IndexOutOfBoundsException(e.getMessage());
+ case "java.lang.IllegalArgumentException" -> new IllegalArgumentException(e.getMessage());
+ case "java.lang.ClassFormatError" -> new ClassFormatError(e.getMessage());
+ default -> e;
+ };
+ if (t != e) {
+ t.initCause(e);
+ }
+ throw sneakyThrow(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static RuntimeException sneakyThrow(Throwable ex) throws T {
+ throw (T) ex;
+ }
+
+ Value getPrimitiveClass(JavaKind kind) {
+ return switch (kind) {
+ case Boolean -> getPrimitiveClass("Boolean");
+ case Byte -> getPrimitiveClass("Byte");
+ case Short -> getPrimitiveClass("Short");
+ case Char -> getPrimitiveClass("Character");
+ case Int -> getPrimitiveClass("Integer");
+ case Float -> getPrimitiveClass("Float");
+ case Long -> getPrimitiveClass("Long");
+ case Double -> getPrimitiveClass("Double");
+ case Void -> getPrimitiveClass("Void");
+ default -> throw new IllegalArgumentException("Bad primitive kind: " + kind);
+ };
+ }
+
+ private Value getPrimitiveClass(String boxName) {
+ String identifier = "java.lang." + boxName;
+ Value result = lookupMetaObject(context, identifier).getMember("TYPE");
+ assert result != null && !result.isNull() : "Couldn't find TYPE for " + identifier;
+ assert result.getMember("static").isMetaObject() : result;
+ return result;
+ }
+
+ private static Value lookupMetaObject(Context context, String name) {
+ Value result = context.getBindings("java").getMember(name);
+ assert result != null && !result.isNull() : " Couldn't find " + name;
+ assert result.isMetaObject() : result;
+ return result;
+ }
+
+ Value lookupMetaObject(String name) {
+ return lookupMetaObject(context, name);
+ }
+
+ JavaType lookupType(String name, ResolvedJavaType accessingClass, boolean resolve) {
+ Objects.requireNonNull(accessingClass);
+ Objects.requireNonNull(name);
+ if (!(accessingClass instanceof EspressoExternalResolvedInstanceType accessingInstantType)) {
+ throw new IllegalArgumentException("Expected an espresso instance type as accessing class");
+ }
+ return lookupType(name, accessingInstantType, resolve);
+ }
+
+ EspressoExternalResolvedInstanceType getJavaLangObject() {
+ return javaLangObject;
+ }
+
+ EspressoExternalResolvedInstanceType[] getArrayInterfaces() {
+ return arrayInterfaces;
+ }
+
+ JavaType lookupType(String name, EspressoExternalResolvedInstanceType accessingClass, boolean resolve) {
+ assert !name.isEmpty();
+ if (name.charAt(0) == '[') {
+ int dims = 0;
+ do {
+ dims++;
+ } while (dims < name.length() && name.charAt(dims) == '[');
+ if (dims >= name.length()) {
+ throw new IllegalArgumentException("Invalid type: " + name);
+ }
+ JavaType javaType = lookupNonArrayType(name.substring(dims), accessingClass, resolve);
+ if (javaType instanceof EspressoResolvedJavaType resolved) {
+ return new EspressoExternalResolvedArrayType(resolved, dims, this);
+ }
+ if (resolve) {
+ throw new NoClassDefFoundError(name);
+ }
+ return UnresolvedJavaType.create(name);
+ }
+ return lookupNonArrayType(name, accessingClass, resolve);
+ }
+
+ private JavaType lookupNonArrayType(String name, EspressoExternalResolvedInstanceType accessingClass, boolean resolve) {
+ assert !name.isEmpty() && name.charAt(0) != '[';
+ if (name.length() == 1) {
+ JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
+ return forPrimitiveKind(kind);
+ }
+ assert name.charAt(0) == 'L' && name.charAt(name.length() - 1) == ';';
+ Value meta = invokeJVMCIHelper("lookupInstanceType", name, accessingClass.getMetaObject(), resolve);
+ assert meta != null;
+ if (meta.isNull()) {
+ assert !resolve;
+ return UnresolvedJavaType.create(name);
+ }
+ return new EspressoExternalResolvedInstanceType(this, meta);
+ }
+
+ EspressoExternalResolvedPrimitiveType forPrimitiveKind(JavaKind kind) {
+ if (!kind.isPrimitive()) {
+ throw new IllegalArgumentException("Not a primitive kind: " + kind);
+ }
+ return forPrimitiveBasicType(kind.getBasicType());
+ }
+
+ private EspressoExternalResolvedPrimitiveType forPrimitiveBasicType(int basicType) {
+ if (primitives[basicType] == null) {
+ throw new IllegalArgumentException("No primitive type for basic type " + basicType);
+ }
+ return primitives[basicType];
+ }
+
+ Value invokeJVMCIHelper(String method, Object... args) {
+ return jvmciHelper.invokeMember(method, args);
+ }
+
+ JavaType toJavaType(Value value) {
+ if (value.isNull()) {
+ return null;
+ }
+ if (value.isString()) {
+ return UnresolvedJavaType.create(value.asString());
+ }
+ return toResolvedJavaType(value);
+ }
+
+ private EspressoResolvedJavaType toResolvedJavaType(Value value) {
+ // See com.oracle.truffle.espresso.impl.jvmci.external.TypeWrapper
+ assert !value.isNull();
+ assert !value.isString();
+ char kindChar = (char) value.getMember("kind").asInt();
+ if (kindChar == '[') {
+ EspressoResolvedJavaType elemental = toResolvedJavaType(value.getMember("elemental"));
+ int dimensions = value.getMember("dimensions").asInt();
+ return new EspressoExternalResolvedArrayType(elemental, dimensions, this);
+ } else if (kindChar == 'A') {
+ // JVMCI and Espresso JavaKind return `A` as the object type char
+ return new EspressoExternalResolvedInstanceType(this, value.getMember("meta"));
+ } else {
+ return forPrimitiveKind(JavaKind.fromPrimitiveOrVoidTypeChar(kindChar));
+ }
+ }
+
+ public EspressoExternalObjectConstant unsafeAllocateInstance(EspressoExternalResolvedInstanceType type) {
+ return (EspressoExternalObjectConstant) invoke(unsafeAllocateInstance, unsafe, getProviders().getConstantReflection().asJavaClass(type));
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccessBuilder.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccessBuilder.java
new file mode 100644
index 000000000000..a9213515560e
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccessBuilder.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.graalvm.polyglot.Context;
+import org.graalvm.polyglot.PolyglotException;
+import org.graalvm.polyglot.Value;
+
+import com.oracle.graal.vmaccess.ModuleSupport;
+import com.oracle.graal.vmaccess.VMAccess;
+
+public final class EspressoExternalVMAccessBuilder implements VMAccess.Builder {
+ private List classpath;
+ private List modulepath;
+ private List addModules;
+ private boolean enableAssertions;
+ private boolean enableSystemAssertions;
+ private Map systemProperties;
+ private List vmOptions;
+
+ @Override
+ public String getVMAccessName() {
+ return "espresso-context";
+ }
+
+ @Override
+ public VMAccess.Builder classPath(List paths) {
+ this.classpath = paths;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder modulePath(List paths) {
+ this.modulepath = paths;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder addModules(List modules) {
+ this.addModules = modules;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder enableAssertions(boolean assertionStatus) {
+ this.enableAssertions = assertionStatus;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder enableSystemAssertions(boolean assertionStatus) {
+ this.enableSystemAssertions = assertionStatus;
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder systemProperty(String name, String value) {
+ if (systemProperties == null) {
+ systemProperties = new HashMap<>();
+ }
+ systemProperties.put(name, value);
+ return this;
+ }
+
+ @Override
+ public VMAccess.Builder vmOption(String option) {
+ if (vmOptions == null) {
+ vmOptions = new ArrayList<>();
+ }
+ vmOptions.add(option);
+ return this;
+ }
+
+ @Override
+ public VMAccess build() {
+ ModuleAccess.ensureModuleAccess();
+ Context.Builder builder = Context.newBuilder();
+ builder.option("java.ExposeJVMCIHelper", "true");
+ builder.allowAllAccess(true);
+
+ if (classpath != null) {
+ builder.option("java.Classpath", String.join(File.pathSeparator, classpath));
+ }
+ if (modulepath != null) {
+ builder.option("java.ModulePath", String.join(File.pathSeparator, modulepath));
+ }
+ if (addModules != null) {
+ builder.option("java.AddModules", String.join(File.pathSeparator, addModules));
+ }
+ if (systemProperties != null) {
+ systemProperties.forEach((key, value) -> builder.option("java.Properties." + key, value));
+ }
+ builder.option("java.EnableAssertions", Boolean.toString(enableAssertions));
+ builder.option("java.EnableSystemAssertions", Boolean.toString(enableSystemAssertions));
+ if (vmOptions != null && !vmOptions.isEmpty()) {
+ throw new RuntimeException("unimplemented: " + vmOptions);
+ }
+ builder.allowExperimentalOptions(true);
+
+ Context context = builder.build();
+ context.enter();
+ try {
+ context.initialize("java");
+ } catch (PolyglotException e) {
+ if (e.isGuestException()) {
+ Value o = e.getGuestObject();
+ if (o != null) {
+ try {
+ o.invokeMember("printStackTrace");
+ } catch (Throwable t) {
+ // ignore exceptions while trying to print exceptions
+ }
+ }
+ }
+ throw e;
+ }
+ return new EspressoExternalVMAccess(context);
+ }
+
+ private static final class ModuleAccess {
+ static {
+ ModuleSupport.addExports("jdk.internal.vm.ci.espresso", "jdk.internal.vm.ci",
+ "jdk.vm.ci.amd64",
+ "jdk.vm.ci.aarch64",
+ "jdk.vm.ci.code",
+ "jdk.vm.ci.code.stack",
+ "jdk.vm.ci.common",
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.meta.annotation",
+ "jdk.vm.ci.riscv64",
+ "jdk.vm.ci.runtime");
+
+ ModuleSupport.addExports("jdk.graal.compiler.espresso.vmaccess", "jdk.internal.vm.ci",
+ "jdk.vm.ci.meta",
+ "jdk.vm.ci.meta.annotation",
+ "jdk.vm.ci.code",
+ "jdk.vm.ci.code.site",
+ "jdk.vm.ci.code.stack",
+ "jdk.vm.ci.common",
+ "jdk.vm.ci.amd64",
+ "jdk.vm.ci.aarch64",
+ "jdk.vm.ci.services",
+ "jdk.vm.ci.runtime");
+ ModuleSupport.addExports("jdk.graal.compiler.espresso.vmaccess", "jdk.graal.compiler",
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.core.common.spi",
+ "jdk.graal.compiler.debug",
+ "jdk.graal.compiler.nodes.loop",
+ "jdk.graal.compiler.nodes.spi",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.word");
+
+ ModuleSupport.addExports("jdk.graal.compiler.espresso", "jdk.graal.compiler",
+ "jdk.graal.compiler.api.replacements",
+ "jdk.graal.compiler.api.runtime",
+ "jdk.graal.compiler.bytecode",
+ "jdk.graal.compiler.code",
+ "jdk.graal.compiler.core.common",
+ "jdk.graal.compiler.core.common.alloc",
+ "jdk.graal.compiler.core.common.memory",
+ "jdk.graal.compiler.core.common.spi",
+ "jdk.graal.compiler.core.common.type",
+ "jdk.graal.compiler.core.target",
+ "jdk.graal.compiler.debug",
+ "jdk.graal.compiler.graph",
+ "jdk.graal.compiler.nodes",
+ "jdk.graal.compiler.nodes.gc",
+ "jdk.graal.compiler.nodes.graphbuilderconf",
+ "jdk.graal.compiler.nodes.loop",
+ "jdk.graal.compiler.nodes.memory",
+ "jdk.graal.compiler.nodes.memory.address",
+ "jdk.graal.compiler.nodes.spi",
+ "jdk.graal.compiler.options",
+ "jdk.graal.compiler.phases.tiers",
+ "jdk.graal.compiler.phases.util",
+ "jdk.graal.compiler.replacements",
+ "jdk.graal.compiler.runtime",
+ "jdk.graal.compiler.word");
+ }
+
+ static void ensureModuleAccess() {
+ }
+ }
+}
diff --git a/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/KlassConstant.java b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/KlassConstant.java
new file mode 100644
index 000000000000..f72c3741d617
--- /dev/null
+++ b/espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/KlassConstant.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.vmaccess;
+
+import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedObjectType;
+
+import jdk.vm.ci.meta.VMConstant;
+
+final class KlassConstant implements VMConstant {
+ private final EspressoResolvedObjectType type;
+
+ KlassConstant(EspressoResolvedObjectType type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean isDefaultForKind() {
+ return false;
+ }
+
+ @Override
+ public String toValueString() {
+ return "KlassConstant<" + type.getName() + '>';
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ KlassConstant other = (KlassConstant) obj;
+ return this.type.equals(other.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode() * 13;
+ }
+
+ EspressoResolvedObjectType getType() {
+ return type;
+ }
+}
diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/attributes/LineNumberTableAttribute.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/attributes/LineNumberTableAttribute.java
index 49cf7a7a8a7e..1ae21d322ab4 100644
--- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/attributes/LineNumberTableAttribute.java
+++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/attributes/LineNumberTableAttribute.java
@@ -53,6 +53,14 @@ public LineNumberTableAttribute(Symbol name, char[] entries) {
this.bciToLineEntries = entries;
}
+ /**
+ * Get the raw data organized in a similar way to the class file format (see JVMS 4.7.12). There
+ * are 2 consecutive chars per entries: the start PC followed by the line number.
+ */
+ public char[] getRawData() {
+ return bciToLineEntries;
+ }
+
@Override
public List getEntries() {
return new ListWrapper();
diff --git a/espresso/mx.espresso/suite.py b/espresso/mx.espresso/suite.py
index a76dba20f9d5..e85a1f6cb84f 100644
--- a/espresso/mx.espresso/suite.py
+++ b/espresso/mx.espresso/suite.py
@@ -267,6 +267,7 @@
"jdk.vm.ci.common",
"jdk.vm.ci.meta",
"jdk.vm.ci.meta.annotation",
+ "jdk.vm.ci.riscv64",
"jdk.vm.ci.runtime",
],
},
@@ -1012,13 +1013,14 @@
"moduleInfo": {
"name": "jdk.internal.vm.ci.espresso",
"exports": [
- "com.oracle.truffle.espresso.jvmci,com.oracle.truffle.espresso.jvmci.meta to jdk.graal.compiler.espresso",
- ]
+ "com.oracle.truffle.espresso.jvmci,com.oracle.truffle.espresso.jvmci.meta to jdk.graal.compiler.espresso,jdk.graal.compiler.espresso.vmaccess",
+ ],
},
"dependencies": [
"com.oracle.truffle.espresso.jvmci",
],
"description": "JVMCI implementation for Espresso",
+ "useModulePath": True,
"maven": False,
},
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/DummyCodeCacheProvider.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/DummyCodeCacheProvider.java
index e15e2cc3bfa4..dbc29ad3b5ee 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/DummyCodeCacheProvider.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/DummyCodeCacheProvider.java
@@ -22,6 +22,29 @@
*/
package com.oracle.truffle.espresso.jvmci;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.CMOV;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.CX8;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.FXSR;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.MMX;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.POPCNT;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSE;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSE2;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSE3;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSE4_1;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSE4_2;
+import static jdk.vm.ci.amd64.AMD64.CPUFeature.SSSE3;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.A;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.C;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.D;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.F;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.I;
+import static jdk.vm.ci.riscv64.RISCV64.CPUFeature.M;
+
+import java.util.EnumSet;
+
+import jdk.vm.ci.aarch64.AArch64;
+import jdk.vm.ci.amd64.AMD64;
+import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.CodeCacheProvider;
import jdk.vm.ci.code.CompiledCode;
import jdk.vm.ci.code.InstalledCode;
@@ -30,6 +53,7 @@
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.SpeculationLog;
+import jdk.vm.ci.riscv64.RISCV64;
public final class DummyCodeCacheProvider implements CodeCacheProvider {
private final TargetDescription target;
@@ -77,4 +101,27 @@ public long getMaxCallTargetOffset(long address) {
public boolean shouldDebugNonSafepoints() {
throw JVMCIError.unimplemented();
}
+
+ /**
+ * Returns a {@link TargetDescription} that matches the host's architecture.
+ *
+ * Note that beyond the general ISA type (AMD64, AArch64, ...) details of the actual host target
+ * are currently not accurate.
+ */
+ public static TargetDescription getHostTarget() {
+ String archString = System.getProperty("os.arch");
+ Architecture arch = switch (archString) {
+ case "amd64", "x86_64" -> {
+ EnumSet x8664v2 = EnumSet.of(CMOV, CX8, FXSR, MMX, SSE, SSE2, POPCNT, SSE3, SSE4_1, SSE4_2, SSSE3);
+ yield new AMD64(x8664v2);
+ }
+ case "aarch64", "arm64" -> new AArch64(EnumSet.of(AArch64.CPUFeature.FP));
+ case "riscv64" -> {
+ EnumSet imacfd = EnumSet.of(I, M, A, C, F, D);
+ yield new RISCV64(imacfd);
+ }
+ default -> throw JVMCIError.unimplemented(archString);
+ };
+ return new TargetDescription(arch, true, 16, 4096, true);
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/EspressoJVMCIRuntime.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/EspressoJVMCIRuntime.java
index 3912c65f8dc5..94a949dc3706 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/EspressoJVMCIRuntime.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/EspressoJVMCIRuntime.java
@@ -25,18 +25,14 @@
import static jdk.vm.ci.common.InitTimer.timer;
import java.io.Serializable;
-import java.util.EnumSet;
import com.oracle.truffle.espresso.jvmci.meta.EspressoConstantReflectionProvider;
import com.oracle.truffle.espresso.jvmci.meta.EspressoMetaAccessProvider;
import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType;
import com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedJavaMethod;
-import jdk.vm.ci.aarch64.AArch64;
-import jdk.vm.ci.amd64.AMD64;
import jdk.vm.ci.code.Architecture;
import jdk.vm.ci.code.CodeCacheProvider;
-import jdk.vm.ci.code.TargetDescription;
import jdk.vm.ci.code.stack.StackIntrospection;
import jdk.vm.ci.common.InitTimer;
import jdk.vm.ci.common.JVMCIError;
@@ -57,7 +53,7 @@ public final class EspressoJVMCIRuntime implements JVMCIRuntime {
private EspressoJVMCIRuntime() {
EspressoMetaAccessProvider metaAccess = new EspressoMetaAccessProvider();
- CodeCacheProvider codeCache = new DummyCodeCacheProvider(getHostTarget());
+ CodeCacheProvider codeCache = new DummyCodeCacheProvider(DummyCodeCacheProvider.getHostTarget());
ConstantReflectionProvider constantReflection = new EspressoConstantReflectionProvider(metaAccess);
StackIntrospection stackIntrospection = new DummyStackIntrospection();
hostBackend = new JVMCIBackend(metaAccess, codeCache, constantReflection, stackIntrospection);
@@ -68,26 +64,6 @@ private EspressoJVMCIRuntime() {
};
}
- private static TargetDescription getHostTarget() {
- String archString = System.getProperty("os.arch");
- Architecture arch;
- switch (archString) {
- case "amd64":
- case "x86_64":
- EnumSet x8664v2 = EnumSet.of(AMD64.CPUFeature.CMOV, AMD64.CPUFeature.CX8, AMD64.CPUFeature.FXSR, AMD64.CPUFeature.MMX, AMD64.CPUFeature.SSE, AMD64.CPUFeature.SSE2,
- AMD64.CPUFeature.POPCNT, AMD64.CPUFeature.SSE3, AMD64.CPUFeature.SSE4_1, AMD64.CPUFeature.SSE4_2, AMD64.CPUFeature.SSSE3);
- arch = new AMD64(x8664v2);
- break;
- case "aarch64":
- case "arm64":
- arch = new AArch64(EnumSet.of(AArch64.CPUFeature.FP));
- break;
- default:
- throw JVMCIError.unimplemented(archString);
- }
- return new TargetDescription(arch, true, 16, 4096, true);
- }
-
private native JVMCICompiler createEspressoGraalJVMCICompiler();
// used by the VM
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoConstantPool.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoConstantPool.java
new file mode 100644
index 000000000000..e79073b9bbd8
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoConstantPool.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.vm.ci.meta.ConstantPool;
+import jdk.vm.ci.meta.JavaField;
+import jdk.vm.ci.meta.JavaMethod;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.Signature;
+import jdk.vm.ci.meta.UnresolvedJavaField;
+import jdk.vm.ci.meta.UnresolvedJavaMethod;
+
+public abstract class AbstractEspressoConstantPool implements ConstantPool {
+ // @formatter:off
+ protected static final byte CONSTANT_Invalid = 0;
+ protected static final byte CONSTANT_Utf8 = 1;
+ protected static final byte CONSTANT_Integer = 3;
+ protected static final byte CONSTANT_Float = 4;
+ protected static final byte CONSTANT_Long = 5;
+ protected static final byte CONSTANT_Double = 6;
+ protected static final byte CONSTANT_Class = 7;
+ protected static final byte CONSTANT_String = 8;
+ protected static final byte CONSTANT_Fieldref = 9;
+ protected static final byte CONSTANT_Methodref = 10;
+ protected static final byte CONSTANT_InterfaceMethodref = 11;
+ protected static final byte CONSTANT_NameAndType = 12;
+ protected static final byte CONSTANT_MethodHandle = 15;
+ protected static final byte CONSTANT_MethodType = 16;
+ protected static final byte CONSTANT_Dynamic = 17;
+ protected static final byte CONSTANT_InvokeDynamic = 18;
+ protected static final byte CONSTANT_Module = 19;
+ protected static final byte CONSTANT_Package = 20;
+ // @formatter:on
+
+ static final int INVOKEDYNAMIC = 186;
+
+ @Override
+ public final void loadReferencedType(int cpi, int opcode) {
+ loadReferencedType(cpi, opcode, true);
+ }
+
+ @Override
+ public final void loadReferencedType(int cpi, int opcode, boolean initialize) {
+ if (!loadReferencedType0(cpi, opcode)) {
+ return;
+ }
+ if (initialize) {
+ EspressoResolvedJavaType type = (EspressoResolvedJavaType) lookupReferencedType(cpi, opcode);
+ type.initialize();
+ }
+ }
+
+ protected abstract boolean loadReferencedType0(int cpi, int opcode);
+
+ @Override
+ public final JavaField lookupField(int cpi, ResolvedJavaMethod method, int opcode) {
+ AbstractEspressoResolvedJavaField field = lookupResolvedField(cpi, (AbstractEspressoResolvedJavaMethod) method, opcode);
+ if (field != null) {
+ return field;
+ }
+ String name = lookupName(cpi);
+ JavaType type = lookupFieldType(cpi, ((AbstractEspressoResolvedJavaMethod) method).getDeclaringClass());
+ JavaType fieldHolder = lookupReferencedType(cpi, opcode);
+ return new UnresolvedJavaField(fieldHolder, name, type);
+ }
+
+ protected abstract AbstractEspressoResolvedJavaField lookupResolvedField(int cpi, AbstractEspressoResolvedJavaMethod method, int opcode);
+
+ @Override
+ public final JavaMethod lookupMethod(int cpi, int opcode, ResolvedJavaMethod caller) {
+ AbstractEspressoResolvedJavaMethod method = lookupResolvedMethod(cpi, opcode, (AbstractEspressoResolvedJavaMethod) caller);
+ if (method != null) {
+ return method;
+ }
+ String name = lookupName(cpi);
+ String rawSignature = lookupDescriptor(cpi);
+ JavaType methodHolder;
+ if (opcode == INVOKEDYNAMIC) {
+ methodHolder = getMethodHandleType();
+ } else {
+ methodHolder = lookupReferencedType(cpi, opcode);
+ }
+ return new UnresolvedJavaMethod(name, getSignature(rawSignature), methodHolder);
+ }
+
+ protected abstract AbstractEspressoSignature getSignature(String rawSignature);
+
+ protected abstract ResolvedJavaType getMethodHandleType();
+
+ protected abstract JavaType lookupFieldType(int cpi, AbstractEspressoResolvedInstanceType accessingType);
+
+ protected abstract String lookupDescriptor(int cpi);
+
+ protected abstract String lookupName(int cpi);
+
+ protected abstract AbstractEspressoResolvedJavaMethod lookupResolvedMethod(int cpi, int opcode, AbstractEspressoResolvedJavaMethod caller);
+
+ protected abstract EspressoBootstrapMethodInvocation lookupIndyBootstrapMethodInvocation(int siteIndex);
+
+ @Override
+ public final List lookupBootstrapMethodInvocations(boolean invokeDynamic) {
+ List result;
+ if (invokeDynamic) {
+ int indyEntries = getNumIndyEntries();
+ if (indyEntries == 0) {
+ return Collections.emptyList();
+ }
+ result = new ArrayList<>(indyEntries);
+ for (int i = 0; i < indyEntries; i++) {
+ result.add(lookupIndyBootstrapMethodInvocation(i));
+ }
+ } else {
+ result = new ArrayList<>();
+ int length = length();
+ for (int i = 0; i < length; i++) {
+ byte tagByte = getTagByteAt(i);
+ if (tagByte == 17) {
+ // Dynamic
+ result.add(lookupBootstrapMethodInvocation(i, -1));
+ }
+ }
+ }
+ return result;
+ }
+
+ protected abstract int getNumIndyEntries();
+
+ @Override
+ public final Signature lookupSignature(int cpi) {
+ String rawSignature = lookupDescriptor(cpi);
+ return getSignature(rawSignature);
+ }
+
+ @Override
+ public final Object lookupConstant(int cpi) {
+ return lookupConstant(cpi, true);
+ }
+
+ protected abstract byte getTagByteAt(int cpi);
+
+ @SuppressWarnings("unused")
+ private String getTagAt(int cpi) {
+ // Used in tests
+ return switch (getTagByteAt(cpi)) {
+ case CONSTANT_Invalid -> "Invalid";
+ case CONSTANT_Utf8 -> "Utf8";
+ case CONSTANT_Integer -> "Integer";
+ case CONSTANT_Float -> "Float";
+ case CONSTANT_Long -> "Long";
+ case CONSTANT_Double -> "Double";
+ case CONSTANT_Class -> "Class";
+ case CONSTANT_String -> "String";
+ case CONSTANT_Fieldref -> "Fieldref";
+ case CONSTANT_Methodref -> "Methodref";
+ case CONSTANT_InterfaceMethodref -> "InterfaceMethodref";
+ case CONSTANT_NameAndType -> "NameAndType";
+ case CONSTANT_MethodHandle -> "MethodHandle";
+ case CONSTANT_MethodType -> "MethodType";
+ case CONSTANT_Dynamic -> "Dynamic";
+ case CONSTANT_InvokeDynamic -> "InvokeDynamic";
+ case CONSTANT_Module -> "Module";
+ case CONSTANT_Package -> "Package";
+ default -> null;
+ };
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedArrayType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedArrayType.java
new file mode 100644
index 000000000000..74ab5dc7299e
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedArrayType.java
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedArrayType extends EspressoResolvedObjectType {
+ protected final EspressoResolvedJavaType elementalType;
+ protected final int dimensions;
+ private EspressoResolvedJavaType componentType;
+
+ protected AbstractEspressoResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions) {
+ this(elementalType, dimensions, null);
+ }
+
+ protected AbstractEspressoResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, EspressoResolvedJavaType componentType) {
+ assert dimensions > 0;
+ assert !elementalType.isArray();
+ this.elementalType = elementalType;
+ this.dimensions = dimensions;
+ this.componentType = componentType;
+ }
+
+ @Override
+ public final boolean hasFinalizer() {
+ return false;
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult hasFinalizableSubclass() {
+ return new Assumptions.AssumptionResult<>(false);
+ }
+
+ @Override
+ public final boolean isArray() {
+ return true;
+ }
+
+ @Override
+ public final int getModifiers() {
+ return (getElementalType().getModifiers() & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED)) | Modifier.FINAL | Modifier.ABSTRACT;
+ }
+
+ @Override
+ public final boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public final boolean isInstanceClass() {
+ return false;
+ }
+
+ @Override
+ public final boolean isEnum() {
+ return false;
+ }
+
+ @Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public final void initialize() {
+ }
+
+ @Override
+ public final boolean isLinked() {
+ return true;
+ }
+
+ @Override
+ public final void link() {
+ }
+
+ @Override
+ public final boolean isAssignableFrom(ResolvedJavaType other) {
+ if (other instanceof AbstractEspressoResolvedArrayType otherArrayType) {
+ if (otherArrayType.dimensions > dimensions) {
+ return elementalType.isAssignableFrom(otherArrayType);
+ } else if (otherArrayType.dimensions == dimensions) {
+ return elementalType.isAssignableFrom(otherArrayType.elementalType);
+ }
+ return false;
+ }
+ return false;
+ }
+
+ @Override
+ public final EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
+ EspressoResolvedJavaType resolvedElementalType = getElementalType().resolve(accessingClass);
+ if (resolvedElementalType.equals(elementalType)) {
+ return this;
+ }
+ return withNewElementalType(resolvedElementalType);
+ }
+
+ protected abstract AbstractEspressoResolvedArrayType withNewElementalType(EspressoResolvedJavaType resolvedElementalType);
+
+ @Override
+ public final boolean declaresDefaultMethods() {
+ return false;
+ }
+
+ @Override
+ public final boolean hasDefaultMethods() {
+ return false;
+ }
+
+ @Override
+ public ResolvedJavaType getSuperclass() {
+ return getJavaLangObject();
+ }
+
+ @Override
+ public ResolvedJavaType[] getInterfaces() {
+ return getArrayInterfaces();
+ }
+
+ @Override
+ public final ResolvedJavaType getSingleImplementor() {
+ throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
+ }
+
+ @Override
+ public EspressoResolvedObjectType getSupertype() {
+ AbstractEspressoResolvedInstanceType javaLangObject = getJavaLangObject();
+ ResolvedJavaType component = getComponentType();
+ if (component.isPrimitive() || component.equals(javaLangObject)) {
+ return javaLangObject;
+ }
+ EspressoResolvedObjectType supertype = ((EspressoResolvedObjectType) component).getSupertype();
+ return supertype.getArrayClass();
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult findLeafConcreteSubtype() {
+ Assumptions.AssumptionResult elementType = elementalType.findLeafConcreteSubtype();
+ if (elementType != null && elementType.getResult().equals(elementalType)) {
+ /*
+ * If the elementType is leaf then the array is leaf under the same assumptions but only
+ * if the element type is exactly the leaf type. The element type can be abstract even
+ * if there is only one implementor of the abstract type.
+ */
+ Assumptions.AssumptionResult result = new Assumptions.AssumptionResult<>(this);
+ result.add(elementType);
+ return result;
+ }
+ return null;
+ }
+
+ @Override
+ public final String getName() {
+ StringBuilder sb = new StringBuilder();
+ sb.repeat('[', dimensions);
+ sb.append(elementalType.getName());
+ return sb.toString();
+ }
+
+ @Override
+ public final EspressoResolvedJavaType getElementalType() {
+ return elementalType;
+ }
+
+ @Override
+ public final ResolvedJavaType getComponentType() {
+ if (componentType == null) {
+ if (dimensions == 1) {
+ componentType = elementalType;
+ } else {
+ componentType = getArrayComponentType0();
+ }
+ }
+ return componentType;
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public List getPermittedSubclasses() {
+ return null;
+ }
+
+ protected abstract AbstractEspressoResolvedArrayType getArrayComponentType0();
+
+ @Override
+ public final boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
+ return getElementalType().isDefinitelyResolvedWithRespectTo(accessingClass);
+ }
+
+ @Override
+ public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
+ return getJavaLangObject().resolveMethod(method, callerType);
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public final ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
+ return NO_FIELDS;
+ }
+
+ @Override
+ public final ResolvedJavaField[] getStaticFields() {
+ return NO_FIELDS;
+ }
+
+ @Override
+ public final ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
+ return null;
+ }
+
+ @Override
+ public final String getSourceFileName() {
+ return null;
+ }
+
+ @Override
+ public final boolean isLocal() {
+ return false;
+ }
+
+ @Override
+ public final boolean isMember() {
+ return false;
+ }
+
+ @Override
+ public ResolvedJavaType[] getDeclaredTypes() {
+ return new ResolvedJavaType[0];
+ }
+
+ @Override
+ public final ResolvedJavaType getEnclosingType() {
+ return null;
+ }
+
+ @Override
+ public ResolvedJavaMethod getEnclosingMethod() {
+ return null;
+ }
+
+ @Override
+ public final ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
+ return NO_METHODS;
+ }
+
+ @Override
+ public final ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
+ return NO_METHODS;
+ }
+
+ @Override
+ public final List getAllMethods(boolean forceLink) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public final ResolvedJavaMethod getClassInitializer() {
+ return null;
+ }
+
+ @Override
+ public final boolean isCloneableWithAllocation() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ return null;
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ return null;
+ }
+
+ @Override
+ public final ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
+ return getElementalType().lookupType(unresolvedJavaType, resolve);
+ }
+
+ @Override
+ public boolean isRecord() {
+ return false;
+ }
+
+ @Override
+ public List extends ResolvedJavaRecordComponent> getRecordComponents() {
+ return null;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AbstractEspressoResolvedArrayType that = (AbstractEspressoResolvedArrayType) o;
+ return dimensions == that.dimensions && Objects.equals(elementalType, that.elementalType);
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(elementalType, dimensions);
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedInstanceType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedInstanceType.java
new file mode 100644
index 000000000000..d607a96027c1
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedInstanceType.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ANNOTATION;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ENUM;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.FINALIZER;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.FINAL;
+import static java.lang.reflect.Modifier.INTERFACE;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static java.util.Objects.requireNonNull;
+
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.Assumptions.AssumptionResult;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ModifiersProvider;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.UnresolvedJavaField;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedInstanceType extends EspressoResolvedObjectType {
+ private static final int JVM_CLASS_MODIFIERS = PUBLIC | FINAL | INTERFACE | ABSTRACT | ANNOTATION | ENUM | SYNTHETIC;
+ private static final SortByOffset fieldSortingMethod = new SortByOffset();
+
+ private static final class SortByOffset implements Comparator {
+ @Override
+ public int compare(ResolvedJavaField a, ResolvedJavaField b) {
+ return a.getOffset() - b.getOffset();
+ }
+ }
+
+ private static final AbstractEspressoResolvedInstanceType[] NO_INSTANCE_TYPES = new AbstractEspressoResolvedInstanceType[0];
+
+ private AbstractEspressoResolvedJavaField[] instanceFields;
+ private AbstractEspressoResolvedJavaField[] staticFields;
+ private AbstractEspressoResolvedInstanceType[] interfaces;
+ private List recordComponents;
+ private AbstractEspressoResolvedInstanceType superClass;
+ private String name;
+
+ @Override
+ public boolean hasFinalizer() {
+ return (getFlags() & FINALIZER) != 0;
+ }
+
+ @Override
+ public AssumptionResult hasFinalizableSubclass() {
+ // TODO?
+ return new Assumptions.AssumptionResult<>(true);
+ }
+
+ @Override
+ public boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public int getModifiers() {
+ return getFlags() & JVM_CLASS_MODIFIERS;
+ }
+
+ protected abstract int getFlags();
+
+ @Override
+ public boolean isInterface() {
+ return Modifier.isInterface(getFlags());
+ }
+
+ @Override
+ public boolean isInstanceClass() {
+ return !isInterface();
+ }
+
+ @Override
+ public boolean isEnum() {
+ return (getFlags() & ENUM) != 0;
+ }
+
+ @Override
+ public boolean isAssignableFrom(ResolvedJavaType other) {
+ requireNonNull(other);
+ if (other instanceof AbstractEspressoResolvedInstanceType espressoInstanceType) {
+ return isAssignableFrom(espressoInstanceType);
+ }
+ if (other instanceof EspressoResolvedArrayType) {
+ if (this.equals(getJavaLangObject())) {
+ return true;
+ }
+ if (this.isInterface()) {
+ for (AbstractEspressoResolvedInstanceType iface : getArrayInterfaces()) {
+ if (this.equals(iface)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ protected abstract boolean isAssignableFrom(AbstractEspressoResolvedInstanceType other);
+
+ @Override
+ public AbstractEspressoResolvedInstanceType getSuperclass() {
+ if (isInterface()) {
+ return null;
+ }
+ AbstractEspressoResolvedInstanceType javaLangObject = getJavaLangObject();
+ if (this.equals(javaLangObject)) {
+ return null;
+ }
+ // Cache result of native call
+ if (superClass == null) {
+ superClass = getSuperclass0();
+ }
+ return superClass;
+ }
+
+ protected abstract AbstractEspressoResolvedInstanceType getSuperclass0();
+
+ @Override
+ public ResolvedJavaType[] getInterfaces() {
+ if (interfaces == null) {
+ interfaces = getInterfaces0();
+ if (interfaces == null) {
+ interfaces = NO_INSTANCE_TYPES;
+ }
+ }
+ return interfaces;
+ }
+
+ protected abstract AbstractEspressoResolvedInstanceType[] getInterfaces0();
+
+ @Override
+ public List extends AbstractEspressoResolvedJavaRecordComponent> getRecordComponents() {
+ if (!isRecord()) {
+ return null;
+ }
+ if (recordComponents == null) {
+ recordComponents = Collections.unmodifiableList(Arrays.asList(getRecordComponents0()));
+ }
+ return recordComponents;
+ }
+
+ protected abstract AbstractEspressoResolvedJavaRecordComponent[] getRecordComponents0();
+
+ /// Denotes class file bytes of a `RuntimeVisibleAnnotations` attribute after
+ /// the `u2 attribute_name_index; u4 attribute_length` prefix.
+ static final int DECLARED_ANNOTATIONS = 0;
+
+ /// Denotes class file bytes of a `RuntimeVisibleParameterAnnotations` attribute after
+ /// the `u2 attribute_name_index; u4 attribute_length` prefix.
+ static final int PARAMETER_ANNOTATIONS = 1;
+
+ /// Denotes class file bytes of a `RuntimeVisibleTypeAnnotations` attribute after
+ /// the `u2 attribute_name_index; u4 attribute_length` prefix.
+ static final int TYPE_ANNOTATIONS = 2;
+
+ /// Denotes class file bytes of a `AnnotationDefault` attribute after
+ /// the `u2 attribute_name_index; u4 attribute_length` prefix.
+ static final int ANNOTATION_DEFAULT_VALUE = 3;
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ if (isArray()) {
+ return null;
+ }
+ byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
+ return AnnotationsInfo.make(bytes, getConstantPool(), this);
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ if (isArray()) {
+ return null;
+ }
+ byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
+ return AnnotationsInfo.make(bytes, getConstantPool(), this);
+ }
+
+ protected abstract byte[] getRawAnnotationBytes(int category);
+
+ @Override
+ public AbstractEspressoResolvedInstanceType getSingleImplementor() {
+ if (!isInterface()) {
+ throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
+ }
+ // espresso only supports finding a leaf concrete implementor.
+ // if there is one, it's also the only implementor that matter since others cannot be
+ // instantiated
+ AbstractEspressoResolvedInstanceType implementor = espressoSingleImplementor();
+ if (implementor == null) {
+ return this;
+ }
+ assert implementor.isConcrete();
+ assert this.isAssignableFrom(implementor);
+ // find the first class that implements the interface
+ while (true) {
+ AbstractEspressoResolvedInstanceType superclass = implementor.getSuperclass();
+ if (!this.isAssignableFrom(superclass)) {
+ return implementor;
+ }
+ implementor = superclass;
+ }
+ }
+
+ protected abstract int getVtableLength();
+
+ @Override
+ public EspressoResolvedObjectType getSupertype() {
+ if (isInterface()) {
+ return getJavaLangObject();
+ }
+ return getSuperclass();
+ }
+
+ @Override
+ public AssumptionResult findLeafConcreteSubtype() {
+ if (isLeaf()) {
+ // No assumptions are required.
+ return new AssumptionResult<>(this);
+ }
+ if (isLeafClass()) {
+ return new AssumptionResult<>(this, new Assumptions.LeafType(this));
+ }
+ if (isAbstract()) {
+ AbstractEspressoResolvedInstanceType espressoSingleImplementor = espressoSingleImplementor();
+ if (espressoSingleImplementor != null) {
+ return leafConcreteSubtype(espressoSingleImplementor);
+ }
+ }
+ return null;
+ }
+
+ protected abstract AbstractEspressoResolvedInstanceType espressoSingleImplementor();
+
+ private AssumptionResult leafConcreteSubtype(AbstractEspressoResolvedInstanceType type) {
+ if (type.isLeaf()) {
+ return new AssumptionResult<>(type, new Assumptions.ConcreteSubtype(this, type));
+ } else {
+ return new AssumptionResult<>(type, new Assumptions.LeafType(type), new Assumptions.ConcreteSubtype(this, type));
+ }
+ }
+
+ protected abstract boolean isLeafClass();
+
+ @Override
+ public String getName() {
+ if (name == null) {
+ name = getName0();
+ }
+ return name;
+ }
+
+ protected abstract String getName0();
+
+ @Override
+ public ResolvedJavaType getComponentType() {
+ return null;
+ }
+
+ @Override
+ public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
+ assert accessingClass != null;
+ ResolvedJavaType elementType = getElementalType();
+ if (elementType.isPrimitive()) {
+ // Primitive type resolution is context free.
+ return true;
+ }
+ if (elementType.getName().startsWith("Ljava/") && hasSameClassLoader(getJavaLangObject())) {
+ // Classes in a java.* package defined by the boot class loader are always resolved.
+ return true;
+ }
+ AbstractEspressoResolvedInstanceType otherMirror = (AbstractEspressoResolvedInstanceType) accessingClass.getElementalType();
+ return hasSameClassLoader(otherMirror);
+ }
+
+ protected abstract boolean hasSameClassLoader(AbstractEspressoResolvedInstanceType otherMirror);
+
+ @Override
+ public EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
+ if (isDefinitelyResolvedWithRespectTo(requireNonNull(accessingClass))) {
+ return this;
+ }
+ AbstractEspressoResolvedInstanceType accessingType = (AbstractEspressoResolvedInstanceType) accessingClass;
+ return (EspressoResolvedJavaType) lookupType(getName(), accessingType, true);
+ }
+
+ @Override
+ public AbstractEspressoResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
+ if (isInterface()) {
+ // Methods can only be resolved against concrete types
+ return null;
+ }
+ AbstractEspressoResolvedJavaMethod espressoMethod = (AbstractEspressoResolvedJavaMethod) method;
+ if (espressoMethod.isConcrete() && espressoMethod.getDeclaringClass().equals(this) && espressoMethod.isPublic() && !isSignaturePolymorphicHolder(espressoMethod.getDeclaringClass())) {
+ return espressoMethod;
+ }
+ if (!espressoMethod.getDeclaringClass().isAssignableFrom(this)) {
+ return null;
+ }
+ if (espressoMethod.isConstructor()) {
+ // Constructor calls should have been checked in the verifier and the method's
+ // declaring class is assignable from this (see above) so treat it as resolved.
+ return espressoMethod;
+ }
+ return resolveMethod0(espressoMethod, (AbstractEspressoResolvedInstanceType) callerType);
+ }
+
+ protected abstract AbstractEspressoResolvedJavaMethod resolveMethod0(AbstractEspressoResolvedJavaMethod method, AbstractEspressoResolvedInstanceType callerType);
+
+ private static boolean isSignaturePolymorphicHolder(ResolvedJavaType type) {
+ String name = type.getName();
+ return "Ljava/lang/invoke/MethodHandle;".equals(name) || "Ljava/lang/invoke/VarHandle;".equals(name);
+ }
+
+ @Override
+ public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
+ EspressoResolvedJavaMethod espressoMethod = (EspressoResolvedJavaMethod) method;
+ AbstractEspressoResolvedInstanceType declaredHolder = espressoMethod.getDeclaringClass();
+ if (!declaredHolder.isAssignableFrom(this) || this.equals(declaredHolder) || !isLinked() || isInterface()) {
+ if (espressoMethod.canBeStaticallyBound()) {
+ // No assumptions are required.
+ return new AssumptionResult<>(espressoMethod);
+ }
+ if (espressoMethod.isLeafMethod()) {
+ return new AssumptionResult<>(espressoMethod, new Assumptions.ConcreteMethod(method, declaredHolder, espressoMethod));
+ }
+ return null;
+ }
+
+ AbstractEspressoResolvedJavaMethod resolvedMethod = resolveMethod(espressoMethod, this);
+ if (resolvedMethod == null) {
+ // The type isn't known to implement the method.
+ return null;
+ }
+ if (resolvedMethod.canBeStaticallyBound()) {
+ // No assumptions are required.
+ return new AssumptionResult<>(resolvedMethod);
+ }
+ if (espressoMethod.isLeafMethod()) {
+ return new AssumptionResult<>(espressoMethod, new Assumptions.ConcreteMethod(method, declaredHolder, espressoMethod));
+ }
+ return null;
+ }
+
+ @Override
+ public AbstractEspressoResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
+ if (instanceFields == null) {
+ if (isInterface()) {
+ instanceFields = NO_FIELDS;
+ } else {
+ boolean needsSort = true;
+ AbstractEspressoResolvedJavaField[] result = getInstanceFields0();
+ if (getSuperclass() != null) {
+ AbstractEspressoResolvedJavaField[] superFields = getSuperclass().getInstanceFields(true);
+ if (superFields.length > 0) {
+ if (result.length > 0) {
+ AbstractEspressoResolvedJavaField[] merged = new AbstractEspressoResolvedJavaField[superFields.length + result.length];
+ System.arraycopy(superFields, 0, merged, 0, superFields.length);
+ System.arraycopy(result, 0, merged, superFields.length, result.length);
+ result = merged;
+ } else {
+ result = superFields;
+ needsSort = false;
+ }
+ }
+ }
+ if (needsSort) {
+ Arrays.sort(result, fieldSortingMethod);
+ }
+ assert Arrays.stream(result).noneMatch(ModifiersProvider::isStatic);
+ instanceFields = result;
+ }
+ }
+ if (includeSuperclasses || getSuperclass() == null) {
+ return instanceFields;
+ }
+ // filter superclass fields out
+ int superClassFieldCount = getSuperclass().getInstanceFields(true).length;
+ if (superClassFieldCount == instanceFields.length) {
+ // This class does not have any instance fields of its own.
+ return NO_FIELDS;
+ } else if (superClassFieldCount != 0) {
+ // Fields of the current class can be interleaved with fields of its super-classes
+ // Since they were sorted and we are only removing entries, the result will be sorted
+ assert instanceFields.length > superClassFieldCount : this + ": instanceFields.length=" + instanceFields.length + " superClassFieldCount=" + superClassFieldCount;
+ AbstractEspressoResolvedJavaField[] result = new AbstractEspressoResolvedJavaField[instanceFields.length - superClassFieldCount];
+ int i = 0;
+ for (AbstractEspressoResolvedJavaField f : instanceFields) {
+ if (f.getDeclaringClass().equals(this)) {
+ assert i == 0 || result[i - 1].getOffset() < f.getOffset();
+ result[i++] = f;
+ }
+ }
+ return result;
+ } else {
+ // The super classes of this class do not have any instance fields.
+ return instanceFields;
+ }
+ }
+
+ @Override
+ public AbstractEspressoResolvedJavaField[] getStaticFields() {
+ if (staticFields == null) {
+ AbstractEspressoResolvedJavaField[] result = getStaticFields0();
+ Arrays.sort(result, fieldSortingMethod);
+ assert Arrays.stream(result).allMatch(ModifiersProvider::isStatic);
+ staticFields = result;
+ }
+ return staticFields;
+ }
+
+ protected abstract AbstractEspressoResolvedJavaField[] getStaticFields0();
+
+ protected abstract AbstractEspressoResolvedJavaField[] getInstanceFields0();
+
+ @Override
+ public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
+ ResolvedJavaField[] declaredFields = getInstanceFields(true);
+ return findFieldWithOffset(offset, expectedKind, declaredFields);
+ }
+
+ private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) {
+ for (ResolvedJavaField field : declaredFields) {
+ if (field.getOffset() == offset && expectedEntryKind == field.getJavaKind()) {
+ return field;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public AbstractEspressoResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
+ if (forceLink) {
+ link();
+ }
+ return getDeclaredConstructors0();
+ }
+
+ protected abstract AbstractEspressoResolvedJavaMethod[] getDeclaredConstructors0();
+
+ @Override
+ public AbstractEspressoResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
+ if (forceLink) {
+ link();
+ }
+ return getDeclaredMethods0();
+ }
+
+ protected abstract AbstractEspressoResolvedJavaMethod[] getDeclaredMethods0();
+
+ @Override
+ public List getAllMethods(boolean forceLink) {
+ if (forceLink) {
+ link();
+ }
+ return Arrays.asList(getAllMethods0());
+ }
+
+ protected abstract AbstractEspressoResolvedJavaMethod[] getAllMethods0();
+
+ @Override
+ public boolean isCloneableWithAllocation() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) {
+ for (ResolvedJavaField field : getInstanceFields(false)) {
+ if (field.getName().equals(unresolvedJavaField.getName())) {
+ return field;
+ }
+ }
+ for (ResolvedJavaField field : getStaticFields()) {
+ if (field.getName().equals(unresolvedJavaField.getName())) {
+ return field;
+ }
+ }
+ throw new InternalError(unresolvedJavaField.toString());
+ }
+
+ @Override
+ public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
+ JavaType javaType = lookupType(unresolvedJavaType.getName(), this, resolve);
+ if (javaType instanceof ResolvedJavaType) {
+ return (ResolvedJavaType) javaType;
+ }
+ return null;
+ }
+
+ protected abstract JavaType lookupType(String typeName, AbstractEspressoResolvedInstanceType accessingType, boolean resolve);
+
+ public abstract AbstractEspressoConstantPool getConstantPool();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AbstractEspressoResolvedInstanceType that = (AbstractEspressoResolvedInstanceType) o;
+ return equals0(that);
+ }
+
+ protected abstract boolean equals0(AbstractEspressoResolvedInstanceType that);
+
+ @Override
+ public abstract int hashCode();
+
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaField.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaField.java
new file mode 100644
index 000000000000..48239443427d
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaField.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.TYPE_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ENUM;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.HIDDEN;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.STABLE_FIELD;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
+import static java.lang.reflect.Modifier.FINAL;
+import static java.lang.reflect.Modifier.PRIVATE;
+import static java.lang.reflect.Modifier.PROTECTED;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static java.lang.reflect.Modifier.STATIC;
+import static java.lang.reflect.Modifier.TRANSIENT;
+import static java.lang.reflect.Modifier.VOLATILE;
+
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+import jdk.vm.ci.meta.annotation.AbstractAnnotated;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedJavaField extends AbstractAnnotated implements ResolvedJavaField {
+ private static final int JVM_FIELDS_MODIFIERS = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | VOLATILE | TRANSIENT | ENUM | SYNTHETIC;
+
+ private final AbstractEspressoResolvedInstanceType holder;
+ private String name;
+ private JavaType type;
+
+ protected AbstractEspressoResolvedJavaField(AbstractEspressoResolvedInstanceType holder) {
+ this.holder = holder;
+ }
+
+ @Override
+ public final int getModifiers() {
+ return getFlags() & JVM_FIELDS_MODIFIERS;
+ }
+
+ protected abstract int getFlags();
+
+ @Override
+ public final boolean isInternal() {
+ return (getFlags() & HIDDEN) != 0;
+ }
+
+ @Override
+ public final boolean isSynthetic() {
+ return (getFlags() & SYNTHETIC) != 0;
+ }
+
+ public final boolean isStable() {
+ return (getFlags() & STABLE_FIELD) != 0;
+ }
+
+ @Override
+ public final String getName() {
+ if (name == null) {
+ name = getName0();
+ }
+ return name;
+ }
+
+ protected abstract String getName0();
+
+ @Override
+ public final JavaType getType() {
+ // Pull field into local variable to prevent a race causing
+ // a ClassCastException below
+ JavaType currentType = type;
+ if (currentType == null || currentType instanceof UnresolvedJavaType) {
+ // Don't allow unresolved types to hang around forever
+ type = getType0((UnresolvedJavaType) currentType);
+ }
+ return type;
+ }
+
+ protected abstract JavaType getType0(UnresolvedJavaType unresolved);
+
+ @Override
+ public final AbstractEspressoResolvedInstanceType getDeclaringClass() {
+ return holder;
+ }
+
+ @Override
+ public final JavaConstant getConstantValue() {
+ int constantValueIndex = getConstantValueIndex();
+ if (constantValueIndex == 0) {
+ return null;
+ }
+ return (JavaConstant) holder.getConstantPool().lookupConstant(constantValueIndex);
+ }
+
+ protected abstract int getConstantValueIndex();
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
+ AbstractEspressoResolvedInstanceType container = getDeclaringClass();
+ return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
+ AbstractEspressoResolvedInstanceType container = getDeclaringClass();
+ return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
+ }
+
+ protected abstract byte[] getRawAnnotationBytes(int category);
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AbstractEspressoResolvedJavaField that = (AbstractEspressoResolvedJavaField) o;
+ return equals0(that);
+ }
+
+ protected abstract boolean equals0(AbstractEspressoResolvedJavaField that);
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public final String toString() {
+ return format("EspressoResolvedJavaField<%H.%n %t:") + getOffset() + ">";
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaMethod.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaMethod.java
new file mode 100644
index 000000000000..6a03f097aac7
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaMethod.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.ANNOTATION_DEFAULT_VALUE;
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.PARAMETER_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.AbstractEspressoResolvedInstanceType.TYPE_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.BRIDGE;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SCOPED_METHOD;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
+import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.VARARGS;
+import static java.lang.reflect.Modifier.ABSTRACT;
+import static java.lang.reflect.Modifier.FINAL;
+import static java.lang.reflect.Modifier.NATIVE;
+import static java.lang.reflect.Modifier.PRIVATE;
+import static java.lang.reflect.Modifier.PROTECTED;
+import static java.lang.reflect.Modifier.PUBLIC;
+import static java.lang.reflect.Modifier.STATIC;
+import static java.lang.reflect.Modifier.STRICT;
+import static java.lang.reflect.Modifier.SYNCHRONIZED;
+
+import java.lang.reflect.Modifier;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.Constant;
+import jdk.vm.ci.meta.DefaultProfilingInfo;
+import jdk.vm.ci.meta.ProfilingInfo;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.SpeculationLog;
+import jdk.vm.ci.meta.TriState;
+import jdk.vm.ci.meta.annotation.AbstractAnnotated;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedJavaMethod extends AbstractAnnotated implements ResolvedJavaMethod {
+ private static final int JVM_METHOD_MODIFIERS = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | SYNCHRONIZED | BRIDGE | VARARGS | NATIVE | ABSTRACT | STRICT | SYNTHETIC;
+ public static final Parameter[] NO_PARAMETERS = new Parameter[0];
+
+ private final AbstractEspressoResolvedInstanceType holder;
+ private final boolean poisonPill;
+ private String nameCache;
+ private byte[] code;
+ private AbstractEspressoSignature signature;
+
+ protected AbstractEspressoResolvedJavaMethod(AbstractEspressoResolvedInstanceType holder, boolean poisonPill) {
+ this.holder = holder;
+ this.poisonPill = poisonPill;
+ }
+
+ @Override
+ public final byte[] getCode() {
+ if (getCodeSize() == 0) {
+ return null;
+ }
+ if (code == null && holder.isLinked()) {
+ code = getCode0();
+ assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
+ }
+ return code;
+ }
+
+ protected abstract byte[] getCode0();
+
+ @Override
+ public final int getCodeSize() {
+ int codeSize = getCodeSize0();
+ if (codeSize > 0 && !getDeclaringClass().isLinked()) {
+ return -1;
+ }
+ return codeSize;
+ }
+
+ protected abstract int getCodeSize0();
+
+ @Override
+ public final String getName() {
+ if (nameCache == null) {
+ nameCache = getName0();
+ }
+ return nameCache;
+ }
+
+ protected abstract String getName0();
+
+ @Override
+ public final AbstractEspressoResolvedInstanceType getDeclaringClass() {
+ return holder;
+ }
+
+ @Override
+ public final AbstractEspressoSignature getSignature() {
+ if (signature == null) {
+ signature = getSignature0();
+ }
+ return signature;
+ }
+
+ protected abstract AbstractEspressoSignature getSignature0();
+
+ @Override
+ public final boolean isSynthetic() {
+ return (getFlags() & SYNTHETIC) != 0;
+ }
+
+ @Override
+ public final boolean isVarArgs() {
+ return (getFlags() & VARARGS) != 0;
+ }
+
+ @Override
+ public final boolean isBridge() {
+ return (getFlags() & BRIDGE) != 0;
+ }
+
+ @Override
+ public final boolean isDefault() {
+ // Copied from java.lang.Method.isDefault()
+ int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
+ return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
+ }
+
+ @Override
+ public final boolean isDeclared() {
+ if (isConstructor() || isClassInitializer()) {
+ return false;
+ }
+ return !poisonPill;
+ }
+
+ @Override
+ public final boolean isClassInitializer() {
+ return isStatic() && "".equals(getName());
+ }
+
+ @Override
+ public final boolean isConstructor() {
+ return !isStatic() && "".equals(getName());
+ }
+
+ @Override
+ public final boolean canBeStaticallyBound() {
+ return (isFinal() || isPrivate() || isStatic() || holder.isLeaf() || isConstructor()) && isConcrete();
+ }
+
+ @Override
+ public final ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
+ // Be optimistic and return false for exceptionSeen?
+ return DefaultProfilingInfo.get(TriState.FALSE);
+ }
+
+ @Override
+ public final void reprofile() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public final AbstractEspressoConstantPool getConstantPool() {
+ return holder.getConstantPool();
+ }
+
+ @Override
+ public final boolean canBeInlined() {
+ if (isForceInline()) {
+ return true;
+ }
+ if (hasNeverInlineDirective()) {
+ return false;
+ }
+ return hasBytecodes();
+ }
+
+ protected abstract boolean isForceInline();
+
+ @Override
+ public final boolean shouldBeInlined() {
+ return isForceInline();
+ }
+
+ @Override
+ public final Constant getEncoding() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public final boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
+ EspressoResolvedInstanceType espressoResolved;
+ if (resolved instanceof EspressoResolvedInstanceType) {
+ espressoResolved = (EspressoResolvedInstanceType) resolved;
+ } else if (resolved instanceof EspressoResolvedArrayType) {
+ espressoResolved = runtime().getJavaLangObject();
+ } else {
+ return false;
+ }
+ int vtableIndex = getVtableIndex(espressoResolved);
+ return vtableIndex >= 0 && vtableIndex < espressoResolved.getVtableLength();
+ }
+
+ private int getVtableIndex(EspressoResolvedObjectType resolved) {
+ if (!holder.isLinked()) {
+ return -1;
+ }
+ if (holder.isInterface()) {
+ if (resolved.isInterface() || !resolved.isLinked() || !getDeclaringClass().isAssignableFrom(resolved)) {
+ return -1;
+ }
+ EspressoResolvedInstanceType type;
+ if (resolved instanceof EspressoResolvedArrayType) {
+ type = runtime().getJavaLangObject();
+ } else {
+ type = (EspressoResolvedInstanceType) resolved;
+ }
+ return getVtableIndexForInterfaceMethod(type);
+ }
+ return getVtableIndex();
+ }
+
+ protected abstract int getVtableIndexForInterfaceMethod(EspressoResolvedInstanceType resolved);
+
+ protected abstract int getVtableIndex();
+
+ @Override
+ public final SpeculationLog getSpeculationLog() {
+ throw JVMCIError.unimplemented();
+ }
+
+ @Override
+ public final int getModifiers() {
+ return getFlags() & JVM_METHOD_MODIFIERS;
+ }
+
+ protected abstract int getFlags();
+
+ protected abstract boolean isLeafMethod();
+
+ @Override
+ public final boolean isScoped() {
+ return (getFlags() & SCOPED_METHOD) != 0;
+ }
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ if (!hasAnnotations()) {
+ return null;
+ }
+ byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
+ return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
+ return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
+ }
+
+ @Override
+ public AnnotationsInfo getAnnotationDefaultInfo() {
+ byte[] bytes = getRawAnnotationBytes(ANNOTATION_DEFAULT_VALUE);
+ return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
+ }
+
+ @Override
+ public AnnotationsInfo getParameterAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(PARAMETER_ANNOTATIONS);
+ return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
+ }
+
+ protected abstract byte[] getRawAnnotationBytes(int category);
+
+ protected abstract boolean hasAnnotations();
+
+ @Override
+ public abstract Parameter[] getParameters();
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AbstractEspressoResolvedJavaMethod that = (AbstractEspressoResolvedJavaMethod) o;
+ return this.poisonPill == that.poisonPill && equals0(that);
+ }
+
+ protected abstract boolean equals0(AbstractEspressoResolvedJavaMethod that);
+
+ @Override
+ public final int hashCode() {
+ return 13 * Boolean.hashCode(poisonPill) + hashCode0();
+ }
+
+ protected abstract int hashCode0();
+
+ @Override
+ public final String toString() {
+ return format("EspressoResolvedJavaMethod<%h.%n(%p)>");
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaRecordComponent.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaRecordComponent.java
new file mode 100644
index 000000000000..dd219ebc8255
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaRecordComponent.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
+import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.TYPE_ANNOTATIONS;
+
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
+import jdk.vm.ci.meta.annotation.AbstractAnnotated;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedJavaRecordComponent extends AbstractAnnotated implements ResolvedJavaRecordComponent {
+ private final AbstractEspressoResolvedInstanceType declaringRecord;
+ private final int index;
+ private final String name;
+ private final JavaType type;
+
+ AbstractEspressoResolvedJavaRecordComponent(AbstractEspressoResolvedInstanceType declaringRecord, int recordIndex, int nameIndex, JavaType type) {
+ this.declaringRecord = declaringRecord;
+ this.index = recordIndex;
+ this.name = declaringRecord.getConstantPool().lookupUtf8(nameIndex);
+ this.type = type;
+ }
+
+ @Override
+ public AbstractEspressoResolvedInstanceType getDeclaringRecord() {
+ return declaringRecord;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public JavaType getType() {
+ return type;
+ }
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
+ AbstractEspressoResolvedInstanceType container = getDeclaringRecord();
+ return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
+ AbstractEspressoResolvedInstanceType container = getDeclaringRecord();
+ return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
+ }
+
+ protected final int getIndex() {
+ return index;
+ }
+
+ protected abstract byte[] getRawAnnotationBytes(int category);
+
+ @Override
+ public int hashCode() {
+ return declaringRecord.hashCode() + index * 31;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof AbstractEspressoResolvedJavaRecordComponent other)) {
+ return false;
+ }
+ /*
+ * No need for a native equals0 helper (cf EspressoResolvedJavaField.equals0) as there is no
+ * metadata object that needs an identity equality check.
+ */
+ return other.index == this.index && other.declaringRecord.equals(this.declaringRecord);
+ }
+
+ @Override
+ public String toString() {
+ return "EspressoResolvedJavaRecordComponent<" + declaringRecord.getName() + "." + name + " " + type.getUnqualifiedName() + ">";
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedPrimitiveType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedPrimitiveType.java
new file mode 100644
index 000000000000..44f60e248a22
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedPrimitiveType.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import static java.util.Objects.requireNonNull;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.List;
+
+import jdk.vm.ci.common.JVMCIError;
+import jdk.vm.ci.meta.Assumptions;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaField;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+
+public abstract class AbstractEspressoResolvedPrimitiveType extends EspressoResolvedJavaType {
+ private final JavaKind kind;
+
+ protected AbstractEspressoResolvedPrimitiveType(JavaKind kind) {
+ assert kind.isPrimitive();
+ this.kind = kind;
+ }
+
+ @Override
+ public final boolean hasFinalizer() {
+ return false;
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult hasFinalizableSubclass() {
+ return new Assumptions.AssumptionResult<>(false);
+ }
+
+ @Override
+ public final int getModifiers() {
+ return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
+ }
+
+ @Override
+ public final boolean isInterface() {
+ return false;
+ }
+
+ @Override
+ public final boolean isInstanceClass() {
+ return false;
+ }
+
+ @Override
+ public final boolean isPrimitive() {
+ return true;
+ }
+
+ @Override
+ public final boolean isEnum() {
+ return false;
+ }
+
+ @Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @Override
+ public final void initialize() {
+ }
+
+ @Override
+ public final boolean isLinked() {
+ return true;
+ }
+
+ @Override
+ public final void link() {
+ }
+
+ @Override
+ public final boolean hasDefaultMethods() {
+ return false;
+ }
+
+ @Override
+ public final boolean declaresDefaultMethods() {
+ return false;
+ }
+
+ @Override
+ public final boolean isAssignableFrom(ResolvedJavaType other) {
+ assert other != null;
+ return other.equals(this);
+ }
+
+ @Override
+ public final boolean isInstance(JavaConstant obj) {
+ return obj.getJavaKind() == kind;
+ }
+
+ @Override
+ public final ResolvedJavaType getSuperclass() {
+ return null;
+ }
+
+ @Override
+ public final ResolvedJavaType[] getInterfaces() {
+ return NO_TYPES;
+ }
+
+ @Override
+ public final ResolvedJavaType getSingleImplementor() {
+ throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
+ }
+
+ @Override
+ public final ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) {
+ return null;
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult findLeafConcreteSubtype() {
+ return new Assumptions.AssumptionResult<>(this);
+ }
+
+ @Override
+ public final String getName() {
+ return String.valueOf(kind.getTypeChar());
+ }
+
+ @Override
+ public final ResolvedJavaType getComponentType() {
+ return null;
+ }
+
+ @Override
+ public boolean isHidden() {
+ return false;
+ }
+
+ @Override
+ public List getPermittedSubclasses() {
+ return null;
+ }
+
+ @Override
+ public final JavaKind getJavaKind() {
+ return kind;
+ }
+
+ @Override
+ public final EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
+ requireNonNull(accessingClass);
+ return this;
+ }
+
+ @Override
+ public final boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
+ requireNonNull(accessingClass);
+ return true;
+ }
+
+ @Override
+ public final ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
+ return null;
+ }
+
+ @Override
+ public final Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
+ return null;
+ }
+
+ @Override
+ public final ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
+ return NO_FIELDS;
+ }
+
+ @Override
+ public final ResolvedJavaField[] getStaticFields() {
+ return NO_FIELDS;
+ }
+
+ @Override
+ public final ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
+ return null;
+ }
+
+ @Override
+ public final String getSourceFileName() {
+ return null;
+ }
+
+ @Override
+ public final boolean isLocal() {
+ return false;
+ }
+
+ @Override
+ public final boolean isMember() {
+ return false;
+ }
+
+ @Override
+ public ResolvedJavaType[] getDeclaredTypes() {
+ return new ResolvedJavaType[0];
+ }
+
+ @Override
+ public final ResolvedJavaType getEnclosingType() {
+ return null;
+ }
+
+ @Override
+ public ResolvedJavaMethod getEnclosingMethod() {
+ return null;
+ }
+
+ @Override
+ public final ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
+ return NO_METHODS;
+ }
+
+ @Override
+ public final ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
+ return NO_METHODS;
+ }
+
+ @Override
+ public final List getAllMethods(boolean forceLink) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public final ResolvedJavaMethod getClassInitializer() {
+ return null;
+ }
+
+ @Override
+ public final boolean isCloneableWithAllocation() {
+ return false;
+ }
+
+ @Override
+ public AnnotationsInfo getRawDeclaredAnnotationInfo() {
+ return null;
+ }
+
+ @Override
+ public AnnotationsInfo getTypeAnnotationInfo() {
+ return null;
+ }
+
+ @Override
+ public final Class> getMirror() {
+ return kind.toJavaClass();
+ }
+
+ @Override
+ public final boolean isArray() {
+ return false;
+ }
+
+ @Override
+ public boolean isRecord() {
+ return false;
+ }
+
+ @Override
+ public List extends ResolvedJavaRecordComponent> getRecordComponents() {
+ return null;
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (!(obj instanceof AbstractEspressoResolvedPrimitiveType that)) {
+ return false;
+ }
+ return that.kind == kind;
+ }
+
+ @Override
+ public final int hashCode() {
+ return kind.hashCode();
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoSignature.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoSignature.java
new file mode 100644
index 000000000000..6bc82c091129
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoSignature.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaType;
+import jdk.vm.ci.meta.Signature;
+import jdk.vm.ci.meta.UnresolvedJavaType;
+
+public abstract class AbstractEspressoSignature implements Signature {
+ private final List parameters = new ArrayList<>();
+ private final String returnType;
+ private final String rawSignature;
+ private EspressoResolvedJavaType[] parameterTypesCache;
+ private EspressoResolvedJavaType returnTypeCache;
+
+ protected AbstractEspressoSignature(String rawSignature) {
+ if (rawSignature.isEmpty()) {
+ throw new IllegalArgumentException("Signature cannot be empty");
+ }
+ this.rawSignature = rawSignature;
+ if (rawSignature.charAt(0) == '(') {
+ int cur = 1;
+ while (cur < rawSignature.length() && rawSignature.charAt(cur) != ')') {
+ int nextCur = parseSignature(rawSignature, cur);
+ parameters.add(rawSignature.substring(cur, nextCur));
+ cur = nextCur;
+ }
+
+ cur++;
+ int nextCur = parseSignature(rawSignature, cur);
+ returnType = rawSignature.substring(cur, nextCur);
+ if (nextCur != rawSignature.length()) {
+ throw new IllegalArgumentException("Extra characters at end of signature: " + rawSignature);
+ }
+ } else {
+ throw new IllegalArgumentException("Signature must start with a '(': " + rawSignature);
+ }
+ }
+
+ private static int parseSignature(String signature, int start) {
+ try {
+ int cur = start;
+ char first;
+ do {
+ first = signature.charAt(cur);
+ cur++;
+ } while (first == '[');
+
+ switch (first) {
+ case 'L':
+ while (signature.charAt(cur) != ';') {
+ if (signature.charAt(cur) == '.') {
+ throw new IllegalArgumentException("Class name in signature contains '.' at index " + cur + ": " + signature);
+ }
+ cur++;
+ }
+ cur++;
+ break;
+ case 'V':
+ case 'I':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'J':
+ case 'S':
+ case 'Z':
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid character '" + signature.charAt(cur - 1) + "' at index " + (cur - 1) + " in signature: " + signature);
+ }
+ return cur;
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("Truncated signature: " + signature);
+ }
+ }
+
+ @Override
+ public final int getParameterCount(boolean withReceiver) {
+ return parameters.size() + (withReceiver ? 1 : 0);
+ }
+
+ private static boolean checkValidCache(ResolvedJavaType type, ResolvedJavaType accessingClass) {
+ assert accessingClass != null;
+ if (type == null) {
+ return false;
+ } else if (type instanceof EspressoResolvedObjectType) {
+ return ((EspressoResolvedJavaType) type).isDefinitelyResolvedWithRespectTo(accessingClass);
+ }
+ return true;
+ }
+
+ private JavaType getUnresolvedOrPrimitiveType(String name) {
+ if (name.length() == 1) {
+ JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
+ return lookupPrimitiveType0(kind);
+ }
+ return UnresolvedJavaType.create(name);
+ }
+
+ @Override
+ public final JavaType getParameterType(int index, ResolvedJavaType accessingClass) {
+ if (accessingClass == null) {
+ // Caller doesn't care about resolution context so return an unresolved
+ // or primitive type (primitive type resolution is context free)
+ return getUnresolvedOrPrimitiveType(parameters.get(index));
+ }
+ if (parameterTypesCache == null) {
+ parameterTypesCache = new EspressoResolvedJavaType[parameters.size()];
+ }
+
+ EspressoResolvedJavaType type = parameterTypesCache[index];
+ if (!checkValidCache(type, accessingClass)) {
+ JavaType result = lookupType(parameters.get(index), (AbstractEspressoResolvedInstanceType) accessingClass);
+ if (result instanceof EspressoResolvedJavaType) {
+ type = (EspressoResolvedJavaType) result;
+ parameterTypesCache[index] = type;
+ } else {
+ assert result != null;
+ return result;
+ }
+ }
+ assert type != null;
+ return type;
+ }
+
+ @Override
+ public final JavaKind getParameterKind(int index) {
+ return JavaKind.fromTypeString(parameters.get(index));
+ }
+
+ @Override
+ public final JavaType getReturnType(ResolvedJavaType accessingClass) {
+ if (accessingClass == null) {
+ // Caller doesn't care about resolution context so return an unresolved
+ // or primitive type (primitive type resolution is context free)
+ return getUnresolvedOrPrimitiveType(returnType);
+ }
+ if (!checkValidCache(returnTypeCache, accessingClass)) {
+ JavaType result = lookupType(returnType, (AbstractEspressoResolvedInstanceType) accessingClass);
+ if (result instanceof EspressoResolvedJavaType) {
+ returnTypeCache = (EspressoResolvedJavaType) result;
+ } else {
+ return result;
+ }
+ }
+ return returnTypeCache;
+ }
+
+ private JavaType lookupType(String descriptor, AbstractEspressoResolvedInstanceType accessingClass) {
+ if (descriptor.length() == 1) {
+ JavaKind kind = JavaKind.fromTypeString(descriptor);
+ if (kind.isPrimitive()) {
+ return lookupPrimitiveType0(kind);
+ }
+ }
+ return lookupType0(descriptor, accessingClass);
+ }
+
+ protected abstract JavaType lookupType0(String descriptor, AbstractEspressoResolvedInstanceType accessingClass);
+
+ protected abstract AbstractEspressoResolvedPrimitiveType lookupPrimitiveType0(JavaKind kind);
+
+ @Override
+ public final JavaKind getReturnKind() {
+ return JavaKind.fromTypeString(returnType);
+ }
+
+ @Override
+ public final String toMethodDescriptor() {
+ assert rawSignature.equals(Signature.super.toMethodDescriptor()) : rawSignature + " != " + Signature.super.toMethodDescriptor();
+ return rawSignature;
+ }
+
+ @Override
+ public final String toString() {
+ return "EspressoSignature<" + rawSignature + ">";
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (obj instanceof AbstractEspressoSignature other) {
+ if (other.rawSignature.equals(rawSignature)) {
+ assert other.parameters.equals(parameters);
+ assert other.returnType.equals(returnType);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return rawSignature.hashCode();
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ConstantReflectionProviderWithStaticsBase.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ConstantReflectionProviderWithStaticsBase.java
new file mode 100644
index 000000000000..cc662f1f38be
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ConstantReflectionProviderWithStaticsBase.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.jvmci.meta;
+
+import jdk.vm.ci.meta.ConstantReflectionProvider;
+import jdk.vm.ci.meta.JavaConstant;
+
+public interface ConstantReflectionProviderWithStaticsBase extends ConstantReflectionProvider {
+ AbstractEspressoResolvedInstanceType getTypeForStaticBase(JavaConstant staticBase);
+}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation.java
index cc49fe335f37..d71bbf4c18ca 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,8 @@
*/
package com.oracle.truffle.espresso.jvmci.meta;
+import static com.oracle.truffle.espresso.jvmci.meta.EspressoConstantPool.INVOKEDYNAMIC;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -36,14 +38,15 @@
*/
public final class EspressoBootstrapMethodInvocation implements ConstantPool.BootstrapMethodInvocation {
private final boolean indy;
- private final EspressoResolvedJavaMethod method;
+ private final AbstractEspressoResolvedJavaMethod method;
private final String name;
private final JavaConstant type;
private final List staticArguments;
private final int cpi;
- private final EspressoConstantPool constantPool;
+ private final AbstractEspressoConstantPool constantPool;
- EspressoBootstrapMethodInvocation(boolean indy, EspressoResolvedJavaMethod method, String name, JavaConstant type, JavaConstant[] staticArguments, int cpi, EspressoConstantPool constantPool) {
+ EspressoBootstrapMethodInvocation(boolean indy, AbstractEspressoResolvedJavaMethod method, String name, JavaConstant type, JavaConstant[] staticArguments, int cpi,
+ AbstractEspressoConstantPool constantPool) {
this.indy = indy;
this.method = method;
this.name = name;
@@ -54,7 +57,7 @@ public final class EspressoBootstrapMethodInvocation implements ConstantPool.Boo
}
@Override
- public EspressoResolvedJavaMethod getMethod() {
+ public AbstractEspressoResolvedJavaMethod getMethod() {
return method;
}
@@ -81,7 +84,7 @@ public List getStaticArguments() {
@Override
public void resolve() {
if (isInvokeDynamic()) {
- constantPool.loadReferencedType(cpi, EspressoConstantPool.INVOKEDYNAMIC);
+ constantPool.loadReferencedType(cpi, INVOKEDYNAMIC);
} else {
constantPool.lookupConstant(cpi, true);
}
@@ -90,7 +93,7 @@ public void resolve() {
@Override
public JavaConstant lookup() {
if (isInvokeDynamic()) {
- return constantPool.lookupAppendix(cpi, EspressoConstantPool.INVOKEDYNAMIC);
+ return constantPool.lookupAppendix(cpi, INVOKEDYNAMIC);
} else {
return (JavaConstant) constantPool.lookupConstant(cpi, false);
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantPool.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantPool.java
index 4cf2948d7e87..c32d3a2fb4de 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantPool.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantPool.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,28 +25,17 @@
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
import java.lang.invoke.MethodHandle;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.JavaConstant;
-import jdk.vm.ci.meta.JavaField;
-import jdk.vm.ci.meta.JavaMethod;
import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.Signature;
-import jdk.vm.ci.meta.UnresolvedJavaField;
-import jdk.vm.ci.meta.UnresolvedJavaMethod;
-
-public final class EspressoConstantPool implements ConstantPool {
- public static final int INVOKEDYNAMIC = 186;
+import jdk.vm.ci.meta.ResolvedJavaType;
+public final class EspressoConstantPool extends AbstractEspressoConstantPool {
@SuppressWarnings("unused")
// Used by the VM
private final EspressoResolvedInstanceType holder;
- public EspressoConstantPool(EspressoResolvedInstanceType holder) {
+ EspressoConstantPool(EspressoResolvedInstanceType holder) {
this.holder = holder;
}
@@ -54,177 +43,72 @@ public EspressoConstantPool(EspressoResolvedInstanceType holder) {
public native int length();
@Override
- public void loadReferencedType(int cpi, int opcode) {
- loadReferencedType(cpi, opcode, true);
- }
+ public native JavaType lookupReferencedType(int cpi, int opcode);
+
+ @Override
+ public native EspressoBootstrapMethodInvocation lookupBootstrapMethodInvocation(int cpi, int opcode);
@Override
- public void loadReferencedType(int cpi, int opcode, boolean initialize) {
- if (!loadReferencedType0(cpi, opcode)) {
- return;
- }
- if (initialize) {
- EspressoResolvedJavaType type = (EspressoResolvedJavaType) lookupReferencedType(cpi, opcode);
- type.initialize();
- }
+ public JavaType lookupType(int cpi, @SuppressWarnings("unused") int opcode) {
+ return lookupType(cpi);
}
- private native boolean loadReferencedType0(int cpi, int opcode);
+ native JavaType lookupType(int cpi);
@Override
- public native JavaType lookupReferencedType(int cpi, int opcode);
+ public native String lookupUtf8(int cpi);
@Override
- public JavaField lookupField(int cpi, ResolvedJavaMethod method, int opcode) {
- EspressoResolvedJavaField field;
- try {
- field = lookupResolvedField(cpi, (EspressoResolvedJavaMethod) method, opcode);
- } catch (Throwable t) {
- // ignore errors that can happen during type resolution
- field = null;
- }
- if (field != null) {
- return field;
- }
- String name = lookupName(cpi);
- String typeDescriptor = lookupDescriptor(cpi);
- JavaType type = runtime().lookupType(typeDescriptor, ((EspressoResolvedJavaMethod) method).getDeclaringClass(), false);
- JavaType fieldHolder = lookupReferencedType(cpi, opcode);
- return new UnresolvedJavaField(fieldHolder, name, type);
- }
+ public native Object lookupConstant(int cpi, boolean resolve);
- private native EspressoResolvedJavaField lookupResolvedField(int cpi, EspressoResolvedJavaMethod method, int opcode);
+ @Override
+ public native JavaConstant lookupAppendix(int cpi, int opcode);
@Override
- public JavaMethod lookupMethod(int cpi, int opcode, ResolvedJavaMethod caller) {
- EspressoResolvedJavaMethod method;
- try {
- method = lookupResolvedMethod(cpi, opcode, (EspressoResolvedJavaMethod) caller);
- } catch (Throwable t) {
- // ignore errors that can happen during type resolution
- method = null;
- }
- if (method != null) {
- return method;
- }
- String name = lookupName(cpi);
- String rawSignature = lookupDescriptor(cpi);
- JavaType methodHolder;
- if (opcode == INVOKEDYNAMIC) {
- methodHolder = runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(MethodHandle.class);
- } else {
- methodHolder = lookupReferencedType(cpi, opcode);
- }
- return new UnresolvedJavaMethod(name, new EspressoSignature(rawSignature), methodHolder);
- }
+ protected native boolean loadReferencedType0(int cpi, int opcode);
- private native String lookupDescriptor(int cpi);
+ @Override
+ protected EspressoResolvedJavaField lookupResolvedField(int cpi, AbstractEspressoResolvedJavaMethod method, int opcode) {
+ return lookupResolvedField(cpi, (EspressoResolvedJavaMethod) method, opcode);
+ }
- private native String lookupName(int cpi);
+ private native EspressoResolvedJavaField lookupResolvedField(int cpi, EspressoResolvedJavaMethod method, int opcode);
- private native EspressoResolvedJavaMethod lookupResolvedMethod(int cpi, int opcode, EspressoResolvedJavaMethod caller);
+ @Override
+ protected native String lookupDescriptor(int cpi);
@Override
- public native EspressoBootstrapMethodInvocation lookupBootstrapMethodInvocation(int cpi, int opcode);
+ protected native String lookupName(int cpi);
- private native EspressoBootstrapMethodInvocation lookupIndyBootstrapMethodInvocation(int siteIndex);
-
- @Override
- public List lookupBootstrapMethodInvocations(boolean invokeDynamic) {
- List result;
- if (invokeDynamic) {
- int indyEntries = getNumIndyEntries();
- if (indyEntries == 0) {
- return Collections.emptyList();
- }
- result = new ArrayList<>(indyEntries);
- for (int i = 0; i < indyEntries; i++) {
- result.add(lookupIndyBootstrapMethodInvocation(i));
- }
- } else {
- result = new ArrayList<>();
- int length = length();
- for (int i = 0; i < length; i++) {
- byte tagByte = getTagByteAt(i);
- if (tagByte == 17) {
- // Dynamic
- result.add(lookupBootstrapMethodInvocation(i, -1));
- }
- }
- }
- return result;
+ @Override
+ protected EspressoResolvedJavaMethod lookupResolvedMethod(int cpi, int opcode, AbstractEspressoResolvedJavaMethod caller) {
+ return lookupResolvedMethod(cpi, opcode, (EspressoResolvedJavaMethod) caller);
}
- private native int getNumIndyEntries();
+ private native EspressoResolvedJavaMethod lookupResolvedMethod(int cpi, int opcode, EspressoResolvedJavaMethod caller);
@Override
- public JavaType lookupType(int cpi, @SuppressWarnings("unused") int opcode) {
- return lookupType(cpi);
- }
-
- native JavaType lookupType(int cpi);
+ protected native EspressoBootstrapMethodInvocation lookupIndyBootstrapMethodInvocation(int siteIndex);
@Override
- public native String lookupUtf8(int cpi);
+ protected native int getNumIndyEntries();
@Override
- public Signature lookupSignature(int cpi) {
- String rawSignature = lookupDescriptor(cpi);
- return new EspressoSignature(rawSignature);
- }
+ protected native byte getTagByteAt(int cpi);
@Override
- public Object lookupConstant(int cpi) {
- return lookupConstant(cpi, true);
+ protected ResolvedJavaType getMethodHandleType() {
+ return runtime().getHostJVMCIBackend().getMetaAccess().lookupJavaType(MethodHandle.class);
}
@Override
- public native Object lookupConstant(int cpi, boolean resolve);
+ protected JavaType lookupFieldType(int cpi, AbstractEspressoResolvedInstanceType accessingType) {
+ String typeDescriptor = lookupDescriptor(cpi);
+ return runtime().lookupType(typeDescriptor, (EspressoResolvedInstanceType) accessingType, false);
+ }
@Override
- public native JavaConstant lookupAppendix(int cpi, int opcode);
-
- private native byte getTagByteAt(int cpi);
-
- @SuppressWarnings("unused")
- private String getTagAt(int cpi) {
- // Used in tests
- switch (getTagByteAt(cpi)) {
- case 1:
- return "Utf8";
- case 3:
- return "Integer";
- case 4:
- return "Float";
- case 5:
- return "Long";
- case 6:
- return "Double";
- case 7:
- return "Class";
- case 8:
- return "String";
- case 9:
- return "Fieldref";
- case 10:
- return "Methodref";
- case 11:
- return "InterfaceMethodref";
- case 12:
- return "NameAndType";
- case 15:
- return "MethodHandle";
- case 16:
- return "MethodType";
- case 17:
- return "Dynamic";
- case 18:
- return "InvokeDynamic";
- case 19:
- return "Module";
- case 20:
- return "Package";
- }
- return null;
+ protected EspressoSignature getSignature(String rawSignature) {
+ return new EspressoSignature(rawSignature);
}
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantReflectionProvider.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantReflectionProvider.java
index ba1e89b3f06b..fa2dd54303c8 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantReflectionProvider.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoConstantReflectionProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,7 +27,6 @@
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.MemoryAccessProvider;
@@ -35,7 +34,7 @@
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;
-public final class EspressoConstantReflectionProvider implements ConstantReflectionProvider {
+public final class EspressoConstantReflectionProvider implements ConstantReflectionProviderWithStaticsBase {
private final EspressoMethodHandleAccessProvider methodHandleAccessProvider;
private final EspressoMetaAccessProvider metaAccess;
@@ -296,5 +295,13 @@ public T asObject(Class type, EspressoObjectConstant constant) {
return null;
}
- public native EspressoResolvedInstanceType getTypeForStaticBase(EspressoObjectConstant staticBase);
+ private native EspressoResolvedInstanceType getTypeForStaticBase(EspressoObjectConstant staticBase);
+
+ @Override
+ public AbstractEspressoResolvedInstanceType getTypeForStaticBase(JavaConstant staticBase) {
+ if (!(staticBase instanceof EspressoObjectConstant)) {
+ return null;
+ }
+ return getTypeForStaticBase((EspressoObjectConstant) staticBase);
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedArrayType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedArrayType.java
index 0254b01d0115..d2411d47f9e6 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedArrayType.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedArrayType.java
@@ -24,38 +24,20 @@
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
+import java.lang.reflect.Array;
-import jdk.vm.ci.common.JVMCIError;
-import jdk.vm.ci.meta.Assumptions;
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaField;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
-import jdk.vm.ci.meta.ResolvedJavaType;
-import jdk.vm.ci.meta.UnresolvedJavaType;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
+import jdk.vm.ci.meta.JavaConstant;
-public final class EspressoResolvedArrayType extends EspressoResolvedObjectType {
- private final EspressoResolvedJavaType elementalType;
- private final int dimensions;
+final class EspressoResolvedArrayType extends AbstractEspressoResolvedArrayType {
private final Class> mirror;
- private EspressoResolvedJavaType componentType;
- EspressoResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, Class> mirror) {
- this(elementalType, dimensions, null, mirror);
+ private EspressoResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, Class> mirror) {
+ super(elementalType, dimensions);
+ this.mirror = mirror;
}
EspressoResolvedArrayType(EspressoResolvedJavaType elementalType, int dimensions, EspressoResolvedJavaType componentType, Class> mirror) {
- assert dimensions > 0;
- assert !elementalType.isArray();
- this.elementalType = elementalType;
- this.dimensions = dimensions;
- this.componentType = componentType;
+ super(elementalType, dimensions, componentType);
this.mirror = mirror;
}
@@ -65,303 +47,36 @@ protected Class> getMirror0() {
}
@Override
- public boolean hasFinalizer() {
- return false;
- }
-
- @Override
- public Assumptions.AssumptionResult hasFinalizableSubclass() {
- return new Assumptions.AssumptionResult<>(false);
- }
-
- @Override
- public boolean isArray() {
- return true;
- }
-
- @Override
- public int getModifiers() {
- return (getElementalType().getModifiers() & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED)) | Modifier.FINAL | Modifier.ABSTRACT;
- }
-
- @Override
- public boolean isInterface() {
- return false;
- }
-
- @Override
- public boolean isInstanceClass() {
- return false;
- }
-
- @Override
- public boolean isEnum() {
- return false;
- }
-
- @Override
- public boolean isInitialized() {
- return true;
- }
-
- @Override
- public void initialize() {
- }
-
- @Override
- public boolean isLinked() {
- return true;
- }
-
- @Override
- public void link() {
- }
-
- @Override
- public boolean isAssignableFrom(ResolvedJavaType other) {
- if (other instanceof EspressoResolvedArrayType otherArrayType) {
- if (otherArrayType.dimensions > dimensions) {
- return elementalType.isAssignableFrom(otherArrayType);
- } else if (otherArrayType.dimensions == dimensions) {
- return elementalType.isAssignableFrom(otherArrayType.elementalType);
- }
- return false;
- }
- return false;
- }
-
- @Override
- public boolean declaresDefaultMethods() {
- return false;
- }
-
- @Override
- public boolean hasDefaultMethods() {
- return false;
- }
-
- @Override
- public ResolvedJavaType getSuperclass() {
+ protected AbstractEspressoResolvedInstanceType getJavaLangObject() {
return runtime().getJavaLangObject();
}
@Override
- public ResolvedJavaType[] getInterfaces() {
+ protected AbstractEspressoResolvedInstanceType[] getArrayInterfaces() {
return runtime().getArrayInterfaces();
}
@Override
- public ResolvedJavaType getSingleImplementor() {
- throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
- }
-
- @Override
- public EspressoResolvedObjectType getSupertype() {
- EspressoResolvedInstanceType javaLangObject = runtime().getJavaLangObject();
- ResolvedJavaType component = getComponentType();
- if (component.isPrimitive() || component.equals(javaLangObject)) {
- return javaLangObject;
- }
- EspressoResolvedObjectType supertype = ((EspressoResolvedObjectType) component).getSupertype();
- return supertype.getArrayClass();
+ protected EspressoResolvedArrayType getArrayComponentType0() {
+ return new EspressoResolvedArrayType(elementalType, dimensions - 1, mirror.getComponentType());
}
@Override
- public Assumptions.AssumptionResult findLeafConcreteSubtype() {
- Assumptions.AssumptionResult elementType = elementalType.findLeafConcreteSubtype();
- if (elementType != null && elementType.getResult().equals(elementalType)) {
- /*
- * If the elementType is leaf then the array is leaf under the same assumptions but only
- * if the element type is exactly the leaf type. The element type can be abstract even
- * if there is only one implementor of the abstract type.
- */
- Assumptions.AssumptionResult result = new Assumptions.AssumptionResult<>(this);
- result.add(elementType);
- return result;
- }
- return null;
+ protected EspressoResolvedArrayType getArrayClass0() {
+ return new EspressoResolvedArrayType(elementalType, dimensions + 1, this, findArrayClass(mirror, 1));
}
@Override
- public String getName() {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < dimensions; i++) {
- sb.append('[');
- }
- sb.append(elementalType.getName());
- return sb.toString();
- }
-
- @Override
- public EspressoResolvedJavaType getElementalType() {
- return elementalType;
- }
-
- @Override
- public ResolvedJavaType getComponentType() {
- if (componentType == null) {
- if (dimensions == 1) {
- componentType = elementalType;
- } else {
- componentType = new EspressoResolvedArrayType(elementalType, dimensions - 1, mirror.getComponentType());
- }
- }
- return componentType;
- }
-
- @Override
- public EspressoResolvedArrayType getArrayClass() {
- if (arrayType == null) {
- arrayType = new EspressoResolvedArrayType(elementalType, dimensions + 1, this, findArrayClass(mirror, 1));
- }
- return arrayType;
- }
-
- @Override
- public boolean isHidden() {
- return false;
- }
-
- @Override
- public List getPermittedSubclasses() {
- return null;
- }
-
- @Override
- public EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
- EspressoResolvedJavaType resolvedElementalType = getElementalType().resolve(accessingClass);
- if (resolvedElementalType.equals(elementalType)) {
- return this;
- }
+ protected EspressoResolvedArrayType withNewElementalType(EspressoResolvedJavaType resolvedElementalType) {
return new EspressoResolvedArrayType(resolvedElementalType, dimensions, findArrayClass(resolvedElementalType.getMirror(), dimensions));
}
- @Override
- public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
- return getElementalType().isDefinitelyResolvedWithRespectTo(accessingClass);
- }
-
- static native Class> findArrayClass(Class> base, int dimensionsDelta);
-
- @Override
- public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
- return runtime().getJavaLangObject().resolveMethod(method, callerType);
- }
-
- @Override
- public Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
- throw JVMCIError.unimplemented();
- }
-
- @Override
- public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
- return NO_FIELDS;
- }
-
- @Override
- public ResolvedJavaField[] getStaticFields() {
- return NO_FIELDS;
- }
-
- @Override
- public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
- return null;
- }
-
- @Override
- public String getSourceFileName() {
- return null;
- }
-
- @Override
- public boolean isLocal() {
- return false;
- }
-
- @Override
- public boolean isMember() {
- return false;
- }
-
- @Override
- public ResolvedJavaType[] getDeclaredTypes() {
- return new ResolvedJavaType[0];
- }
-
- @Override
- public ResolvedJavaType getEnclosingType() {
- return null;
- }
-
- @Override
- public ResolvedJavaMethod getEnclosingMethod() {
- return null;
- }
-
- @Override
- public ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
- return NO_METHODS;
- }
-
- @Override
- public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
- return NO_METHODS;
- }
-
- @Override
- public List getAllMethods(boolean forceLink) {
- return Collections.emptyList();
- }
-
- @Override
- public ResolvedJavaMethod getClassInitializer() {
- return null;
- }
-
- @Override
- public boolean isCloneableWithAllocation() {
- throw JVMCIError.unimplemented();
- }
-
- @Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- return null;
- }
-
- @Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- return null;
- }
-
- @Override
- public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
- return getElementalType().lookupType(unresolvedJavaType, resolve);
- }
-
- @Override
- public boolean isRecord() {
- return false;
- }
-
- @Override
- public List extends ResolvedJavaRecordComponent> getRecordComponents() {
- return null;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EspressoResolvedArrayType that = (EspressoResolvedArrayType) o;
- return dimensions == that.dimensions && Objects.equals(elementalType, that.elementalType);
+ static Class> findArrayClass(Class> base, int dimensionsDelta) {
+ return Array.newInstance(base, new int[dimensionsDelta]).getClass();
}
@Override
- public int hashCode() {
- return Objects.hash(elementalType, dimensions);
+ protected EspressoResolvedObjectType getObjectType(JavaConstant obj) {
+ return ((EspressoObjectConstant) obj).getType();
}
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType.java
index a715bb6343a7..ceb2ff310fd7 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,255 +23,89 @@
package com.oracle.truffle.espresso.jvmci.meta;
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ANNOTATION;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ENUM;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.FINALIZER;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
-import static java.lang.reflect.Modifier.ABSTRACT;
-import static java.lang.reflect.Modifier.FINAL;
-import static java.lang.reflect.Modifier.INTERFACE;
-import static java.lang.reflect.Modifier.PUBLIC;
-import static java.util.Objects.requireNonNull;
+import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedArrayType.findArrayClass;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
-import jdk.vm.ci.common.JVMCIError;
-import jdk.vm.ci.meta.Assumptions;
-import jdk.vm.ci.meta.Assumptions.AssumptionResult;
-import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.MetaAccessProvider;
-import jdk.vm.ci.meta.ModifiersProvider;
-import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
-import jdk.vm.ci.meta.UnresolvedJavaField;
-import jdk.vm.ci.meta.UnresolvedJavaType;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
-
-public final class EspressoResolvedInstanceType extends EspressoResolvedObjectType {
- private static final int JVM_CLASS_MODIFIERS = PUBLIC | FINAL | INTERFACE | ABSTRACT | ANNOTATION | ENUM | SYNTHETIC;
- private static final SortByOffset fieldSortingMethod = new SortByOffset();
-
- private static final class SortByOffset implements Comparator {
- @Override
- public int compare(ResolvedJavaField a, ResolvedJavaField b) {
- return a.getOffset() - b.getOffset();
- }
- }
-
- private static final EspressoResolvedInstanceType[] NO_INSTANCE_TYPES = new EspressoResolvedInstanceType[0];
+public final class EspressoResolvedInstanceType extends AbstractEspressoResolvedInstanceType {
private final EspressoConstantPool constantPool;
- private EspressoResolvedJavaField[] instanceFields;
- private EspressoResolvedJavaField[] staticFields;
- private EspressoResolvedInstanceType[] interfaces;
- private List recordComponents;
- private EspressoResolvedInstanceType superClass;
- private String name;
@SuppressWarnings("this-escape")
- public EspressoResolvedInstanceType() {
- constantPool = new EspressoConstantPool(this);
+ EspressoResolvedInstanceType() {
+ this.constantPool = new EspressoConstantPool(this);
}
@Override
- public boolean hasFinalizer() {
- return (getFlags() & FINALIZER) != 0;
+ public EspressoConstantPool getConstantPool() {
+ return constantPool;
}
@Override
- public AssumptionResult hasFinalizableSubclass() {
- return new Assumptions.AssumptionResult<>(true);
- }
+ public native boolean isInitialized();
@Override
- public boolean isArray() {
- return false;
- }
+ public native void initialize();
@Override
- public int getModifiers() {
- return getFlags() & JVM_CLASS_MODIFIERS;
- }
-
- private native int getFlags();
+ public native boolean isLinked();
@Override
- public boolean isInterface() {
- return Modifier.isInterface(getFlags());
- }
+ public native boolean declaresDefaultMethods();
@Override
- public boolean isInstanceClass() {
- return !isInterface();
- }
+ public native boolean hasDefaultMethods();
@Override
- public boolean isEnum() {
- return (getFlags() & ENUM) != 0;
- }
+ public native String getSourceFileName();
@Override
- public native boolean isInitialized();
+ public native void link();
@Override
- public native void initialize();
+ public native EspressoResolvedJavaMethod getClassInitializer();
@Override
- public native boolean isLinked();
+ public native int hashCode();
@Override
- public boolean isAssignableFrom(ResolvedJavaType other) {
- requireNonNull(other);
- if (other instanceof EspressoResolvedInstanceType otherType) {
- return getMirror().isAssignableFrom(otherType.getMirror());
- }
- if (other instanceof EspressoResolvedArrayType) {
- if (this.equals(runtime().getJavaLangObject())) {
- return true;
- }
- if (this.isInterface()) {
- for (EspressoResolvedInstanceType iface : runtime().getArrayInterfaces()) {
- if (this.equals(iface)) {
- return true;
- }
- }
- }
- }
- return false;
- }
+ protected native int getVtableLength();
@Override
- public native boolean declaresDefaultMethods();
+ protected native Class> getMirror0();
@Override
- public native boolean hasDefaultMethods();
+ protected native byte[] getRawAnnotationBytes(int category);
@Override
- public EspressoResolvedInstanceType getSuperclass() {
- if (isInterface()) {
- return null;
- }
- EspressoResolvedInstanceType javaLangObject = runtime().getJavaLangObject();
- if (this.equals(javaLangObject)) {
- return null;
- }
- // Cache result of native call
- if (superClass == null) {
- superClass = getSuperclass0();
- }
- return superClass;
- }
-
- private native EspressoResolvedInstanceType getSuperclass0();
+ protected native int getFlags();
@Override
- public ResolvedJavaType[] getInterfaces() {
- if (interfaces == null) {
- interfaces = getInterfaces0();
- if (interfaces == null) {
- interfaces = NO_INSTANCE_TYPES;
- }
- }
- return interfaces;
- }
-
- private native EspressoResolvedInstanceType[] getInterfaces0();
+ protected native EspressoResolvedInstanceType getSuperclass0();
@Override
- public EspressoResolvedInstanceType getSingleImplementor() {
- if (!isInterface()) {
- throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
- }
- // espresso only supports finding a leaf concrete implementor.
- // if there is one, it's also the only implementor that matter since others cannot be
- // instanciated
- EspressoResolvedInstanceType implementor = espressoSingleImplementor();
- if (implementor == null) {
- return this;
- }
- assert implementor.isConcrete();
- assert this.isAssignableFrom(implementor);
- // find the first class that implements the interface
- while (true) {
- EspressoResolvedInstanceType superclass = implementor.getSuperclass();
- if (!this.isAssignableFrom(superclass)) {
- return implementor;
- }
- implementor = superclass;
- }
- }
-
- native int getVtableLength();
-
- private boolean directlyImplements(EspressoResolvedInstanceType iface) {
- for (ResolvedJavaType i : getInterfaces()) {
- if (i.equals(iface)) {
- return true;
- }
- }
- return false;
- }
+ protected native EspressoResolvedInstanceType[] getInterfaces0();
@Override
- public EspressoResolvedObjectType getSupertype() {
- if (isInterface()) {
- return runtime().getJavaLangObject();
- }
- return getSuperclass();
- }
+ protected native EspressoResolvedJavaRecordComponent[] getRecordComponents0();
@Override
- public AssumptionResult findLeafConcreteSubtype() {
- if (isLeaf()) {
- // No assumptions are required.
- return new AssumptionResult<>(this);
- }
- if (isLeafClass()) {
- return new AssumptionResult<>(this, new Assumptions.LeafType(this));
- }
- if (isAbstract()) {
- EspressoResolvedInstanceType espressoSingleImplementor = espressoSingleImplementor();
- if (espressoSingleImplementor != null) {
- return leafConcreteSubtype(espressoSingleImplementor);
- }
- }
- return null;
- }
-
- private native EspressoResolvedInstanceType espressoSingleImplementor();
-
- private AssumptionResult leafConcreteSubtype(EspressoResolvedInstanceType type) {
- if (type.isLeaf()) {
- return new AssumptionResult<>(type, new Assumptions.ConcreteSubtype(this, type));
- } else {
- return new AssumptionResult<>(type, new Assumptions.LeafType(type), new Assumptions.ConcreteSubtype(this, type));
- }
- }
-
- private native boolean isLeafClass();
+ protected native EspressoResolvedInstanceType espressoSingleImplementor();
@Override
- public String getName() {
- if (name == null) {
- name = getName0();
- }
- return name;
- }
-
- private native String getName0();
+ protected native boolean isLeafClass();
@Override
- public ResolvedJavaType getComponentType() {
- return null;
- }
+ protected native String getName0();
@Override
public native boolean isHidden();
@@ -290,183 +124,56 @@ public List getPermittedSubclasses() {
return Collections.unmodifiableList(Arrays.asList(permittedSubtypes));
}
+ @Override
+ public native boolean isRecord();
+
private static native Class>[] getPermittedSubclasses0(Class> mirror);
@Override
- public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
- assert accessingClass != null;
- ResolvedJavaType elementType = getElementalType();
- if (elementType.isPrimitive()) {
- // Primitive type resolution is context free.
- return true;
- }
- if (elementType.getName().startsWith("Ljava/") && hasSameClassLoader(runtime().getJavaLangObject())) {
- // Classes in a java.* package defined by the boot class loader are always resolved.
- return true;
- }
- EspressoResolvedInstanceType otherMirror = (EspressoResolvedInstanceType) accessingClass.getElementalType();
- return hasSameClassLoader(otherMirror);
+ protected boolean hasSameClassLoader(AbstractEspressoResolvedInstanceType otherMirror) {
+ return hasSameClassLoader((EspressoResolvedInstanceType) otherMirror);
}
private native boolean hasSameClassLoader(EspressoResolvedInstanceType otherMirror);
@Override
- public EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
- if (isDefinitelyResolvedWithRespectTo(requireNonNull(accessingClass))) {
- return this;
- }
- EspressoResolvedInstanceType accessingType = (EspressoResolvedInstanceType) accessingClass;
- return (EspressoResolvedJavaType) runtime().lookupType(getName(), accessingType, true);
- }
+ protected native EspressoResolvedJavaField[] getStaticFields0();
@Override
- public EspressoResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
- EspressoResolvedJavaMethod espressoMethod = (EspressoResolvedJavaMethod) method;
- if (isInterface()) {
- // Methods can only be resolved against concrete types
- return null;
- }
- if (espressoMethod.isConcrete() && espressoMethod.getDeclaringClass().equals(this) && espressoMethod.isPublic() && !isSignaturePolymorphicHolder(espressoMethod.getDeclaringClass())) {
- return espressoMethod;
- }
- if (!espressoMethod.getDeclaringClass().isAssignableFrom(this)) {
- return null;
- }
- if (espressoMethod.isConstructor()) {
- // Constructor calls should have been checked in the verifier and the method's
- // declaring class is assignable from this (see above) so treat it as resolved.
- return espressoMethod;
- }
- return runtime().resolveMethod(this, espressoMethod, (EspressoResolvedInstanceType) callerType);
- }
-
- private static boolean isSignaturePolymorphicHolder(ResolvedJavaType type) {
- String name = type.getName();
- return "Ljava/lang/invoke/MethodHandle;".equals(name) || "Ljava/lang/invoke/VarHandle;".equals(name);
- }
+ protected native EspressoResolvedJavaField[] getInstanceFields0();
@Override
- public AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
- EspressoResolvedJavaMethod espressoMethod = (EspressoResolvedJavaMethod) method;
- EspressoResolvedInstanceType declaredHolder = espressoMethod.getDeclaringClass();
- if (!declaredHolder.isAssignableFrom(this) || this.equals(declaredHolder) || !isLinked() || isInterface()) {
- if (espressoMethod.canBeStaticallyBound()) {
- // No assumptions are required.
- return new AssumptionResult<>(espressoMethod);
- }
- if (espressoMethod.isLeafMethod()) {
- return new AssumptionResult<>(espressoMethod, new Assumptions.ConcreteMethod(method, declaredHolder, espressoMethod));
- }
- return null;
- }
+ protected native EspressoResolvedJavaMethod[] getDeclaredConstructors0();
- EspressoResolvedJavaMethod resolvedMethod = resolveMethod(espressoMethod, this);
- if (resolvedMethod == null) {
- // The type isn't known to implement the method.
- return null;
- }
- if (resolvedMethod.canBeStaticallyBound()) {
- // No assumptions are required.
- return new AssumptionResult<>(resolvedMethod);
- }
- if (espressoMethod.isLeafMethod()) {
- return new AssumptionResult<>(espressoMethod, new Assumptions.ConcreteMethod(method, declaredHolder, espressoMethod));
- }
- return null;
- }
+ @Override
+ protected native EspressoResolvedJavaMethod[] getDeclaredMethods0();
@Override
- public EspressoResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
- if (instanceFields == null) {
- if (isInterface()) {
- instanceFields = NO_FIELDS;
- } else {
- boolean needsSort = true;
- EspressoResolvedJavaField[] result = getInstanceFields0();
- if (getSuperclass() != null) {
- EspressoResolvedJavaField[] superFields = getSuperclass().getInstanceFields(true);
- if (superFields.length > 0) {
- if (result.length > 0) {
- EspressoResolvedJavaField[] merged = new EspressoResolvedJavaField[superFields.length + result.length];
- System.arraycopy(superFields, 0, merged, 0, superFields.length);
- System.arraycopy(result, 0, merged, superFields.length, result.length);
- result = merged;
- } else {
- result = superFields;
- needsSort = false;
- }
- }
- }
- if (needsSort) {
- Arrays.sort(result, fieldSortingMethod);
- }
- assert Arrays.stream(result).noneMatch(ModifiersProvider::isStatic);
- instanceFields = result;
- }
- }
- if (includeSuperclasses || getSuperclass() == null) {
- return instanceFields;
- }
- // filter superclass fields out
- int superClassFieldCount = getSuperclass().getInstanceFields(true).length;
- if (superClassFieldCount == instanceFields.length) {
- // This class does not have any instance fields of its own.
- return NO_FIELDS;
- } else if (superClassFieldCount != 0) {
- // Fields of the current class can be interleaved with fields of its super-classes
- // Since they were sorted and we are only removing entries, the result will be sorted
- assert instanceFields.length > superClassFieldCount : this + ": instanceFields.length=" + instanceFields.length + " superClassFieldCount=" + superClassFieldCount;
- EspressoResolvedJavaField[] result = new EspressoResolvedJavaField[instanceFields.length - superClassFieldCount];
- int i = 0;
- for (EspressoResolvedJavaField f : instanceFields) {
- if (f.getDeclaringClass().equals(this)) {
- assert i == 0 || result[i - 1].getOffset() < f.getOffset();
- result[i++] = f;
- }
- }
- return result;
- } else {
- // The super classes of this class do not have any instance fields.
- return instanceFields;
- }
- }
+ protected native EspressoResolvedJavaMethod[] getAllMethods0();
@Override
- public EspressoResolvedJavaField[] getStaticFields() {
- if (staticFields == null) {
- EspressoResolvedJavaField[] result = getStaticFields0();
- Arrays.sort(result, fieldSortingMethod);
- assert Arrays.stream(result).allMatch(ModifiersProvider::isStatic);
- staticFields = result;
+ protected boolean equals0(AbstractEspressoResolvedInstanceType that) {
+ if (that instanceof EspressoResolvedInstanceType espressoInstanceType) {
+ return equals0(espressoInstanceType);
}
- return staticFields;
+ return false;
}
- private native EspressoResolvedJavaField[] getStaticFields0();
-
- private native EspressoResolvedJavaField[] getInstanceFields0();
+ private native boolean equals0(EspressoResolvedInstanceType that);
@Override
- public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
- ResolvedJavaField[] declaredFields = getInstanceFields(true);
- return findFieldWithOffset(offset, expectedKind, declaredFields);
- }
-
- private static ResolvedJavaField findFieldWithOffset(long offset, JavaKind expectedEntryKind, ResolvedJavaField[] declaredFields) {
- for (ResolvedJavaField field : declaredFields) {
- if (field.getOffset() == offset && expectedEntryKind == field.getJavaKind()) {
- return field;
- }
- }
- return null;
+ public boolean isLocal() {
+ return getMirror().isLocalClass();
}
@Override
- public native String getSourceFileName();
+ protected EspressoResolvedInstanceType getJavaLangObject() {
+ return runtime().getJavaLangObject();
+ }
@Override
- public boolean isLocal() {
- return getMirror().isLocalClass();
+ protected boolean isAssignableFrom(AbstractEspressoResolvedInstanceType other) {
+ return getMirror().isAssignableFrom(other.getMirror());
}
@Override
@@ -506,139 +213,27 @@ public ResolvedJavaMethod getEnclosingMethod() {
}
@Override
- public native void link();
-
- @Override
- public EspressoResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
- if (forceLink) {
- link();
- }
- return getDeclaredConstructors0();
- }
-
- private native EspressoResolvedJavaMethod[] getDeclaredConstructors0();
-
- @Override
- public EspressoResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
- if (forceLink) {
- link();
- }
- return getDeclaredMethods0();
- }
-
- private native EspressoResolvedJavaMethod[] getDeclaredMethods0();
-
- @Override
- public List getAllMethods(boolean forceLink) {
- if (forceLink) {
- link();
- }
- return Arrays.asList(getAllMethods0());
- }
-
- private native EspressoResolvedJavaMethod[] getAllMethods0();
-
- @Override
- public native EspressoResolvedJavaMethod getClassInitializer();
-
- @Override
- public boolean isCloneableWithAllocation() {
- throw JVMCIError.unimplemented();
+ protected AbstractEspressoResolvedArrayType getArrayClass0() {
+ return new EspressoResolvedArrayType(this, 1, this, findArrayClass(getMirror(), 1));
}
@Override
- public ResolvedJavaField resolveField(UnresolvedJavaField unresolvedJavaField, ResolvedJavaType accessingClass) {
- for (ResolvedJavaField field : getInstanceFields(false)) {
- if (field.getName().equals(unresolvedJavaField.getName())) {
- return field;
- }
- }
- for (ResolvedJavaField field : getStaticFields()) {
- if (field.getName().equals(unresolvedJavaField.getName())) {
- return field;
- }
- }
- throw new InternalError(unresolvedJavaField.toString());
- }
-
- @Override
- public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
- return lookupType(unresolvedJavaType, this, resolve);
- }
-
- public EspressoConstantPool getConstantPool() {
- return constantPool;
- }
-
- @Override
- protected native Class> getMirror0();
-
- @Override
- public native boolean isRecord();
-
- @Override
- public List extends EspressoResolvedJavaRecordComponent> getRecordComponents() {
- if (!isRecord()) {
- return null;
- }
- if (recordComponents == null) {
- recordComponents = Collections.unmodifiableList(Arrays.asList(getRecordComponents0()));
- }
- return recordComponents;
+ protected AbstractEspressoResolvedInstanceType[] getArrayInterfaces() {
+ return runtime().getArrayInterfaces();
}
- private native EspressoResolvedJavaRecordComponent[] getRecordComponents0();
-
- /// Denotes class file bytes of a `RuntimeVisibleAnnotations` attribute after
- /// the `u2 attribute_name_index; u4 attribute_length` prefix.
- static final int DECLARED_ANNOTATIONS = 0;
-
- /// Denotes class file bytes of a `RuntimeVisibleParameterAnnotations` attribute after
- /// the `u2 attribute_name_index; u4 attribute_length` prefix.
- static final int PARAMETER_ANNOTATIONS = 1;
-
- /// Denotes class file bytes of a `RuntimeVisibleTypeAnnotations` attribute after
- /// the `u2 attribute_name_index; u4 attribute_length` prefix.
- static final int TYPE_ANNOTATIONS = 2;
-
- /// Denotes class file bytes of a `AnnotationDefault` attribute after
- /// the `u2 attribute_name_index; u4 attribute_length` prefix.
- static final int ANNOTATION_DEFAULT_VALUE = 3;
-
@Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- if (isArray()) {
- return null;
- }
- byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
- return AnnotationsInfo.make(bytes, getConstantPool(), this);
+ protected EspressoResolvedJavaMethod resolveMethod0(AbstractEspressoResolvedJavaMethod method, AbstractEspressoResolvedInstanceType callerType) {
+ return runtime().resolveMethod(this, (EspressoResolvedJavaMethod) method, (EspressoResolvedInstanceType) callerType);
}
@Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- if (isArray()) {
- return null;
- }
- byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
- return AnnotationsInfo.make(bytes, getConstantPool(), this);
+ protected JavaType lookupType(String name, AbstractEspressoResolvedInstanceType accessingType, boolean resolve) {
+ return runtime().lookupType(name, (EspressoResolvedInstanceType) accessingType, resolve);
}
- private native byte[] getRawAnnotationBytes(int category);
-
@Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EspressoResolvedInstanceType that = (EspressoResolvedInstanceType) o;
- return equals0(that);
+ protected EspressoResolvedObjectType getObjectType(JavaConstant obj) {
+ return ((EspressoObjectConstant) obj).getType();
}
-
- private native boolean equals0(EspressoResolvedInstanceType that);
-
- @Override
- public native int hashCode();
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField.java
index 5b2f6877f907..a0ef3e4c36e1 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,104 +22,45 @@
*/
package com.oracle.truffle.espresso.jvmci.meta;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.TYPE_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.ENUM;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.HIDDEN;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.STABLE_FIELD;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
-import static java.lang.reflect.Modifier.FINAL;
-import static java.lang.reflect.Modifier.PRIVATE;
-import static java.lang.reflect.Modifier.PROTECTED;
-import static java.lang.reflect.Modifier.PUBLIC;
-import static java.lang.reflect.Modifier.STATIC;
-import static java.lang.reflect.Modifier.TRANSIENT;
-import static java.lang.reflect.Modifier.VOLATILE;
-
import java.lang.reflect.Field;
-import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.UnresolvedJavaType;
-import jdk.vm.ci.meta.annotation.AbstractAnnotated;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
-
-public final class EspressoResolvedJavaField extends AbstractAnnotated implements ResolvedJavaField {
- private static final int JVM_FIELDS_MODIFIERS = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | VOLATILE | TRANSIENT | ENUM | SYNTHETIC;
- private final EspressoResolvedInstanceType holder;
- private String name;
- private JavaType type;
+public final class EspressoResolvedJavaField extends AbstractEspressoResolvedJavaField {
private Field mirrorCache;
- public EspressoResolvedJavaField(EspressoResolvedInstanceType holder) {
- this.holder = holder;
- }
-
- @Override
- public int getModifiers() {
- return getFlags() & JVM_FIELDS_MODIFIERS;
+ EspressoResolvedJavaField(EspressoResolvedInstanceType holder) {
+ super(holder);
}
- private native int getFlags();
-
@Override
public native int getOffset();
@Override
- public boolean isInternal() {
- return (getFlags() & HIDDEN) != 0;
- }
+ protected native int getFlags();
@Override
- public boolean isSynthetic() {
- return (getFlags() & SYNTHETIC) != 0;
- }
-
- public boolean isStable() {
- return (getFlags() & STABLE_FIELD) != 0;
- }
+ protected native String getName0();
@Override
- public String getName() {
- if (name == null) {
- name = getName0();
- }
- return name;
- }
+ protected native JavaType getType0(UnresolvedJavaType unresolved);
- private native String getName0();
+ @Override
+ protected native int getConstantValueIndex();
@Override
- public JavaType getType() {
- // Pull field into local variable to prevent a race causing
- // a ClassCastException below
- JavaType currentType = type;
- if (currentType == null || currentType instanceof UnresolvedJavaType) {
- // Don't allow unresolved types to hang around forever
- type = getType0((UnresolvedJavaType) currentType);
+ protected boolean equals0(AbstractEspressoResolvedJavaField that) {
+ if (that instanceof EspressoResolvedJavaField espressoResolvedJavaField) {
+ return equals0(espressoResolvedJavaField);
}
- return type;
+ return false;
}
- private native JavaType getType0(UnresolvedJavaType unresolved);
-
- @Override
- public EspressoResolvedInstanceType getDeclaringClass() {
- return holder;
- }
+ private native boolean equals0(EspressoResolvedJavaField that);
@Override
- public JavaConstant getConstantValue() {
- int constantValueIndex = getConstantValueIndex();
- if (constantValueIndex == 0) {
- return null;
- }
- return (JavaConstant) holder.getConstantPool().lookupConstant(constantValueIndex);
- }
-
- private native int getConstantValueIndex();
+ public native int hashCode();
public Field getMirror() {
if (mirrorCache == null) {
@@ -131,40 +72,5 @@ public Field getMirror() {
private native Field getMirror0();
@Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
- EspressoResolvedInstanceType container = getDeclaringClass();
- return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
- }
-
- @Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
- EspressoResolvedInstanceType container = getDeclaringClass();
- return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
- }
-
- private native byte[] getRawAnnotationBytes(int category);
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EspressoResolvedJavaField that = (EspressoResolvedJavaField) o;
- return equals0(that);
- }
-
- private native boolean equals0(EspressoResolvedJavaField that);
-
- @Override
- public native int hashCode();
-
- @Override
- public String toString() {
- return format("EspressoResolvedJavaField<%H.%n %t:") + getOffset() + ">";
- }
+ protected native byte[] getRawAnnotationBytes(int category);
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod.java
index 6395331d3d83..a130f57a0efd 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,109 +22,21 @@
*/
package com.oracle.truffle.espresso.jvmci.meta;
-import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.ANNOTATION_DEFAULT_VALUE;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.PARAMETER_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.TYPE_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.BRIDGE;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SCOPED_METHOD;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.SYNTHETIC;
-import static com.oracle.truffle.espresso.jvmci.meta.ExtendedModifiers.VARARGS;
-import static java.lang.reflect.Modifier.ABSTRACT;
-import static java.lang.reflect.Modifier.FINAL;
-import static java.lang.reflect.Modifier.NATIVE;
-import static java.lang.reflect.Modifier.PRIVATE;
-import static java.lang.reflect.Modifier.PROTECTED;
-import static java.lang.reflect.Modifier.PUBLIC;
-import static java.lang.reflect.Modifier.STATIC;
-import static java.lang.reflect.Modifier.STRICT;
-import static java.lang.reflect.Modifier.SYNCHRONIZED;
-
import java.lang.reflect.Executable;
-import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
-import jdk.vm.ci.common.JVMCIError;
-import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.DefaultProfilingInfo;
import jdk.vm.ci.meta.ExceptionHandler;
import jdk.vm.ci.meta.LineNumberTable;
import jdk.vm.ci.meta.LocalVariableTable;
-import jdk.vm.ci.meta.ProfilingInfo;
import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.ResolvedJavaType;
-import jdk.vm.ci.meta.SpeculationLog;
-import jdk.vm.ci.meta.TriState;
-import jdk.vm.ci.meta.annotation.AbstractAnnotated;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
-
-public final class EspressoResolvedJavaMethod extends AbstractAnnotated implements ResolvedJavaMethod {
- private static final int JVM_METHOD_MODIFIERS = PUBLIC | PRIVATE | PROTECTED | STATIC | FINAL | SYNCHRONIZED | BRIDGE | VARARGS | NATIVE | ABSTRACT | STRICT | SYNTHETIC;
- public static final Parameter[] NO_PARAMETERS = new Parameter[0];
- private final EspressoResolvedInstanceType holder;
- private final boolean poisonPill;
+public final class EspressoResolvedJavaMethod extends AbstractEspressoResolvedJavaMethod {
private Executable mirrorCache;
- private String nameCache;
- private byte[] code;
- private EspressoSignature signature;
-
- private EspressoResolvedJavaMethod(EspressoResolvedInstanceType holder, boolean poisonPill) {
- this.holder = holder;
- this.poisonPill = poisonPill;
- }
-
- @Override
- public byte[] getCode() {
- if (getCodeSize() == 0) {
- return null;
- }
- if (code == null && holder.isLinked()) {
- code = getCode0();
- assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length;
- }
- return code;
- }
-
- private native byte[] getCode0();
- @Override
- public int getCodeSize() {
- int codeSize = getCodeSize0();
- if (codeSize > 0 && !getDeclaringClass().isLinked()) {
- return -1;
- }
- return codeSize;
+ EspressoResolvedJavaMethod(EspressoResolvedInstanceType holder, boolean poisonPill) {
+ super(holder, poisonPill);
}
- private native int getCodeSize0();
-
- @Override
- public String getName() {
- if (nameCache == null) {
- nameCache = getName0();
- }
- return nameCache;
- }
-
- private native String getName0();
-
- @Override
- public EspressoResolvedInstanceType getDeclaringClass() {
- return holder;
- }
-
- @Override
- public EspressoSignature getSignature() {
- if (signature == null) {
- signature = new EspressoSignature(getRawSignature());
- }
- return signature;
- }
-
- private native String getRawSignature();
-
@Override
public native int getMaxLocals();
@@ -132,163 +44,81 @@ public EspressoSignature getSignature() {
public native int getMaxStackSize();
@Override
- public boolean isSynthetic() {
- return (getFlags() & SYNTHETIC) != 0;
- }
-
- @Override
- public boolean isVarArgs() {
- return (getFlags() & VARARGS) != 0;
- }
-
- @Override
- public boolean isBridge() {
- return (getFlags() & BRIDGE) != 0;
- }
-
- @Override
- public boolean isDefault() {
- // Copied from java.lang.Method.isDefault()
- int mask = Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC;
- return ((getModifiers() & mask) == Modifier.PUBLIC) && getDeclaringClass().isInterface();
- }
-
- @Override
- public boolean isDeclared() {
- if (isConstructor() || isClassInitializer()) {
- return false;
- }
- return !poisonPill;
- }
-
- @Override
- public boolean isClassInitializer() {
- return isStatic() && "".equals(getName());
- }
-
- @Override
- public boolean isConstructor() {
- return !isStatic() && "".equals(getName());
- }
+ public native ExceptionHandler[] getExceptionHandlers();
@Override
- public boolean canBeStaticallyBound() {
- return (isFinal() || isPrivate() || isStatic() || holder.isLeaf() || isConstructor()) && isConcrete();
- }
+ public native StackTraceElement asStackTraceElement(int bci);
@Override
- public native ExceptionHandler[] getExceptionHandlers();
+ public native boolean hasNeverInlineDirective();
@Override
- public native StackTraceElement asStackTraceElement(int bci);
+ public native LineNumberTable getLineNumberTable();
@Override
- public ProfilingInfo getProfilingInfo(boolean includeNormal, boolean includeOSR) {
- // Be optimistic and return false for exceptionSeen?
- return DefaultProfilingInfo.get(TriState.FALSE);
- }
+ public native LocalVariableTable getLocalVariableTable();
@Override
- public void reprofile() {
- throw JVMCIError.unimplemented();
- }
+ protected native byte[] getCode0();
@Override
- public EspressoConstantPool getConstantPool() {
- return holder.getConstantPool();
- }
+ protected native int getCodeSize0();
@Override
- public Type[] getGenericParameterTypes() {
- return getMirror().getGenericParameterTypes();
- }
+ protected native String getName0();
@Override
- public boolean canBeInlined() {
- if (isForceInline()) {
- return true;
- }
- if (hasNeverInlineDirective()) {
- return false;
- }
- return hasBytecodes();
+ protected AbstractEspressoSignature getSignature0() {
+ return new EspressoSignature(getRawSignature());
}
- private native boolean isForceInline();
+ private native String getRawSignature();
@Override
- public native boolean hasNeverInlineDirective();
+ protected native boolean isForceInline();
@Override
- public boolean shouldBeInlined() {
- return isForceInline();
- }
+ protected native int getVtableIndexForInterfaceMethod(EspressoResolvedInstanceType resolved);
@Override
- public native LineNumberTable getLineNumberTable();
+ protected native int getVtableIndex();
@Override
- public native LocalVariableTable getLocalVariableTable();
+ protected native int getFlags();
@Override
- public Constant getEncoding() {
- throw JVMCIError.unimplemented();
- }
+ protected native boolean isLeafMethod();
@Override
- public boolean isInVirtualMethodTable(ResolvedJavaType resolved) {
- EspressoResolvedInstanceType espressoResolved;
- if (resolved instanceof EspressoResolvedInstanceType) {
- espressoResolved = (EspressoResolvedInstanceType) resolved;
- } else if (resolved instanceof EspressoResolvedArrayType) {
- espressoResolved = runtime().getJavaLangObject();
- } else {
- return false;
+ protected boolean equals0(AbstractEspressoResolvedJavaMethod that) {
+ if (that instanceof EspressoResolvedJavaMethod espressoResolvedJavaMethod) {
+ return equals0(espressoResolvedJavaMethod);
}
- int vtableIndex = getVtableIndex(espressoResolved);
- return vtableIndex >= 0 && vtableIndex < espressoResolved.getVtableLength();
+ return false;
}
- private int getVtableIndex(EspressoResolvedObjectType resolved) {
- if (!holder.isLinked()) {
- return -1;
- }
- if (holder.isInterface()) {
- if (resolved.isInterface() || !resolved.isLinked() || !getDeclaringClass().isAssignableFrom(resolved)) {
- return -1;
- }
- EspressoResolvedInstanceType type;
- if (resolved instanceof EspressoResolvedArrayType) {
- type = runtime().getJavaLangObject();
- } else {
- type = (EspressoResolvedInstanceType) resolved;
- }
- return getVtableIndexForInterfaceMethod(type);
- }
- return getVtableIndex();
- }
-
- private native int getVtableIndexForInterfaceMethod(EspressoResolvedInstanceType resolved);
-
- private native int getVtableIndex();
+ private native boolean equals0(EspressoResolvedJavaMethod that);
@Override
- public SpeculationLog getSpeculationLog() {
- throw JVMCIError.unimplemented();
+ protected native int hashCode0();
+
+ public Executable getMirror() {
+ if (mirrorCache == null) {
+ mirrorCache = getMirror0();
+ }
+ return mirrorCache;
}
- private native boolean hasAnnotations();
+ private native Executable getMirror0();
@Override
- public int getModifiers() {
- return getFlags() & JVM_METHOD_MODIFIERS;
+ public Type[] getGenericParameterTypes() {
+ return getMirror().getGenericParameterTypes();
}
- private native int getFlags();
-
@Override
public Parameter[] getParameters() {
- if (signature.getParameterCount(false) == 0) {
+ if (getSignature().getParameterCount(false) == 0) {
return NO_PARAMETERS;
}
java.lang.reflect.Parameter[] javaParameters = getMirror().getParameters();
@@ -301,74 +131,9 @@ public Parameter[] getParameters() {
return res;
}
- public Executable getMirror() {
- if (mirrorCache == null) {
- mirrorCache = getMirror0();
- }
- return mirrorCache;
- }
-
- private native Executable getMirror0();
-
- public native boolean isLeafMethod();
-
- @Override
- public boolean isScoped() {
- return (getFlags() & SCOPED_METHOD) != 0;
- }
-
- @Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- if (!hasAnnotations()) {
- return null;
- }
- byte[] bytes = getRawAnnotationBytes(DECLARED_ANNOTATIONS);
- return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
- }
-
- @Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(TYPE_ANNOTATIONS);
- return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
- }
-
- @Override
- public AnnotationsInfo getAnnotationDefaultInfo() {
- byte[] bytes = getRawAnnotationBytes(ANNOTATION_DEFAULT_VALUE);
- return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
- }
-
@Override
- public AnnotationsInfo getParameterAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(PARAMETER_ANNOTATIONS);
- return AnnotationsInfo.make(bytes, getConstantPool(), getDeclaringClass());
- }
-
- private native byte[] getRawAnnotationBytes(int category);
+ protected native boolean hasAnnotations();
@Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- EspressoResolvedJavaMethod that = (EspressoResolvedJavaMethod) o;
- return this.poisonPill == that.poisonPill && equals0(that);
- }
-
- private native boolean equals0(EspressoResolvedJavaMethod that);
-
- @Override
- public int hashCode() {
- return 13 * Boolean.hashCode(poisonPill) + hashCode0();
- }
-
- private native int hashCode0();
-
- @Override
- public String toString() {
- return format("EspressoResolvedJavaMethod<%h.%n(%p)>");
- }
+ protected native byte[] getRawAnnotationBytes(int category);
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaRecordComponent.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaRecordComponent.java
index dfe123c952e5..7e3b42590285 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaRecordComponent.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaRecordComponent.java
@@ -23,77 +23,17 @@
package com.oracle.truffle.espresso.jvmci.meta;
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.DECLARED_ANNOTATIONS;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedInstanceType.TYPE_ANNOTATIONS;
-import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
-import jdk.vm.ci.meta.annotation.AbstractAnnotated;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
-
-public final class EspressoResolvedJavaRecordComponent extends AbstractAnnotated implements ResolvedJavaRecordComponent {
- private final EspressoResolvedInstanceType declaringRecord;
- private final int index;
- private final String name;
- private final JavaType type;
+final class EspressoResolvedJavaRecordComponent extends AbstractEspressoResolvedJavaRecordComponent {
EspressoResolvedJavaRecordComponent(EspressoResolvedInstanceType declaringRecord, int recordIndex, int nameIndex, int typeIndex) {
- this.declaringRecord = declaringRecord;
- this.index = recordIndex;
- this.name = declaringRecord.getConstantPool().lookupUtf8(nameIndex);
- this.type = runtime().lookupType(declaringRecord.getConstantPool().lookupUtf8(typeIndex), declaringRecord, false);
- }
-
- @Override
- public EspressoResolvedInstanceType getDeclaringRecord() {
- return declaringRecord;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public JavaType getType() {
- return type;
- }
-
- @Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(declaringRecord, index, DECLARED_ANNOTATIONS);
- EspressoResolvedInstanceType container = getDeclaringRecord();
- return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
- }
-
- @Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- byte[] bytes = getRawAnnotationBytes(declaringRecord, index, TYPE_ANNOTATIONS);
- EspressoResolvedInstanceType container = getDeclaringRecord();
- return AnnotationsInfo.make(bytes, container.getConstantPool(), container);
+ super(declaringRecord, recordIndex, nameIndex, runtime().lookupType(declaringRecord.getConstantPool().lookupUtf8(typeIndex), declaringRecord, false));
}
- private static native byte[] getRawAnnotationBytes(EspressoResolvedInstanceType declaringRecord, int index, int category);
-
@Override
- public int hashCode() {
- return declaringRecord.hashCode() + index * 31;
+ protected byte[] getRawAnnotationBytes(int category) {
+ return getRawAnnotationBytes0((EspressoResolvedInstanceType) getDeclaringRecord(), getIndex(), category);
}
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof EspressoResolvedJavaRecordComponent other)) {
- return false;
- }
- /*
- * No need for a native equals0 helper (cf EspressoResolvedJavaField.equals0) as there is no
- * metadata object that needs an identity equality check.
- */
- return other.index == this.index && other.declaringRecord.equals(this.declaringRecord);
- }
-
- @Override
- public String toString() {
- return "EspressoResolvedJavaRecordComponent<" + declaringRecord.getName() + "." + name + " " + type.getUnqualifiedName() + ">";
- }
+ private static native byte[] getRawAnnotationBytes0(EspressoResolvedInstanceType declaringRecord, int index, int category);
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaType.java
index a83b638b9882..c1d6d788f30f 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaType.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,15 +22,10 @@
*/
package com.oracle.truffle.espresso.jvmci.meta;
-import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedArrayType.findArrayClass;
-
import java.lang.annotation.Annotation;
-import jdk.vm.ci.meta.JavaType;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
-import jdk.vm.ci.meta.UnresolvedJavaType;
import jdk.vm.ci.meta.annotation.AbstractAnnotated;
public abstract class EspressoResolvedJavaType extends AbstractAnnotated implements ResolvedJavaType {
@@ -38,16 +33,18 @@ public abstract class EspressoResolvedJavaType extends AbstractAnnotated impleme
protected static final EspressoResolvedJavaField[] NO_FIELDS = new EspressoResolvedJavaField[0];
protected static final ResolvedJavaType[] NO_TYPES = new ResolvedJavaType[0];
protected static final ResolvedJavaMethod[] NO_METHODS = new ResolvedJavaMethod[0];
- protected EspressoResolvedArrayType arrayType;
+ protected AbstractEspressoResolvedArrayType arrayType;
@Override
- public EspressoResolvedArrayType getArrayClass() {
+ public AbstractEspressoResolvedArrayType getArrayClass() {
if (arrayType == null) {
- arrayType = new EspressoResolvedArrayType(this, 1, this, findArrayClass(getMirror(), 1));
+ arrayType = getArrayClass0();
}
return arrayType;
}
+ protected abstract AbstractEspressoResolvedArrayType getArrayClass0();
+
@Override
public abstract EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass);
@@ -76,12 +73,12 @@ public String toString() {
return getClass().getSimpleName() + "<" + getName() + ">";
}
- static ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, EspressoResolvedInstanceType accessingType, boolean resolve) {
- JavaType javaType = runtime().lookupType(unresolvedJavaType.getName(), accessingType, resolve);
- if (javaType instanceof ResolvedJavaType resolved) {
- return resolved;
- }
- return null;
- }
+ @Override
+ public abstract void link();
+
+ @Override
+ public abstract boolean declaresDefaultMethods();
+ @Override
+ public abstract boolean hasDefaultMethods();
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedObjectType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedObjectType.java
index b480078f9cf8..707e4c205839 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedObjectType.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedObjectType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -45,9 +45,11 @@ public boolean isInstance(JavaConstant obj) {
if (obj.isNull() || !obj.getJavaKind().isObject()) {
return false;
}
- return isAssignableFrom(((EspressoObjectConstant) obj).getType());
+ return isAssignableFrom(getObjectType(obj));
}
+ protected abstract EspressoResolvedObjectType getObjectType(JavaConstant obj);
+
@Override
public final Class> getMirror() {
if (mirrorCache == null) {
@@ -62,6 +64,10 @@ public final Class> getMirror() {
@Override
public abstract ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve);
+ protected abstract AbstractEspressoResolvedInstanceType getJavaLangObject();
+
+ protected abstract AbstractEspressoResolvedInstanceType[] getArrayInterfaces();
+
@Override
public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) {
if (otherType.isPrimitive()) {
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedPrimitiveType.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedPrimitiveType.java
index cf4f05458ec8..b0d1819071df 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedPrimitiveType.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoResolvedPrimitiveType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,25 +23,14 @@
package com.oracle.truffle.espresso.jvmci.meta;
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import static java.util.Objects.requireNonNull;
+import static com.oracle.truffle.espresso.jvmci.meta.EspressoResolvedArrayType.findArrayClass;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.List;
-
-import jdk.vm.ci.common.JVMCIError;
-import jdk.vm.ci.meta.Assumptions;
-import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaField;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-import jdk.vm.ci.meta.ResolvedJavaRecordComponent;
import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.UnresolvedJavaType;
-import jdk.vm.ci.meta.annotation.AnnotationsInfo;
-public final class EspressoResolvedPrimitiveType extends EspressoResolvedJavaType {
+public final class EspressoResolvedPrimitiveType extends AbstractEspressoResolvedPrimitiveType {
private static final EspressoResolvedPrimitiveType[] primitives;
static {
EspressoResolvedPrimitiveType[] prims = new EspressoResolvedPrimitiveType[JavaKind.Void.getBasicType() + 1];
@@ -57,14 +46,11 @@ public final class EspressoResolvedPrimitiveType extends EspressoResolvedJavaTyp
primitives = prims;
}
- private final JavaKind kind;
-
private EspressoResolvedPrimitiveType(JavaKind kind) {
- assert kind.isPrimitive();
- this.kind = kind;
+ super(kind);
}
- public static EspressoResolvedPrimitiveType forKind(JavaKind kind) {
+ static EspressoResolvedPrimitiveType forKind(JavaKind kind) {
if (!kind.isPrimitive()) {
throw new IllegalArgumentException("Not a primitive kind: " + kind);
}
@@ -78,267 +64,17 @@ private static EspressoResolvedPrimitiveType forBasicType(int basicType) {
return primitives[basicType];
}
- @Override
- public boolean hasFinalizer() {
- return false;
- }
-
- @Override
- public Assumptions.AssumptionResult hasFinalizableSubclass() {
- return new Assumptions.AssumptionResult<>(false);
- }
-
- @Override
- public int getModifiers() {
- return Modifier.ABSTRACT | Modifier.FINAL | Modifier.PUBLIC;
- }
-
- @Override
- public boolean isInterface() {
- return false;
- }
-
- @Override
- public boolean isInstanceClass() {
- return false;
- }
-
- @Override
- public boolean isPrimitive() {
- return true;
- }
-
- @Override
- public boolean isEnum() {
- return false;
- }
-
- @Override
- public boolean isInitialized() {
- return true;
- }
-
- @Override
- public void initialize() {
- }
-
- @Override
- public boolean isLinked() {
- return true;
- }
-
- @Override
- public void link() {
- }
-
- @Override
- public boolean hasDefaultMethods() {
- return false;
- }
-
- @Override
- public boolean declaresDefaultMethods() {
- return false;
- }
-
- @Override
- public boolean isAssignableFrom(ResolvedJavaType other) {
- assert other != null;
- return other.equals(this);
- }
-
- @Override
- public boolean isInstance(JavaConstant obj) {
- return obj.getJavaKind() == kind;
- }
-
- @Override
- public ResolvedJavaType getSuperclass() {
- return null;
- }
-
- @Override
- public ResolvedJavaType[] getInterfaces() {
- return NO_TYPES;
- }
-
- @Override
- public ResolvedJavaType getSingleImplementor() {
- throw new JVMCIError("Cannot call getSingleImplementor() on a non-interface type: %s", this);
- }
-
- @Override
- public ResolvedJavaType findLeastCommonAncestor(ResolvedJavaType otherType) {
- return null;
- }
-
- @Override
- public Assumptions.AssumptionResult findLeafConcreteSubtype() {
- return new Assumptions.AssumptionResult<>(this);
- }
-
- @Override
- public String getName() {
- return String.valueOf(kind.getTypeChar());
- }
-
- @Override
- public ResolvedJavaType getComponentType() {
- return null;
- }
-
- @Override
- public boolean isHidden() {
- return false;
- }
-
- @Override
- public List getPermittedSubclasses() {
- return null;
- }
-
- @Override
- public JavaKind getJavaKind() {
- return kind;
- }
-
- @Override
- public EspressoResolvedJavaType resolve(ResolvedJavaType accessingClass) {
- requireNonNull(accessingClass);
- return this;
- }
-
- @Override
- public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass) {
- requireNonNull(accessingClass);
- return true;
- }
-
- @Override
- public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) {
- return null;
- }
-
- @Override
- public Assumptions.AssumptionResult findUniqueConcreteMethod(ResolvedJavaMethod method) {
- return null;
- }
-
- @Override
- public ResolvedJavaField[] getInstanceFields(boolean includeSuperclasses) {
- return NO_FIELDS;
- }
-
- @Override
- public ResolvedJavaField[] getStaticFields() {
- return NO_FIELDS;
- }
-
- @Override
- public ResolvedJavaField findInstanceFieldWithOffset(long offset, JavaKind expectedKind) {
- return null;
- }
-
- @Override
- public String getSourceFileName() {
- return null;
- }
-
- @Override
- public boolean isLocal() {
- return false;
- }
-
- @Override
- public boolean isMember() {
- return false;
- }
-
- @Override
- public ResolvedJavaType[] getDeclaredTypes() {
- return new ResolvedJavaType[0];
- }
-
- @Override
- public ResolvedJavaType getEnclosingType() {
- return null;
- }
-
- @Override
- public ResolvedJavaMethod getEnclosingMethod() {
- return null;
- }
-
- @Override
- public ResolvedJavaMethod[] getDeclaredMethods(boolean forceLink) {
- return NO_METHODS;
- }
-
- @Override
- public ResolvedJavaMethod[] getDeclaredConstructors(boolean forceLink) {
- return NO_METHODS;
- }
-
- @Override
- public List getAllMethods(boolean forceLink) {
- return Collections.emptyList();
- }
-
- @Override
- public ResolvedJavaMethod getClassInitializer() {
- return null;
- }
-
- @Override
- public boolean isCloneableWithAllocation() {
- return false;
- }
-
@Override
public ResolvedJavaType lookupType(UnresolvedJavaType unresolvedJavaType, boolean resolve) {
- return lookupType(unresolvedJavaType, runtime().getJavaLangObject(), resolve);
- }
-
- @Override
- public AnnotationsInfo getRawDeclaredAnnotationInfo() {
- return null;
- }
-
- @Override
- public AnnotationsInfo getTypeAnnotationInfo() {
- return null;
- }
-
- @Override
- public Class> getMirror() {
- return kind.toJavaClass();
- }
-
- @Override
- public boolean isArray() {
- return false;
- }
-
- @Override
- public boolean isRecord() {
- return false;
- }
-
- @Override
- public List extends ResolvedJavaRecordComponent> getRecordComponents() {
- return null;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof EspressoResolvedPrimitiveType that)) {
- return false;
+ JavaType javaType = runtime().lookupType(unresolvedJavaType.getName(), runtime().getJavaLangObject(), resolve);
+ if (javaType instanceof ResolvedJavaType resolved) {
+ return resolved;
}
- return that.kind == kind;
+ return null;
}
@Override
- public int hashCode() {
- return kind.hashCode();
+ protected AbstractEspressoResolvedArrayType getArrayClass0() {
+ return new EspressoResolvedArrayType(this, 1, this, findArrayClass(getMirror(), 1));
}
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoSignature.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoSignature.java
index f54f775fa431..f9d7a4519ab0 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoSignature.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/EspressoSignature.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,201 +24,27 @@
import static com.oracle.truffle.espresso.jvmci.EspressoJVMCIRuntime.runtime;
-import java.util.ArrayList;
-import java.util.List;
-
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaType;
-import jdk.vm.ci.meta.ResolvedJavaType;
import jdk.vm.ci.meta.Signature;
-import jdk.vm.ci.meta.UnresolvedJavaType;
/**
* Implementation of {@link Signature} that caches espresso types. Mostly a copy of
* jdk.vm.ci.hotspot.HotSpotSignature.
*/
-public final class EspressoSignature implements Signature {
- private final List parameters = new ArrayList<>();
- private final String returnType;
- private final String rawSignature;
- private EspressoResolvedJavaType[] parameterTypesCache;
- private EspressoResolvedJavaType returnTypeCache;
-
- public EspressoSignature(String rawSignature) {
- if (rawSignature.isEmpty()) {
- throw new IllegalArgumentException("Signature cannot be empty");
- }
- this.rawSignature = rawSignature;
- if (rawSignature.charAt(0) == '(') {
- int cur = 1;
- while (cur < rawSignature.length() && rawSignature.charAt(cur) != ')') {
- int nextCur = parseSignature(rawSignature, cur);
- parameters.add(rawSignature.substring(cur, nextCur));
- cur = nextCur;
- }
-
- cur++;
- int nextCur = parseSignature(rawSignature, cur);
- returnType = rawSignature.substring(cur, nextCur);
- if (nextCur != rawSignature.length()) {
- throw new IllegalArgumentException("Extra characters at end of signature: " + rawSignature);
- }
- } else {
- throw new IllegalArgumentException("Signature must start with a '(': " + rawSignature);
- }
- }
-
- private static int parseSignature(String signature, int start) {
- try {
- int cur = start;
- char first;
- do {
- first = signature.charAt(cur);
- cur++;
- } while (first == '[');
+final class EspressoSignature extends AbstractEspressoSignature {
- switch (first) {
- case 'L':
- while (signature.charAt(cur) != ';') {
- if (signature.charAt(cur) == '.') {
- throw new IllegalArgumentException("Class name in signature contains '.' at index " + cur + ": " + signature);
- }
- cur++;
- }
- cur++;
- break;
- case 'V':
- case 'I':
- case 'B':
- case 'C':
- case 'D':
- case 'F':
- case 'J':
- case 'S':
- case 'Z':
- break;
- default:
- throw new IllegalArgumentException("Invalid character '" + signature.charAt(cur - 1) + "' at index " + (cur - 1) + " in signature: " + signature);
- }
- return cur;
- } catch (StringIndexOutOfBoundsException e) {
- throw new IllegalArgumentException("Truncated signature: " + signature);
- }
- }
-
- @Override
- public int getParameterCount(boolean withReceiver) {
- return parameters.size() + (withReceiver ? 1 : 0);
- }
-
- private static boolean checkValidCache(ResolvedJavaType type, ResolvedJavaType accessingClass) {
- assert accessingClass != null;
- if (type == null) {
- return false;
- } else if (type instanceof EspressoResolvedObjectType) {
- return ((EspressoResolvedObjectType) type).isDefinitelyResolvedWithRespectTo(accessingClass);
- }
- return true;
- }
-
- private static JavaType getUnresolvedOrPrimitiveType(String name) {
- if (name.length() == 1) {
- JavaKind kind = JavaKind.fromPrimitiveOrVoidTypeChar(name.charAt(0));
- return EspressoResolvedPrimitiveType.forKind(kind);
- }
- return UnresolvedJavaType.create(name);
- }
-
- @Override
- public JavaType getParameterType(int index, ResolvedJavaType accessingClass) {
- if (accessingClass == null) {
- // Caller doesn't care about resolution context so return an unresolved
- // or primitive type (primitive type resolution is context free)
- return getUnresolvedOrPrimitiveType(parameters.get(index));
- }
- if (parameterTypesCache == null) {
- parameterTypesCache = new EspressoResolvedJavaType[parameters.size()];
- }
-
- EspressoResolvedJavaType type = parameterTypesCache[index];
- if (!checkValidCache(type, accessingClass)) {
- JavaType result = lookupType(parameters.get(index), (EspressoResolvedInstanceType) accessingClass);
- if (result instanceof EspressoResolvedJavaType) {
- type = (EspressoResolvedJavaType) result;
- parameterTypesCache[index] = type;
- } else {
- assert result != null;
- return result;
- }
- }
- assert type != null;
- return type;
- }
-
- @Override
- public JavaKind getParameterKind(int index) {
- return JavaKind.fromTypeString(parameters.get(index));
- }
-
- @Override
- public JavaType getReturnType(ResolvedJavaType accessingClass) {
- if (accessingClass == null) {
- // Caller doesn't care about resolution context so return an unresolved
- // or primitive type (primitive type resolution is context free)
- return getUnresolvedOrPrimitiveType(returnType);
- }
- if (!checkValidCache(returnTypeCache, accessingClass)) {
- JavaType result = lookupType(returnType, (EspressoResolvedInstanceType) accessingClass);
- if (result instanceof EspressoResolvedJavaType) {
- returnTypeCache = (EspressoResolvedJavaType) result;
- } else {
- return result;
- }
- }
- return returnTypeCache;
- }
-
- private static JavaType lookupType(String descriptor, EspressoResolvedInstanceType accessingClass) {
- if (descriptor.length() == 1) {
- JavaKind kind = JavaKind.fromTypeString(descriptor);
- if (kind.isPrimitive()) {
- return EspressoResolvedPrimitiveType.forKind(kind);
- }
- }
- return runtime().lookupType(descriptor, accessingClass, false);
- }
-
- @Override
- public JavaKind getReturnKind() {
- return JavaKind.fromTypeString(returnType);
- }
-
- @Override
- public String toMethodDescriptor() {
- assert rawSignature.equals(Signature.super.toMethodDescriptor()) : rawSignature + " != " + Signature.super.toMethodDescriptor();
- return rawSignature;
- }
-
- @Override
- public String toString() {
- return "EspressoSignature<" + rawSignature + ">";
+ EspressoSignature(String rawSignature) {
+ super(rawSignature);
}
@Override
- public boolean equals(Object obj) {
- if (obj instanceof EspressoSignature) {
- EspressoSignature other = (EspressoSignature) obj;
- if (other.rawSignature.equals(rawSignature)) {
- assert other.parameters.equals(parameters);
- assert other.returnType.equals(returnType);
- return true;
- }
- }
- return false;
+ protected JavaType lookupType0(String descriptor, AbstractEspressoResolvedInstanceType accessingClass) {
+ return runtime().lookupType(descriptor, (EspressoResolvedInstanceType) accessingClass, false);
}
@Override
- public int hashCode() {
- return rawSignature.hashCode();
+ protected AbstractEspressoResolvedPrimitiveType lookupPrimitiveType0(JavaKind kind) {
+ return EspressoResolvedPrimitiveType.forKind(kind);
}
}
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ExtendedModifiers.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ExtendedModifiers.java
index bb251583730d..b3706f95724a 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ExtendedModifiers.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ExtendedModifiers.java
@@ -22,14 +22,14 @@
*/
package com.oracle.truffle.espresso.jvmci.meta;
-public final class ExtendedModifiers {
- public static final int SYNTHETIC = 0x00001000;
- public static final int ENUM = 0x00004000;
- public static final int BRIDGE = 0x00000040;
- public static final int VARARGS = 0x00000080;
- public static final int ANNOTATION = 0x00002000;
- public static final int HIDDEN = 0x00100000;
- public static final int FINALIZER = 0x00010000;
+final class ExtendedModifiers {
+ static final int SYNTHETIC = 0x00001000;
+ static final int ENUM = 0x00004000;
+ static final int BRIDGE = 0x00000040;
+ static final int VARARGS = 0x00000080;
+ static final int ANNOTATION = 0x00002000;
+ static final int HIDDEN = 0x00100000;
+ static final int FINALIZER = 0x00010000;
static final int STABLE_FIELD = 0x00010000;
static final int SCOPED_METHOD = 0x00200000;
diff --git a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/KlassConstant.java b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/KlassConstant.java
index 06b3a15d965b..4d6a42971edb 100644
--- a/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/KlassConstant.java
+++ b/espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/KlassConstant.java
@@ -27,7 +27,7 @@
public final class KlassConstant implements VMConstant {
private final EspressoResolvedObjectType type;
- public KlassConstant(EspressoResolvedObjectType type) {
+ KlassConstant(EspressoResolvedObjectType type) {
this.type = type;
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoBindings.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoBindings.java
index 1e6ae3698c5d..44d025b027b1 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoBindings.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoBindings.java
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.List;
+import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Exclusive;
import com.oracle.truffle.api.interop.ArityException;
@@ -35,8 +36,10 @@
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
-import com.oracle.truffle.api.profiles.BranchProfile;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.espresso.impl.KeysArray;
+import com.oracle.truffle.espresso.impl.jvmci.external.JVMCIInteropHelper;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.nodes.commands.AddPathToBindingsNode;
import com.oracle.truffle.espresso.runtime.EspressoContext;
@@ -65,24 +68,30 @@
@ExportLibrary(InteropLibrary.class)
public final class EspressoBindings implements TruffleObject {
public static final String JAVA_VM = "";
+ public static final String JVMCI_HELPER = "";
public static final String ADD_PATH = "addPath";
final boolean useBindingsLoader;
boolean withNativeJavaVM;
+ boolean withJVMCIHelper;
- public EspressoBindings(boolean withNativeJavaVM, boolean useBindingsLoader) {
+ public EspressoBindings(boolean withNativeJavaVM, boolean useBindingsLoader, boolean withJVMCIHelper) {
this.withNativeJavaVM = withNativeJavaVM;
this.useBindingsLoader = useBindingsLoader;
+ this.withJVMCIHelper = withJVMCIHelper;
}
@ExportMessage
@SuppressWarnings("static-method")
Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
- List members = new ArrayList<>(2);
+ List members = new ArrayList<>(3);
if (withNativeJavaVM) {
members.add(JAVA_VM);
}
+ if (withJVMCIHelper) {
+ members.add(JVMCI_HELPER);
+ }
if (useBindingsLoader) {
members.add(ADD_PATH);
}
@@ -102,6 +111,9 @@ boolean isMemberReadable(String member) {
if (JAVA_VM.equals(member)) {
return withNativeJavaVM;
}
+ if (JVMCI_HELPER.equals(member)) {
+ return withJVMCIHelper;
+ }
if (ADD_PATH.equals(member)) {
return useBindingsLoader;
}
@@ -118,18 +130,23 @@ boolean isMemberInvocable(String member) {
}
@ExportMessage
- Object readMember(String member,
+ static Object readMember(EspressoBindings receiver, String member,
+ @Bind Node node,
@CachedLibrary("this") InteropLibrary self,
- @Exclusive @Cached BranchProfile error) throws UnknownIdentifierException {
- if (!isMemberReadable(member)) {
- error.enter();
+ @Exclusive @Cached InlinedBranchProfile error) throws UnknownIdentifierException {
+ if (!receiver.isMemberReadable(member)) {
+ error.enter(node);
throw UnknownIdentifierException.create(member);
}
EspressoContext context = EspressoContext.get(self);
- if (withNativeJavaVM && JAVA_VM.equals(member)) {
+ if (receiver.withNativeJavaVM && JAVA_VM.equals(member)) {
return context.getVM().getJavaVM();
}
- if (useBindingsLoader && ADD_PATH.equals(member)) {
+ if (receiver.withJVMCIHelper && JVMCI_HELPER.equals(member)) {
+ assert context.getLanguage().isExternalJVMCIEnabled();
+ return new JVMCIInteropHelper(context);
+ }
+ if (receiver.useBindingsLoader && ADD_PATH.equals(member)) {
return new AddPathToBindingsNode.InvocableAddToBindings();
}
Meta meta = context.getMeta();
@@ -137,7 +154,7 @@ Object readMember(String member,
StaticObject clazz = (StaticObject) meta.java_lang_Class_forName_String_boolean_ClassLoader.invokeDirectStatic(meta.toGuestString(member), false, context.getBindingsLoader());
return clazz.getMirrorKlass(meta);
} catch (EspressoException e) {
- error.enter();
+ error.enter(node);
if (InterpreterToVM.instanceOf(e.getGuestException(), meta.java_lang_ClassNotFoundException)) {
throw UnknownIdentifierException.create(member, e);
}
@@ -146,18 +163,19 @@ Object readMember(String member,
}
@ExportMessage
- Object invokeMember(String member, Object[] arguments,
+ static Object invokeMember(EspressoBindings receiver, String member, Object[] arguments,
+ @Bind Node node,
@Cached AddPathToBindingsNode addPathToBindingsNode,
- @Exclusive @Cached BranchProfile error) throws UnknownIdentifierException, ArityException, UnsupportedTypeException {
- if (!isMemberInvocable(member)) {
- error.enter();
+ @Exclusive @Cached InlinedBranchProfile error) throws UnknownIdentifierException, ArityException, UnsupportedTypeException {
+ if (!receiver.isMemberInvocable(member)) {
+ error.enter(node);
throw UnknownIdentifierException.create(member);
}
- if (useBindingsLoader && ADD_PATH.equals(member)) {
+ if (receiver.useBindingsLoader && ADD_PATH.equals(member)) {
addPathToBindingsNode.execute(arguments);
return StaticObject.NULL;
}
- error.enter();
+ error.enter(node);
throw UnknownIdentifierException.create(member);
}
@@ -166,18 +184,25 @@ boolean isMemberRemovable(String member) {
if (withNativeJavaVM && JAVA_VM.equals(member)) {
return true;
}
+ if (withJVMCIHelper && JVMCI_HELPER.equals(member)) {
+ return true;
+ }
return false;
}
@ExportMessage
- void removeMember(String member,
- @Exclusive @Cached BranchProfile error) throws UnknownIdentifierException {
- if (!isMemberRemovable(member)) {
- error.enter();
+ static void removeMember(EspressoBindings receiver, String member,
+ @Bind Node node,
+ @Exclusive @Cached InlinedBranchProfile error) throws UnknownIdentifierException {
+ if (!receiver.isMemberRemovable(member)) {
+ error.enter(node);
throw UnknownIdentifierException.create(member);
}
- if (withNativeJavaVM && JAVA_VM.equals(member)) {
- withNativeJavaVM = false;
+ if (receiver.withNativeJavaVM && JAVA_VM.equals(member)) {
+ receiver.withNativeJavaVM = false;
+ }
+ if (receiver.withJVMCIHelper && JVMCI_HELPER.equals(member)) {
+ receiver.withJVMCIHelper = false;
}
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java
index 1e3240613df6..37bfc3774a02 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoLanguage.java
@@ -149,6 +149,7 @@ public final class EspressoLanguage extends TruffleLanguage imp
@CompilationFinal private boolean whiteBoxEnabled;
@CompilationFinal private boolean eagerFrameAnalysis;
@CompilationFinal private boolean internalJvmciEnabled;
+ @CompilationFinal private boolean externalJvmciEnabled;
@CompilationFinal private boolean useEspressoLibs;
@CompilationFinal private boolean enableNetworking;
@CompilationFinal private boolean continuum;
@@ -253,6 +254,7 @@ private void initializeOptions(final TruffleLanguage.Env env) {
previewEnabled = env.getOptions().get(EspressoOptions.EnablePreview);
whiteBoxEnabled = env.getOptions().get(EspressoOptions.WhiteBoxAPI);
internalJvmciEnabled = env.getOptions().get(EspressoOptions.EnableJVMCI);
+ externalJvmciEnabled = env.getOptions().get(EspressoOptions.ExposeJVMCIHelper);
continuum = env.getOptions().get(EspressoOptions.Continuum);
maxStackTraceDepth = env.getOptions().get(EspressoOptions.MaxJavaStackTraceDepth);
@@ -401,6 +403,7 @@ protected boolean areOptionsCompatible(OptionValues oldOptions, OptionValues new
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnablePreview) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.WhiteBoxAPI) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.EnableJVMCI) &&
+ isOptionCompatible(newOptions, oldOptions, EspressoOptions.ExposeJVMCIHelper) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.Continuum) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.UseTRegex) &&
isOptionCompatible(newOptions, oldOptions, EspressoOptions.GuestFieldOffsetStrategy) &&
@@ -640,8 +643,12 @@ public boolean isInternalJVMCIEnabled() {
return internalJvmciEnabled;
}
+ public boolean isExternalJVMCIEnabled() {
+ return externalJvmciEnabled;
+ }
+
public boolean isJVMCIEnabled() {
- return internalJvmciEnabled;
+ return internalJvmciEnabled || externalJvmciEnabled;
}
public boolean useTRegex() {
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java
index ae738fde0efc..d168040fa964 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/EspressoOptions.java
@@ -704,6 +704,12 @@ public JImageMode apply(String s) {
usageSyntax = "false|true") //
public static final OptionKey EnableJVMCI = new OptionKey<>(false);
+ @Option(help = "Expose the binding to support external/host JVMCI.", //
+ category = OptionCategory.INTERNAL, //
+ stability = OptionStability.EXPERIMENTAL, //
+ usageSyntax = "false|true") //
+ public static final OptionKey ExposeJVMCIHelper = new OptionKey<>(false);
+
public enum GuestFieldOffsetStrategyEnum {
safety,
compact,
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java
index 82db76609223..5a158cfbcfdf 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/descriptors/EspressoSymbols.java
@@ -526,13 +526,16 @@ public static void ensureInitialized() {
// @formatter:off
public static final Symbol com_oracle_truffle_espresso_jvmci_EspressoJVMCIRuntime = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/EspressoJVMCIRuntime;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType;");
+ public static final Symbol com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoResolvedInstanceType = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedInstanceType;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;");
+ public static final Symbol com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoResolvedJavaMethod = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaMethod;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaRecordComponent;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedArrayType = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedArrayType;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedPrimitiveType = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedPrimitiveType;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaType = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaType;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoConstantPool;");
+ public static final Symbol com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoConstantPool = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/AbstractEspressoConstantPool;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoObjectConstant = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoObjectConstant;");
public static final Symbol com_oracle_truffle_espresso_jvmci_meta_EspressoBootstrapMethodInvocation = SYMBOLS.putType("Lcom/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation;");
// @formatter:on
@@ -1431,9 +1434,10 @@ public static class Signatures {
public static final Symbol PrimitiveConstant_float = SYMBOLS.putSignature(Types.jdk_vm_ci_meta_PrimitiveConstant, Types._float);
public static final Symbol PrimitiveConstant_double = SYMBOLS.putSignature(Types.jdk_vm_ci_meta_PrimitiveConstant, Types._double);
public static final Symbol PrimitiveConstant_char_long = SYMBOLS.putSignature(Types.jdk_vm_ci_meta_PrimitiveConstant, Types._char, Types._long);
- public static final Symbol _void_boolean_EspressoResolvedJavaMethod_String_JavaConstant_JavaConstant_array_int_EspressoConstantPool = SYMBOLS.putSignature(Types._void,
- Types._boolean, Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod, Types.java_lang_String, Types.jdk_vm_ci_meta_JavaConstant,
- Types.jdk_vm_ci_meta_JavaConstant_array, Types._int, Types.com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool);
+ public static final Symbol _void_boolean_AbstractEspressoResolvedJavaMethod_String_JavaConstant_JavaConstant_array_int_AbstractEspressoConstantPool = SYMBOLS.putSignature(
+ Types._void,
+ Types._boolean, Types.com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoResolvedJavaMethod, Types.java_lang_String, Types.jdk_vm_ci_meta_JavaConstant,
+ Types.jdk_vm_ci_meta_JavaConstant_array, Types._int, Types.com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoConstantPool);
public static final Symbol UnresolvedJavaType_String = SYMBOLS.putSignature(Types.jdk_vm_ci_meta_UnresolvedJavaType, Types.java_lang_String);
public static final Symbol _void_sun_misc_Signal = SYMBOLS.putSignature(Types._void, Types.sun_misc_Signal);
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/Callback.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/Callback.java
index ca05ae5b0d4d..ee776bd1e08d 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/Callback.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/ffi/Callback.java
@@ -40,8 +40,8 @@ public Callback(int arity, Function function) {
this.function = function;
}
- @SuppressWarnings("static-method")
@ExportMessage
+ @SuppressWarnings("static-method")
boolean isExecutable() {
return true;
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java
index 12fa7e0c15cd..245b41f42c6f 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Field.java
@@ -22,6 +22,7 @@
*/
package com.oracle.truffle.espresso.impl;
+import java.util.Set;
import java.util.function.Function;
import com.oracle.truffle.api.Assumption;
@@ -29,6 +30,17 @@
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.utilities.TriState;
+import com.oracle.truffle.espresso.EspressoLanguage;
import com.oracle.truffle.espresso.EspressoOptions;
import com.oracle.truffle.espresso.classfile.ClassfileParser;
import com.oracle.truffle.espresso.classfile.Constants;
@@ -52,6 +64,7 @@
import com.oracle.truffle.espresso.runtime.staticobject.FieldStorageObject;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
import com.oracle.truffle.espresso.shared.meta.FieldAccess;
+import com.oracle.truffle.espresso.substitutions.standard.Target_sun_misc_Unsafe;
/**
* Represents a resolved Espresso field.
@@ -82,7 +95,8 @@
* value (this could be either an Original Field or a Redefine Added Field) a Delegation field is
* assigned the underlying field as a Compatible Field.
*/
-public class Field extends Member implements FieldRef, FieldAccess, AttributedElement {
+@ExportLibrary(InteropLibrary.class)
+public class Field extends Member implements FieldRef, TruffleObject, FieldAccess, AttributedElement {
public static final Field[] EMPTY_ARRAY = new Field[0];
@@ -1080,5 +1094,87 @@ public synchronized void set(boolean value) {
}
}
}
+
// endregion jdwp-specific
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] members = {
+ ReadMember.FLAGS,
+ ReadMember.OFFSET,
+ ReadMember.NAME,
+ ReadMember.TYPE,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String FLAGS = "flags";
+ static final String OFFSET = "offset";
+ static final String NAME = "name";
+ static final String TYPE = "type";
+
+ @Specialization(guards = "FLAGS.equals(member)")
+ static int getFlags(Field receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getFlags();
+ }
+
+ @Specialization(guards = "OFFSET.equals(member)")
+ static int getOffset(Field receiver, @SuppressWarnings("unused") String member,
+ @Bind Node node) {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ return Target_sun_misc_Unsafe.getGuestFieldOffset(receiver, EspressoLanguage.get(node));
+ }
+
+ @Specialization(guards = "NAME.equals(member)")
+ static String getName(Field receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getNameAsString();
+ }
+
+ @Specialization(guards = "TYPE.equals(member)")
+ static String getType(Field receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getType().toString();
+ }
+
+ @Fallback
+ @SuppressWarnings("unused")
+ public static Object doUnknown(Field receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ @TruffleBoundary
+ @SuppressWarnings("static-method")
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+
+ @ExportMessage
+ public TriState isIdenticalOrUndefined(Object other) {
+ return TriState.valueOf(this == other);
+ }
+
+ @ExportMessage
+ public int identityHashCode() {
+ return System.identityHashCode(this);
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java
index 815ba663b80f..0af0f8af1818 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/Method.java
@@ -60,6 +60,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.logging.Level;
@@ -70,11 +71,27 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Cached.Exclusive;
+import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Idempotent;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.ArityException;
+import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedBranchProfile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.utilities.TriState;
+import com.oracle.truffle.espresso.EspressoLanguage;
import com.oracle.truffle.espresso.EspressoOptions;
import com.oracle.truffle.espresso.analysis.frame.EspressoFrameDescriptor;
import com.oracle.truffle.espresso.analysis.frame.FrameAnalysis;
@@ -94,6 +111,7 @@
import com.oracle.truffle.espresso.classfile.attributes.CodeAttribute;
import com.oracle.truffle.espresso.classfile.attributes.ExceptionsAttribute;
import com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute;
+import com.oracle.truffle.espresso.classfile.attributes.Local;
import com.oracle.truffle.espresso.classfile.attributes.LocalVariableTable;
import com.oracle.truffle.espresso.classfile.attributes.SignatureAttribute;
import com.oracle.truffle.espresso.classfile.attributes.SourceFileAttribute;
@@ -105,6 +123,7 @@
import com.oracle.truffle.espresso.classfile.descriptors.SignatureSymbols;
import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
import com.oracle.truffle.espresso.classfile.descriptors.Type;
+import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols;
import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool;
import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Names;
import com.oracle.truffle.espresso.descriptors.EspressoSymbols.Types;
@@ -118,6 +137,10 @@
import com.oracle.truffle.espresso.impl.generics.tree.ReturnType;
import com.oracle.truffle.espresso.impl.generics.tree.TypeSignature;
import com.oracle.truffle.espresso.impl.generics.visitor.Reifier;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData;
+import com.oracle.truffle.espresso.impl.jvmci.external.ExceptionHandlerInteropWrapper;
+import com.oracle.truffle.espresso.impl.jvmci.external.InteropLineNumberTableHelper;
+import com.oracle.truffle.espresso.impl.jvmci.external.LocalInteropWrapper;
import com.oracle.truffle.espresso.jdwp.api.KlassRef;
import com.oracle.truffle.espresso.jdwp.api.MethodHook;
import com.oracle.truffle.espresso.jdwp.api.MethodRef;
@@ -150,6 +173,7 @@
import com.oracle.truffle.espresso.vm.InterpreterToVM;
import com.oracle.truffle.espresso.vm.VM.EspressoStackElement;
+@ExportLibrary(InteropLibrary.class)
public final class Method extends Member implements MethodRef, TruffleObject, ContextAccess,
MethodAccess, AttributedElement {
@@ -500,7 +524,7 @@ public boolean hasReceiver() {
}
/**
- * Determines if this method is {@link java.lang.Object#Object()}.
+ * Determines if this method is {@link Object#Object()}.
*/
public boolean isJavaLangObjectInit() {
return getDeclaringKlass().isJavaLangObject() && Names._init_.equals(getName());
@@ -2139,4 +2163,355 @@ public RuntimeConstantPool getPool() {
}
// endregion jdwp-specific
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] readableMembers = {
+ ReadMember.FLAGS,
+ ReadMember.RAW_SIGNATURE,
+ ReadMember.NAME,
+ ReadMember.EXCEPTION_HANDLERS,
+ ReadMember.LOCAL_VARIABLE_TABLE,
+ ReadMember.LINE_NUMBER_TABLE,
+ ReadMember.CODE_SIZE,
+ ReadMember.CODE,
+ ReadMember.MAX_LOCALS,
+ ReadMember.MAX_STACK_SIZE,
+ ReadMember.FORCE_INLINE,
+ ReadMember.NEVER_INLINE,
+ ReadMember.LEAF_METHOD,
+ ReadMember.HAS_POISON,
+ ReadMember.HOLDER,
+ };
+ ALL_MEMBERS = new KeysArray<>(readableMembers);
+ ALL_MEMBERS_SET = Set.of(readableMembers);
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String FLAGS = "flags";
+ static final String RAW_SIGNATURE = "rawSignature";
+ static final String NAME = "name";
+ static final String EXCEPTION_HANDLERS = "exceptionHandlers";
+ static final String LOCAL_VARIABLE_TABLE = "localVariableTable";
+ static final String LINE_NUMBER_TABLE = "lineNumberTable";
+ static final String CODE_SIZE = "codeSize";
+ static final String CODE = "code";
+ static final String MAX_LOCALS = "maxLocals";
+ static final String MAX_STACK_SIZE = "maxStackSize";
+ static final String FORCE_INLINE = "forceInline";
+ static final String NEVER_INLINE = "neverInline";
+ static final String LEAF_METHOD = "leafMethod";
+ static final String HAS_POISON = "hasPoison";
+ static final String HOLDER = "holder";
+
+ @Specialization(guards = "FLAGS.equals(member)")
+ static int getFlags(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getModifiers();
+ }
+
+ @Specialization(guards = "RAW_SIGNATURE.equals(member)")
+ static String getRawSignature(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getRawSignature().toString();
+ }
+
+ @Specialization(guards = "NAME.equals(member)")
+ static String getName(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getNameAsString();
+ }
+
+ @Specialization(guards = "EXCEPTION_HANDLERS.equals(member)")
+ static Object getExceptionHandlers(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ CodeAttribute codeAttribute = receiver.getCodeAttribute();
+ if (codeAttribute == null) {
+ return StaticObject.NULL;
+ }
+ ExceptionHandler[] handlers = codeAttribute.getExceptionHandlers();
+ if (handlers.length == 0) {
+ return StaticObject.NULL;
+ }
+ ExceptionHandlerInteropWrapper[] result = new ExceptionHandlerInteropWrapper[handlers.length];
+ for (int i = 0; i < handlers.length; ++i) {
+ result[i] = new ExceptionHandlerInteropWrapper(handlers[i]);
+ }
+ return new KeysArray<>(result);
+ }
+
+ @Specialization(guards = "LOCAL_VARIABLE_TABLE.equals(member)")
+ static Object getLocalVariableTable(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ LocalVariableTable localVariableTable = receiver.getLocalVariableTable();
+ Local[] locals = localVariableTable.getLocals();
+ if (locals.length == 0) {
+ return StaticObject.NULL;
+ }
+ LocalInteropWrapper[] result = new LocalInteropWrapper[locals.length];
+ for (int i = 0; i < locals.length; ++i) {
+ result[i] = new LocalInteropWrapper(locals[i]);
+ }
+ return new KeysArray<>(result);
+ }
+
+ @Specialization(guards = "LINE_NUMBER_TABLE.equals(member)")
+ static Object getLineNumberTable(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ CodeAttribute codeAttribute = receiver.getCodeAttribute();
+ if (codeAttribute == null) {
+ return StaticObject.NULL;
+ }
+ LineNumberTableAttribute lineNumberTable = codeAttribute.getLineNumberTableAttribute();
+ if (lineNumberTable == null) {
+ return StaticObject.NULL;
+ }
+ return new InteropLineNumberTableHelper(lineNumberTable.getRawData());
+ }
+
+ @Specialization(guards = "CODE_SIZE.equals(member)")
+ static int getCodeSize(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ CodeAttribute codeAttribute = receiver.getCodeAttribute();
+ if (codeAttribute == null) {
+ return 0;
+ }
+ return codeAttribute.getOriginalCode().length;
+ }
+
+ @Specialization(guards = "CODE.equals(member)")
+ static Object getCode(Method receiver, @SuppressWarnings("unused") String member,
+ @Bind Node node) {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (receiver.getCodeAttribute() == null) {
+ return StaticObject.NULL;
+ }
+ Meta meta = EspressoContext.get(node).getMeta();
+ if (!receiver.getMethodVersion().usesIndy()) {
+ return meta.java_nio_ByteBuffer_wrap.invokeDirectStatic(StaticObject.wrap(receiver.getOriginalCode(), meta));
+ }
+ JVMCIIndyData indyData = JVMCIIndyData.getOrCreate(receiver.getDeclaringKlass(), meta);
+ return meta.java_nio_ByteBuffer_wrap.invokeDirectStatic(StaticObject.wrap(indyData.getCode(receiver), meta));
+ }
+
+ @Specialization(guards = "MAX_LOCALS.equals(member)")
+ static int getMaxLocals(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ CodeAttribute codeAttribute = receiver.getCodeAttribute();
+ if (codeAttribute == null) {
+ return 0;
+ }
+ return codeAttribute.getMaxLocals();
+ }
+
+ @Specialization(guards = "MAX_STACK_SIZE.equals(member)")
+ static int getMaxStackSize(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ CodeAttribute codeAttribute = receiver.getCodeAttribute();
+ if (codeAttribute == null) {
+ return 0;
+ }
+ // 1 additional slot for the appendix that gets "pushed" on the stack by the compiler
+ // both for INVOKEDYNAMIC and usage of "InvokeGeneric" polymorphic signature methods
+ // ("invokehandle" in HotSpot)
+ return codeAttribute.getMaxStack() + 1;
+ }
+
+ @Specialization(guards = "FORCE_INLINE.equals(member)")
+ static boolean isForceInline(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.isForceInline();
+ }
+
+ @Specialization(guards = "NEVER_INLINE.equals(member)")
+ static boolean hasNeverInlineDirective(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.isDontInline();
+ }
+
+ @Specialization(guards = "LEAF_METHOD.equals(member)")
+ static boolean isLeafMethod(Method receiver, @SuppressWarnings("unused") String member,
+ @Bind Node node) {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ EspressoContext context = EspressoContext.get(node);
+ return context.getClassHierarchyOracle().isLeafMethod(receiver).isValid();
+ }
+
+ @Specialization(guards = "HAS_POISON.equals(member)")
+ static boolean hasPoison(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.hasPoisonPill();
+ }
+
+ @Specialization(guards = "HOLDER.equals(member)")
+ static ObjectKlass holder(Method receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.getDeclaringKlass();
+ }
+
+ @Fallback
+ public static Object doUnknown(@SuppressWarnings("unused") Method receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ /*
+ * We use the "execute" message on Method objects instead of the usual way of invoking espresso
+ * over interop because usual interop cannot call private methods. For instance methods interop
+ * also only returns "bound" methods for a specific receiver. Also, this "execute" message is
+ * much more restrictive in what arguments it accepts: it only expects host boxes for primitive
+ * arguments and espresso objects for object arguments. It doesn't perform any of the more
+ * complex conversions typically done for interop.
+ */
+ @ExportMessage
+ abstract static class Execute {
+ @Specialization
+ static Object invoke(Method receiver, Object[] arguments,
+ @Bind Node node,
+ @CachedLibrary(limit = "2") @Exclusive InteropLibrary interop,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ int argumentCount = receiver.getArgumentCount();
+ if (argumentCount != arguments.length) {
+ arityError.enter(node);
+ throw ArityException.create(argumentCount, argumentCount, arguments.length);
+ }
+ Symbol[] signature = receiver.getParsedSignature();
+ Object[] convertedArguments = new Object[argumentCount];
+ int argumentIndex = 0;
+ ObjectKlass declaringKlass = receiver.getDeclaringKlass();
+ if (!receiver.isStatic()) {
+ if (interop.isNull(arguments[0])) {
+ throw receiver.getMeta().throwNullPointerExceptionBoundary();
+ }
+ if (!(arguments[0] instanceof StaticObject receiverObject)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!declaringKlass.isAssignableFrom(receiverObject.getKlass())) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ convertedArguments[argumentIndex++] = receiverObject;
+ }
+ int signatureIndex = 0;
+ for (; argumentIndex < arguments.length; argumentIndex++) {
+ Symbol typeSymbol = signature[signatureIndex++];
+ JavaKind javaKind = TypeSymbols.getJavaKind(typeSymbol);
+ if (javaKind.isObject()) {
+ if (interop.isNull(arguments[argumentIndex])) {
+ convertedArguments[argumentIndex] = StaticObject.NULL;
+ } else {
+ if (!(arguments[argumentIndex] instanceof StaticObject object)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ Klass type = receiver.getMeta().resolveSymbolOrFail(typeSymbol,
+ declaringKlass.getDefiningClassLoader(),
+ declaringKlass.protectionDomain());
+ if (!type.isAssignableFrom(object.getKlass())) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ convertedArguments[argumentIndex] = object;
+ }
+ } else if (javaKind.isStackInt()) {
+ if (!(arguments[argumentIndex] instanceof Integer value)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ // GR-71116 we should use stack kind during calls
+ convertedArguments[argumentIndex] = switch (javaKind) {
+ case Boolean -> value != 0;
+ case Byte -> (byte) (int) value;
+ case Short -> (short) (int) value;
+ case Char -> (char) (int) value;
+ case Int -> value;
+ default -> {
+ CompilerDirectives.transferToInterpreter();
+ throw EspressoError.shouldNotReachHere(javaKind.toString());
+ }
+ };
+ } else {
+ switch (javaKind) {
+ case Long -> {
+ if (!(arguments[argumentIndex] instanceof Long value)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ convertedArguments[argumentIndex] = value;
+ }
+ case Float -> {
+ if (!(arguments[argumentIndex] instanceof Float value)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ convertedArguments[argumentIndex] = value;
+ }
+ case Double -> {
+ if (!(arguments[argumentIndex] instanceof Double value)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ convertedArguments[argumentIndex] = value;
+ }
+ default -> {
+ CompilerDirectives.transferToInterpreter();
+ throw EspressoError.shouldNotReachHere(javaKind.toString());
+ }
+ }
+ }
+ }
+ Object result;
+ if (receiver.isStatic()) {
+ result = receiver.invokeDirectStatic(convertedArguments);
+ } else if (receiver.isConstructor()) {
+ result = receiver.invokeDirectSpecial(convertedArguments);
+ } else if (declaringKlass.isInterface()) {
+ result = receiver.invokeDirectInterface(convertedArguments);
+ } else {
+ result = receiver.invokeDirectVirtual(convertedArguments);
+ }
+ return result;
+ }
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ @TruffleBoundary
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ @TruffleBoundary
+ public boolean isExecutable() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+
+ @ExportMessage
+ public TriState isIdenticalOrUndefined(Object other) {
+ return TriState.valueOf(this == other);
+ }
+
+ @ExportMessage
+ public int identityHashCode() {
+ return System.identityHashCode(this);
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIConstantPoolUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIConstantPoolUtils.java
new file mode 100644
index 000000000000..47adfb40e666
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIConstantPoolUtils.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci;
+
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.ANEWARRAY;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.CHECKCAST;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.GETFIELD;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.GETSTATIC;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INSTANCEOF;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEDYNAMIC;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEINTERFACE;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKESPECIAL;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKESTATIC;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEVIRTUAL;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC2_W;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC_W;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.MULTIANEWARRAY;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.NEW;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.PUTFIELD;
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.PUTSTATIC;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData.indyCpi;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData.isIndyCPI;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.findObjectType;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.espresso.classfile.ConstantPool;
+import com.oracle.truffle.espresso.classfile.ParserKlass;
+import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute;
+import com.oracle.truffle.espresso.classfile.bytecode.Bytecodes;
+import com.oracle.truffle.espresso.classfile.descriptors.Descriptor;
+import com.oracle.truffle.espresso.classfile.descriptors.Name;
+import com.oracle.truffle.espresso.classfile.descriptors.Signature;
+import com.oracle.truffle.espresso.classfile.descriptors.SignatureSymbols;
+import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
+import com.oracle.truffle.espresso.classfile.descriptors.Type;
+import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols;
+import com.oracle.truffle.espresso.constantpool.CallSiteLink;
+import com.oracle.truffle.espresso.constantpool.ResolvedConstant;
+import com.oracle.truffle.espresso.constantpool.ResolvedDynamicConstant;
+import com.oracle.truffle.espresso.constantpool.ResolvedInvokeDynamicConstant;
+import com.oracle.truffle.espresso.constantpool.ResolvedWithInvokerClassMethodRefConstant;
+import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool;
+import com.oracle.truffle.espresso.constantpool.SuccessfulCallSiteLink;
+import com.oracle.truffle.espresso.impl.Field;
+import com.oracle.truffle.espresso.impl.Klass;
+import com.oracle.truffle.espresso.impl.Method;
+import com.oracle.truffle.espresso.impl.ObjectKlass;
+import com.oracle.truffle.espresso.meta.EspressoError;
+import com.oracle.truffle.espresso.meta.Meta;
+import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
+import com.oracle.truffle.espresso.runtime.EspressoContext;
+import com.oracle.truffle.espresso.runtime.EspressoException;
+import com.oracle.truffle.espresso.runtime.EspressoLinkResolver;
+import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
+import com.oracle.truffle.espresso.shared.meta.SignaturePolymorphicIntrinsic;
+import com.oracle.truffle.espresso.shared.resolver.CallSiteType;
+import com.oracle.truffle.espresso.shared.resolver.FieldAccessType;
+import com.oracle.truffle.espresso.shared.resolver.ResolvedCall;
+
+/**
+ * This class contains methods that are used by both internal and external JVMCI to implement
+ * JVMCI's constant pool accesses.
+ */
+public final class JVMCIConstantPoolUtils {
+ private JVMCIConstantPoolUtils() {
+ }
+
+ public static ConstantPool.Tag safeTagAt(RuntimeConstantPool pool, int cpi, Meta meta) {
+ if (cpi < 0 || pool.length() <= cpi) {
+ throw meta.throwIndexOutOfBoundsExceptionBoundary("Invalid constant pool index", cpi, pool.length());
+ }
+ return pool.tagAt(cpi);
+ }
+
+ @TruffleBoundary
+ public static boolean loadReferencedType0(int cpi, int opcode, RuntimeConstantPool constantPool, ObjectKlass cpHolderKlass, Meta meta) {
+ switch (opcode) {
+ case CHECKCAST:
+ case INSTANCEOF:
+ case NEW:
+ case ANEWARRAY:
+ case MULTIANEWARRAY: {
+ Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, cpi);
+ LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
+ return true;
+ }
+ case LDC:
+ case LDC_W:
+ case LDC2_W: {
+ if (safeTagAt(constantPool, cpi, meta) == ConstantPool.Tag.CLASS) {
+ Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, cpi);
+ LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
+ return true;
+ }
+ return false;
+ }
+ case INVOKEDYNAMIC: {
+ // resolve this indy and call boostrap method
+ assert isIndyCPI(cpi);
+ JVMCIIndyData indyData = JVMCIIndyData.getExisting(cpHolderKlass, meta);
+ LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") Looking up CallSiteLink for index=" + cpi + " in " + cpHolderKlass);
+ JVMCIIndyData.Location location = indyData.getLocation(cpi);
+ assert location != null;
+ int indyCpi = indyCpi(cpi);
+ if (!(safeTagAt(constantPool, indyCpi, meta) == ConstantPool.Tag.INVOKEDYNAMIC)) {
+ throw meta.throwIllegalArgumentExceptionBoundary();
+ }
+ constantPool.linkInvokeDynamic(cpHolderKlass, indyCpi, location.method(), location.bci());
+ return false;
+ }
+ case GETSTATIC:
+ case PUTSTATIC:
+ case GETFIELD:
+ case PUTFIELD:
+ case INVOKEVIRTUAL:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ case INVOKEINTERFACE: {
+ Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, constantPool.memberClassIndex(cpi));
+ LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
+ if ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && ParserKlass.isSignaturePolymorphicHolderType(klass.getType())) {
+ ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(cpi, meta);
+ if (resolvedConstant == null) {
+ Symbol methodName = constantPool.memberName(cpi);
+ if (SignaturePolymorphicIntrinsic.getId(methodName, klass) != null) {
+ // trigger resolution for method handle intrinsics
+ Method method = constantPool.resolvedMethodAt(cpHolderKlass, cpi);
+ LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") resolved MH intrinsic to " + method);
+ }
+ }
+ }
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+
+ @TruffleBoundary
+ public static Method lookupResolvedMethod(RuntimeConstantPool constantPool, int cpi, int opcode, Method caller, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ ObjectKlass cpHolderKlass = constantPool.getHolder();
+ if (caller != null && caller.getDeclaringKlass() != cpHolderKlass) {
+ LOGGER.finer(() -> "ECP.lookupResolvedMethod caller declaring class (" + caller.getDeclaringKlass() + ") doesn't match constant pool holder (" + cpHolderKlass + ")");
+ }
+ if (opcode == INVOKEDYNAMIC) {
+ LOGGER.finer(() -> "ECP.lookupResolvedMethod resolving indy in CP of %s at cpi=0x%08x".formatted(cpHolderKlass, cpi));
+ CallSiteLink callSiteLink = getCallSiteLink(cpHolderKlass, cpi, meta);
+ if (!(callSiteLink instanceof SuccessfulCallSiteLink successfulCallSiteLink)) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod no call site link or failed link in CP of %s at cpi=0x%08x".formatted(cpHolderKlass, cpi));
+ return null;
+ }
+ return (Method) meta.HIDDEN_VMTARGET.getHiddenObject(successfulCallSiteLink.getMemberName());
+ }
+ if (!Bytecodes.isInvoke(opcode) || !(safeTagAt(constantPool, cpi, meta).isMethod())) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod opcode=" + Bytecodes.nameOf(opcode) + " poolConstant=" + constantPool.toString(cpi));
+ throw meta.throwIllegalArgumentExceptionBoundary("Not an invoke or method ref");
+ }
+ Klass symbolicHolder = lookupSymbolicHolder(cpi, constantPool, meta);
+ if (symbolicHolder == null) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod couldn't find symbolic holder klass " + constantPool.memberClassName(cpi));
+ return null;
+ }
+ Method symbolicResolution;
+ ResolvedConstant resolvedConstantOrNull = constantPool.peekResolvedOrNull(cpi, meta);
+ if (resolvedConstantOrNull != null) {
+ symbolicResolution = (Method) resolvedConstantOrNull.value();
+ } else {
+ symbolicResolution = tryResolveMethod(cpi, symbolicHolder, constantPool, meta);
+ if (symbolicResolution == null) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod lookup method failed symbolic lookup for " + symbolicHolder + ", " + constantPool.toString(cpi));
+ return null;
+ }
+ }
+ ResolvedCall resolvedCall = EspressoLinkResolver.resolveCallSiteOrNull(context, cpHolderKlass, symbolicResolution, CallSiteType.fromOpCode(opcode), symbolicHolder);
+ if (resolvedCall == null) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod failed call site resolution for " + symbolicResolution + " from " + cpHolderKlass + " with " + Bytecodes.nameOf(opcode));
+ return null;
+ }
+ Method method;
+ if (resolvedConstantOrNull instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker) {
+ MHInvokeGenericNode.MethodHandleInvoker invoker = withInvoker.invoker();
+ method = invoker.method();
+ } else {
+ method = resolvedCall.getResolvedMethod();
+ }
+ // we don't return the invoker for unresolved InvokeGeneric cases;
+ // this seems to be in line with HotSpot
+ if (method.isInvokeIntrinsic()) {
+ LOGGER.fine(() -> "ECP.lookupResolvedMethod lookup method found InvokeGeneric that was not resolved yet: " + method);
+ }
+ LOGGER.finer(() -> "ECP.lookupResolvedMethod found " + symbolicResolution);
+ return method;
+ }
+
+ private static Klass lookupSymbolicHolder(int cpi, RuntimeConstantPool constantPool, Meta meta) {
+ int holderClassIndex = constantPool.memberClassIndex(cpi);
+ return findObjectType(holderClassIndex, constantPool, false, true, meta);
+ }
+
+ private static Method tryResolveMethod(int methodIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Meta meta) {
+ Symbol name = constantPool.methodName(methodIndex);
+ Symbol signature = constantPool.methodSignature(methodIndex);
+ ConstantPool.Tag tag = safeTagAt(constantPool, methodIndex, meta);
+ return EspressoLinkResolver.resolveMethodSymbolOrNull(meta.getContext(), constantPool.getHolder(), name, signature, symbolicHolder, tag == ConstantPool.Tag.INTERFACE_METHOD_REF, true, true);
+ }
+
+ private static CallSiteLink getCallSiteLink(ObjectKlass cpHolderKlass, int index, Meta meta) {
+ JVMCIIndyData indyData = JVMCIIndyData.getExisting(cpHolderKlass, meta);
+ JVMCIIndyData.Location location = indyData.getLocation(index);
+ assert isIndyCPI(index);
+ int cpi = indyCpi(index);
+ RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
+ ResolvedConstant resolvedConstantOrNull = constantPool.peekResolvedOrNull(cpi, meta);
+ if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.INVOKEDYNAMIC) {
+ throw meta.throwIllegalArgumentExceptionBoundary();
+ }
+ if (!(resolvedConstantOrNull instanceof ResolvedInvokeDynamicConstant resolvedIndy)) {
+ return null;
+ }
+ return resolvedIndy.getCallSiteLink(location.method(), location.bci());
+ }
+
+ @TruffleBoundary
+ public static Field lookupResolvedField(RuntimeConstantPool constantPool, int cpi, Method method, int opcode, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ if (!(opcode == GETSTATIC || opcode == PUTSTATIC || opcode == GETFIELD || opcode == PUTFIELD)) {
+ throw meta.throwIllegalArgumentExceptionBoundary();
+ }
+ if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.FIELD_REF) {
+ throw meta.throwException(meta.java_lang_ClassFormatError);
+ }
+ Klass symbolicHolder = lookupSymbolicHolder(cpi, constantPool, meta);
+ if (symbolicHolder == null) {
+ LOGGER.fine(() -> "ECP.lookupResolvedField cannot resolve symbolic holder: " + constantPool.memberClassName(cpi));
+ return null;
+ }
+ Field resolved = lookupResolvedField(cpi, symbolicHolder, constantPool, method, opcode, meta);
+ if (resolved != null) {
+ LOGGER.finer(() -> "ECP.lookupResolvedField found " + resolved);
+ }
+ return resolved;
+ }
+
+ private static Field lookupResolvedField(int fieldIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Method method, int opcode, Meta meta) {
+ Field symbolicResolution;
+ ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(fieldIndex, meta);
+ if (resolvedConstant != null) {
+ symbolicResolution = (Field) resolvedConstant.value();
+ } else {
+ symbolicResolution = tryResolveField(fieldIndex, symbolicHolder, constantPool, meta);
+ if (symbolicResolution == null) {
+ LOGGER.fine(() -> "ECP.lookupResolvedField failed symbolic lookup for " + symbolicHolder + ", " + constantPool.fieldName(fieldIndex) + ", " +
+ constantPool.fieldType(fieldIndex));
+ return null;
+ }
+ }
+ // Note that constantPool.getHolder() may be different from method.getDeclaringKlass()
+ // in particular this is true in native image where the method might be a JDK method
+ // (e.g.,// Ljava/lang/ClassValue;.()V)
+ // and the constant pool might be the one of its substitution
+ // (e.g., com/oracle/svm/core/jdk/Target_java_lang_ClassValue)
+ if (!EspressoLinkResolver.checkFieldAccess(meta.getContext(), symbolicResolution, FieldAccessType.fromOpCode(opcode), constantPool.getHolder(), method)) {
+ LOGGER.fine(() -> {
+ if (constantPool.getHolder() == method.getDeclaringKlass()) {
+ return "ECP.lookupResolvedField failed access checks for " + symbolicResolution + " from " + method + " with " + Bytecodes.nameOf(opcode);
+ } else {
+ return "ECP.lookupResolvedField failed access checks for " + symbolicResolution + " from " + method + " (currentKlass=" + constantPool.getHolder() + ") with " +
+ Bytecodes.nameOf(opcode);
+ }
+ });
+ return null;
+ }
+ return symbolicResolution;
+ }
+
+ private static Field tryResolveField(int fieldIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Meta meta) {
+ Symbol name = constantPool.fieldName(fieldIndex);
+ Symbol type = constantPool.fieldType(fieldIndex);
+ return EspressoLinkResolver.resolveFieldSymbolOrNull(meta.getContext(), constantPool.getHolder(), name, type, symbolicHolder, true, true);
+ }
+
+ @TruffleBoundary
+ public static Symbol extends Descriptor> lookupDescriptor(RuntimeConstantPool constantPool, int cpi, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ int index = isIndyCPI(cpi) ? indyCpi(cpi) : cpi;
+ ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
+ if (tag.isMember()) {
+ LOGGER.finer(() -> "ECP.lookupDescriptor found " + constantPool.memberDescriptor(index));
+ return constantPool.memberDescriptor(index);
+ }
+ if (tag == ConstantPool.Tag.INVOKEDYNAMIC) {
+ Symbol indySignature = constantPool.invokeDynamicSignature(index);
+ LOGGER.finer(() -> "ECP.lookupDescriptor found " + indySignature);
+ return indySignature;
+ }
+ LOGGER.warning(() -> "Unsupported CP entry type for lookupDescriptor: " + tag);
+ throw meta.throwIllegalArgumentExceptionBoundary();
+ }
+
+ @TruffleBoundary
+ public static Symbol lookupName(RuntimeConstantPool constantPool, int cpi, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ int index = isIndyCPI(cpi) ? indyCpi(cpi) : cpi;
+ ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
+ if (tag.isMember()) {
+ LOGGER.finer(() -> "ECP.lookupName found " + constantPool.memberName(index));
+ return constantPool.memberName(index);
+ }
+ if (tag == ConstantPool.Tag.INVOKEDYNAMIC) {
+ LOGGER.finer(() -> "ECP.lookupName found " + constantPool.invokeDynamicName(index));
+ return constantPool.invokeDynamicName(index);
+ }
+ LOGGER.warning(() -> "Unsupported CP entry type for lookupName: " + tag + " " + constantPool.toString(index));
+ throw meta.throwIllegalArgumentExceptionBoundary();
+ }
+
+ @TruffleBoundary
+ public static StaticObject lookupAppendix(RuntimeConstantPool constantPool, int index, int opcode, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ if (opcode != INVOKEDYNAMIC && opcode != INVOKEVIRTUAL) {
+ throw meta.throwIllegalArgumentExceptionBoundary("Expected INVOKEDYNAMIC or INVOKEVIRTUAL");
+ }
+ if (opcode == INVOKEDYNAMIC) {
+ LOGGER.finer(() -> "ECP.lookupAppendix: Looking up CallSiteLink for index=" + Integer.toHexString(index) + " in " + constantPool.getHolder());
+ CallSiteLink callSiteLink = getCallSiteLink(constantPool.getHolder(), index, meta);
+ if (!(callSiteLink instanceof SuccessfulCallSiteLink successfulCallSiteLink)) {
+ return StaticObject.NULL;
+ }
+ StaticObject appendix = successfulCallSiteLink.getUnboxedAppendix();
+ assert StaticObject.notNull(appendix);
+ return appendix;
+ } else {
+ assert opcode == INVOKEVIRTUAL;
+ ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(index, meta);
+ if (safeTagAt(constantPool, index, meta) != ConstantPool.Tag.METHOD_REF) {
+ throw meta.throwIllegalArgumentExceptionBoundary("The index does not reference a MethodRef");
+ }
+ if (!(resolvedConstant instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker)) {
+ return StaticObject.NULL;
+ }
+ MHInvokeGenericNode.MethodHandleInvoker invoker = withInvoker.invoker();
+ assert invoker != null;
+ assert StaticObject.notNull(invoker.appendix());
+ return invoker.appendix();
+ }
+ }
+
+ @TruffleBoundary
+ public static Object lookupReferencedType(RuntimeConstantPool constantPool, int cpi, @SuppressWarnings("unused") int opcode, EspressoContext context) {
+ assert context.getLanguage().isJVMCIEnabled();
+ Meta meta = context.getMeta();
+ int classCpi;
+ switch (opcode) {
+ case CHECKCAST:
+ case INSTANCEOF:
+ case NEW:
+ case ANEWARRAY:
+ case MULTIANEWARRAY:
+ case LDC:
+ case LDC_W:
+ case LDC2_W:
+ if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.CLASS) {
+ throw meta.throwIllegalArgumentExceptionBoundary("Opcode and constant pool entry types mismatch");
+ }
+ classCpi = cpi;
+ break;
+ case GETSTATIC:
+ case PUTSTATIC:
+ case GETFIELD:
+ case PUTFIELD:
+ case INVOKEVIRTUAL:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ case INVOKEINTERFACE:
+ if (!safeTagAt(constantPool, cpi, meta).isMember()) {
+ throw meta.throwIllegalArgumentExceptionBoundary("Opcode and constant pool entry types mismatch");
+ }
+ classCpi = constantPool.memberClassIndex(cpi);
+ break;
+ default:
+ LOGGER.warning(() -> "Unsupported CP entry type for lookupReferencedType: " + safeTagAt(constantPool, cpi, meta) + " " + constantPool.toString(cpi) + " for " +
+ Bytecodes.nameOf(opcode));
+ throw meta.throwIllegalArgumentExceptionBoundary("Unsupported CP entry type");
+ }
+ Klass klass;
+ try {
+ klass = findObjectType(classCpi, constantPool, false, true, meta);
+ } catch (EspressoException e) {
+ throw EspressoError.shouldNotReachHere("findObjectType with resolve=false should never throw", e);
+ }
+ if (klass == null) {
+ Symbol className = constantPool.className(classCpi);
+ return TypeSymbols.nameToType(className);
+ }
+ LOGGER.finer(() -> "ECP.lookupReferencedType found " + klass);
+ return klass;
+ }
+
+ @TruffleBoundary
+ public static void lookupBootstrapMethodInvocation(RuntimeConstantPool constantPool, int cpi, int opcode, EspressoContext context, BootstrapMethodInvocationBuilder builder) {
+ Meta meta = context.getMeta();
+ int index;
+ if (opcode == -1) {
+ assert !isIndyCPI(cpi);
+ index = cpi;
+ } else if (opcode == INVOKEDYNAMIC) {
+ assert isIndyCPI(cpi);
+ index = indyCpi(cpi);
+ } else {
+ throw meta.throwIllegalArgumentExceptionBoundary("Unexpected opcode: " + opcode);
+ }
+ ObjectKlass cpHolderKlass = constantPool.getHolder();
+ ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
+ if (tag != ConstantPool.Tag.DYNAMIC && tag != ConstantPool.Tag.INVOKEDYNAMIC) {
+ return;
+ }
+ BootstrapMethodsAttribute bms = cpHolderKlass.getAttribute(BootstrapMethodsAttribute.NAME, BootstrapMethodsAttribute.class);
+ int bsmAttrIndex = constantPool.bsmBootstrapMethodAttrIndex(index);
+ BootstrapMethodsAttribute.Entry bsmEntry = bms.at(bsmAttrIndex);
+ StaticObject methodHandle = constantPool.getMethodHandle(bsmEntry, cpHolderKlass);
+ methodHandle = (StaticObject) meta.java_lang_invoke_MethodHandle_asFixedArity.invokeDirectVirtual(methodHandle);
+ assert meta.java_lang_invoke_DirectMethodHandle.isAssignableFrom(methodHandle.getKlass());
+ StaticObject member = meta.java_lang_invoke_DirectMethodHandle_member.getObject(methodHandle);
+
+ boolean isIndy = tag == ConstantPool.Tag.INVOKEDYNAMIC;
+ Method bootstrapMethod = (Method) meta.HIDDEN_VMTARGET.getHiddenObject(member);
+ Symbol name = constantPool.bsmName(index);
+ StaticObject type;
+ if (isIndy) {
+ Symbol invokeSignature = SignatureSymbols.fromDescriptor(constantPool.invokeDynamicSignature(index));
+ Symbol[] parsedInvokeSignature = meta.getSignatures().parsed(invokeSignature);
+ type = RuntimeConstantPool.signatureToMethodType(parsedInvokeSignature, cpHolderKlass, meta.getContext().getJavaVersion().java8OrEarlier(), meta);
+ } else {
+ Symbol typeSymbol = TypeSymbols.fromSymbol(constantPool.dynamicType(index));
+ Klass klass = meta.resolveSymbolOrFail(typeSymbol, cpHolderKlass.getDefiningClassLoader(), cpHolderKlass.protectionDomain());
+ type = klass.mirror();
+ }
+ builder.setupStaticArguments(bsmEntry.numBootstrapArguments());
+ for (int i = 0; i < bsmEntry.numBootstrapArguments(); i++) {
+ char entryCPI = bsmEntry.argAt(i);
+ ConstantPool.Tag entryTag = safeTagAt(constantPool, entryCPI, meta);
+ if (entryTag == ConstantPool.Tag.DYNAMIC) {
+ ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(entryCPI, meta);
+ if (resolvedConstant instanceof ResolvedDynamicConstant resolvedDynamicConstant) {
+ builder.staticArgument(i, resolvedDynamicConstant.guestBoxedValue(meta));
+ } else {
+ builder.staticArgumentUnresolvedDynamic(i, entryCPI);
+ }
+ } else {
+ StaticObject obj = switch (entryTag) {
+ case METHODHANDLE -> constantPool.resolvedMethodHandleAt(cpHolderKlass, entryCPI);
+ case METHODTYPE -> constantPool.resolvedMethodTypeAt(cpHolderKlass, entryCPI);
+ case CLASS -> constantPool.resolvedKlassAt(cpHolderKlass, entryCPI).mirror();
+ case STRING -> constantPool.resolvedStringAt(entryCPI);
+ case INTEGER -> meta.boxInteger(constantPool.intAt(entryCPI));
+ case LONG -> meta.boxLong(constantPool.longAt(entryCPI));
+ case DOUBLE -> meta.boxDouble(constantPool.doubleAt(entryCPI));
+ case FLOAT -> meta.boxFloat(constantPool.floatAt(entryCPI));
+ default -> throw EspressoError.shouldNotReachHere(entryTag.toString());
+ };
+ builder.staticArgument(i, obj);
+ }
+ }
+ builder.finalize(isIndy, bootstrapMethod, name, type);
+ }
+
+ public interface BootstrapMethodInvocationBuilder {
+ void setupStaticArguments(int length);
+
+ void staticArgument(int i, StaticObject value);
+
+ void staticArgumentUnresolvedDynamic(int i, int cpi);
+
+ void finalize(boolean isIndy, Method bootstrapMethod, Symbol name, StaticObject type);
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIIndyData.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIIndyData.java
similarity index 98%
rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIIndyData.java
rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIIndyData.java
index 0ff9a18b5b6c..7bab55fa4902 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIIndyData.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIIndyData.java
@@ -20,10 +20,10 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.truffle.espresso.jvmci;
+package com.oracle.truffle.espresso.impl.jvmci;
import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEDYNAMIC;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.LOGGER;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java
similarity index 77%
rename from espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIUtils.java
rename to espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java
index 4021b3914b0d..e633ada38477 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/jvmci/JVMCIUtils.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java
@@ -20,7 +20,9 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-package com.oracle.truffle.espresso.jvmci;
+package com.oracle.truffle.espresso.impl.jvmci;
+
+import java.util.function.IntFunction;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleLogger;
@@ -33,6 +35,7 @@
import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool;
import com.oracle.truffle.espresso.impl.ClassRegistry;
import com.oracle.truffle.espresso.impl.Klass;
+import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
@@ -102,4 +105,35 @@ public static Klass findObjectType(int classIndex, RuntimeConstantPool pool, boo
}
return findObjectType(type, pool.getHolder(), resolve, checkAccess, meta);
}
+
+ @FunctionalInterface
+ public interface MethodArraySetter {
+ void set(T o, int i, Method method);
+ }
+
+ public static T getAllMethods(ObjectKlass klass, IntFunction alloc, MethodArraySetter setter) {
+ Method.MethodVersion[] declaredMethodVersions = klass.getDeclaredMethodVersions();
+ Method.MethodVersion[] mirandaMethods = klass.getMirandaMethods();
+ int resultSize = declaredMethodVersions.length;
+ if (mirandaMethods != null) {
+ for (Method.MethodVersion mirandaMethod : mirandaMethods) {
+ if (mirandaMethod.getMethod().hasPoisonPill()) {
+ resultSize++;
+ }
+ }
+ }
+ T result = alloc.apply(resultSize);
+ int i = 0;
+ for (Method.MethodVersion methodVersion : declaredMethodVersions) {
+ setter.set(result, i++, methodVersion.getMethod());
+ }
+ if (resultSize != declaredMethodVersions.length) {
+ for (Method.MethodVersion mirandaMethod : mirandaMethods) {
+ if (mirandaMethod.getMethod().hasPoisonPill()) {
+ setter.set(result, i++, mirandaMethod.getMethod());
+ }
+ }
+ }
+ return result;
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/ExceptionHandlerInteropWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/ExceptionHandlerInteropWrapper.java
new file mode 100644
index 000000000000..aff86f4c267f
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/ExceptionHandlerInteropWrapper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.espresso.classfile.ExceptionHandler;
+import com.oracle.truffle.espresso.impl.KeysArray;
+
+@ExportLibrary(InteropLibrary.class)
+public final class ExceptionHandlerInteropWrapper implements TruffleObject {
+ private final ExceptionHandler handler;
+
+ public ExceptionHandlerInteropWrapper(ExceptionHandler handler) {
+ this.handler = handler;
+ }
+
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] members = {
+ ReadMember.START_BCI,
+ ReadMember.END_BCI,
+ ReadMember.HANDLER_BCI,
+ ReadMember.CATCH_TYPE_CPI,
+ ReadMember.CATCH_TYPE,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String START_BCI = "startBCI";
+ static final String END_BCI = "endBCI";
+ static final String HANDLER_BCI = "handlerBCI";
+ static final String CATCH_TYPE_CPI = "catchTypeCPI";
+ static final String CATCH_TYPE = "catchType";
+
+ @Specialization(guards = "START_BCI.equals(member)")
+ static int getStartBCI(ExceptionHandlerInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.handler.getStartBCI();
+ }
+
+ @Specialization(guards = "END_BCI.equals(member)")
+ static int getEndBCI(ExceptionHandlerInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.handler.getEndBCI();
+ }
+
+ @Specialization(guards = "HANDLER_BCI.equals(member)")
+ static int getHandlerBCI(ExceptionHandlerInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.handler.getHandlerBCI();
+ }
+
+ @Specialization(guards = "CATCH_TYPE_CPI.equals(member)")
+ static int getCatchTypeCPI(ExceptionHandlerInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.handler.catchTypeCPI();
+ }
+
+ @Specialization(guards = "CATCH_TYPE.equals(member)")
+ static String getCatchType(ExceptionHandlerInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.handler.getCatchType().toString();
+ }
+
+ @Fallback
+ @SuppressWarnings("unused")
+ public static Object doUnknown(ExceptionHandlerInteropWrapper receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ @TruffleBoundary
+ @SuppressWarnings("static-method")
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropBootstrapMethodInvocation.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropBootstrapMethodInvocation.java
new file mode 100644
index 000000000000..1a819cf1fe3e
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropBootstrapMethodInvocation.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.InvalidArrayIndexException;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedBranchProfile;
+import com.oracle.truffle.espresso.EspressoLanguage;
+import com.oracle.truffle.espresso.classfile.descriptors.Name;
+import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
+import com.oracle.truffle.espresso.impl.KeysArray;
+import com.oracle.truffle.espresso.impl.Method;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIConstantPoolUtils.BootstrapMethodInvocationBuilder;
+import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
+
+@ExportLibrary(InteropLibrary.class)
+public final class InteropBootstrapMethodInvocation implements TruffleObject, BootstrapMethodInvocationBuilder {
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] members = {
+ ReadMember.TYPE,
+ ReadMember.IS_INDY,
+ ReadMember.NAME,
+ ReadMember.BOOTSTRAP_METHOD,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ private Object[] staticArguments;
+ private boolean isIndy;
+ private Method bootstrapMethod;
+ private Symbol name;
+ private StaticObject type;
+
+ @Override
+ public void setupStaticArguments(int length) {
+ assert staticArguments == null;
+ staticArguments = new Object[length];
+ }
+
+ @Override
+ public void staticArgument(int i, StaticObject value) {
+ assert staticArguments[i] == null;
+ staticArguments[i] = value;
+ }
+
+ @Override
+ public void staticArgumentUnresolvedDynamic(int i, int cpi) {
+ assert staticArguments[i] == null;
+ staticArguments[i] = i;
+ }
+
+ @Override
+ public void finalize(boolean finalIsIndy, Method finalBootstrapMethod, Symbol finalName, StaticObject finalType) {
+ assert !this.isIndy;
+ assert this.bootstrapMethod == null;
+ assert this.name == null;
+ assert this.type == null;
+ this.isIndy = finalIsIndy;
+ this.bootstrapMethod = finalBootstrapMethod;
+ this.name = finalName;
+ this.type = finalType;
+ }
+
+ boolean isInitialised() {
+ return this.bootstrapMethod != null;
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String TYPE = "type";
+ static final String IS_INDY = "isIndy";
+ static final String NAME = "name";
+ static final String BOOTSTRAP_METHOD = "bootstrapMethod";
+
+ @Specialization(guards = "TYPE.equals(member)")
+ static StaticObject type(InteropBootstrapMethodInvocation receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.type;
+ }
+
+ @Specialization(guards = "IS_INDY.equals(member)")
+ static boolean isIndy(InteropBootstrapMethodInvocation receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.isIndy;
+ }
+
+ @Specialization(guards = "NAME.equals(member)")
+ static String name(InteropBootstrapMethodInvocation receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.name.toString();
+ }
+
+ @Specialization(guards = "BOOTSTRAP_METHOD.equals(member)")
+ static Method bootstrapMethod(InteropBootstrapMethodInvocation receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.bootstrapMethod;
+ }
+
+ @Fallback
+ public static Object doUnknown(@SuppressWarnings("unused") InteropBootstrapMethodInvocation receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ long getArraySize() {
+ return staticArguments.length;
+ }
+
+ @ExportMessage
+ boolean isArrayElementReadable(long idx) {
+ return 0 <= idx && idx < getArraySize();
+ }
+
+ @ExportMessage
+ Object readArrayElement(long idx,
+ @Bind Node node,
+ @Cached InlinedBranchProfile exception) throws InvalidArrayIndexException {
+ if (!isArrayElementReadable(idx)) {
+ exception.enter(node);
+ throw InvalidArrayIndexException.create(idx);
+ }
+ return staticArguments[(int) idx];
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasArrayElements() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ @TruffleBoundary
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropConstantPoolWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropConstantPoolWrapper.java
new file mode 100644
index 000000000000..0b90b6a2bce7
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropConstantPoolWrapper.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEDYNAMIC;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIConstantPoolUtils.safeTagAt;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Cached.Exclusive;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.ArityException;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedBranchProfile;
+import com.oracle.truffle.espresso.EspressoLanguage;
+import com.oracle.truffle.espresso.classfile.ConstantPool;
+import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence;
+import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols;
+import com.oracle.truffle.espresso.constantpool.ResolvedConstant;
+import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool;
+import com.oracle.truffle.espresso.impl.Field;
+import com.oracle.truffle.espresso.impl.KeysArray;
+import com.oracle.truffle.espresso.impl.Klass;
+import com.oracle.truffle.espresso.impl.Method;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIConstantPoolUtils;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData;
+import com.oracle.truffle.espresso.meta.Meta;
+import com.oracle.truffle.espresso.runtime.EspressoContext;
+import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
+
+@ExportLibrary(InteropLibrary.class)
+public class InteropConstantPoolWrapper implements TruffleObject {
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set INVOCABLE_MEMBERS;
+ private static final Set READABLE_MEMBERS;
+
+ static {
+ String[] readableMembers = {
+ ReadMember.LENGTH,
+ ReadMember.NUM_INDY_ENTRIES,
+ };
+ String[] invocableMembers = {
+ InvokeMember.LOAD_REFERENCED_TYPE,
+ InvokeMember.LOOKUP_RESOLVED_METHOD,
+ InvokeMember.LOOKUP_RESOLVED_FIELD,
+ InvokeMember.LOOKUP_DESCRIPTOR,
+ InvokeMember.LOOKUP_NAME,
+ InvokeMember.LOOKUP_APPENDIX,
+ InvokeMember.LOOKUP_CONSTANT,
+ InvokeMember.LOOKUP_DYNAMIC_KIND,
+ InvokeMember.GET_TAG_BYTE_AT,
+ InvokeMember.LOOKUP_REFERENCED_TYPE,
+ InvokeMember.LOOKUP_TYPE,
+ InvokeMember.LOOKUP_BOOTSTRAP_METHOD_INVOCATION,
+ InvokeMember.LOOKUP_INDY_BOOTSTRAP_METHOD_INVOCATION,
+ };
+ String[] allMembers = new String[readableMembers.length + invocableMembers.length];
+ System.arraycopy(readableMembers, 0, allMembers, 0, readableMembers.length);
+ System.arraycopy(invocableMembers, 0, allMembers, readableMembers.length, invocableMembers.length);
+ ALL_MEMBERS = new KeysArray<>(allMembers);
+ READABLE_MEMBERS = Set.of(readableMembers);
+ INVOCABLE_MEMBERS = Set.of(invocableMembers);
+ }
+
+ private final RuntimeConstantPool constantPool;
+
+ public InteropConstantPoolWrapper(RuntimeConstantPool constantPool) {
+ this.constantPool = constantPool;
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String LENGTH = "length";
+ static final String NUM_INDY_ENTRIES = "numIndyEntries";
+
+ @Specialization(guards = "LENGTH.equals(member)")
+ static int length(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.constantPool.length();
+ }
+
+ @Specialization(guards = "NUM_INDY_ENTRIES.equals(member)")
+ static int numIndyEntries(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member,
+ @Bind Node node) {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ Meta meta = EspressoContext.get(node).getMeta();
+ JVMCIIndyData indyData = JVMCIIndyData.maybeGetExisting(receiver.constantPool.getHolder(), meta);
+ if (indyData == null) {
+ return 0;
+ }
+ return indyData.getLocationCount();
+ }
+
+ @Fallback
+ public static Object doUnknown(@SuppressWarnings("unused") InteropConstantPoolWrapper receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ abstract static class InvokeMember {
+ static final String LOAD_REFERENCED_TYPE = "loadReferencedType";
+ static final String LOOKUP_RESOLVED_METHOD = "lookupResolvedMethod";
+ static final String LOOKUP_RESOLVED_FIELD = "lookupResolvedField";
+ static final String LOOKUP_DESCRIPTOR = "lookupDescriptor";
+ static final String LOOKUP_NAME = "lookupName";
+ static final String LOOKUP_APPENDIX = "lookupAppendix";
+ static final String LOOKUP_CONSTANT = "lookupConstant";
+ static final String LOOKUP_DYNAMIC_KIND = "lookupDynamicKind";
+ static final String GET_TAG_BYTE_AT = "getTagByteAt";
+ static final String LOOKUP_REFERENCED_TYPE = "lookupReferencedType";
+ static final String LOOKUP_TYPE = "lookupType";
+ static final String LOOKUP_BOOTSTRAP_METHOD_INVOCATION = "lookupBootstrapMethodInvocation";
+ static final String LOOKUP_INDY_BOOTSTRAP_METHOD_INVOCATION = "lookupIndyBootstrapMethodInvocation";
+
+ @Specialization(guards = "LOAD_REFERENCED_TYPE.equals(member)")
+ static boolean loadReferencedType(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ Meta meta = EspressoContext.get(node).getMeta();
+ RuntimeConstantPool cp = receiver.constantPool;
+ return JVMCIConstantPoolUtils.loadReferencedType0(cpi, opcode, cp, cp.getHolder(), meta);
+ }
+
+ @Specialization(guards = "LOOKUP_RESOLVED_METHOD.equals(member)")
+ static Object lookupResolvedMethod(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @CachedLibrary(limit = "1") @Exclusive InteropLibrary interopLibrary,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 3) {
+ arityError.enter(node);
+ throw ArityException.create(3, 3, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ Method caller;
+ if ((arguments[2] instanceof Method callerMethod)) {
+ caller = callerMethod;
+ } else if (interopLibrary.isNull(arguments[2])) {
+ caller = null;
+ } else {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ Method result = JVMCIConstantPoolUtils.lookupResolvedMethod(receiver.constantPool, cpi, opcode, caller, context);
+ if (result == null) {
+ return StaticObject.NULL;
+ }
+ return result;
+ }
+
+ @Specialization(guards = "LOOKUP_RESOLVED_FIELD.equals(member)")
+ static Object lookupResolvedField(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @CachedLibrary(limit = "1") @Exclusive InteropLibrary interopLibrary,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 3) {
+ arityError.enter(node);
+ throw ArityException.create(3, 3, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ Method method;
+ if ((arguments[2] instanceof Method m)) {
+ method = m;
+ } else if (interopLibrary.isNull(arguments[2])) {
+ method = null;
+ } else {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ Field result = JVMCIConstantPoolUtils.lookupResolvedField(receiver.constantPool, cpi, method, opcode, context);
+ if (result == null) {
+ return StaticObject.NULL;
+ }
+ return result;
+ }
+
+ @Specialization(guards = "LOOKUP_DESCRIPTOR.equals(member)")
+ static String lookupDescriptor(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ return JVMCIConstantPoolUtils.lookupDescriptor(receiver.constantPool, cpi, context).toString();
+ }
+
+ @Specialization(guards = "LOOKUP_NAME.equals(member)")
+ static String lookupName(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ return JVMCIConstantPoolUtils.lookupName(receiver.constantPool, cpi, context).toString();
+ }
+
+ @Specialization(guards = "LOOKUP_APPENDIX.equals(member)")
+ static StaticObject lookupAppendix(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer index)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ return JVMCIConstantPoolUtils.lookupAppendix(receiver.constantPool, index, opcode, context);
+ }
+
+ @Specialization(guards = "LOOKUP_DYNAMIC_KIND.equals(member)")
+ static int lookupDynamicKind(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ return TypeSymbols.getJavaKind(receiver.constantPool.dynamicType(cpi)).getTypeChar();
+ }
+
+ @Specialization(guards = "LOOKUP_CONSTANT.equals(member)")
+ static Object lookupConstant(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError,
+ @Cached @Exclusive InlinedBranchProfile indexError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1 && arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(1, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ boolean resolve = false;
+ if (arguments.length > 1) {
+ if (!(arguments[1] instanceof Boolean shouldDesolve)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ resolve = shouldDesolve;
+ }
+ if (cpi < 0 || cpi >= receiver.constantPool.length()) {
+ indexError.enter(node);
+ Meta meta = EspressoContext.get(node).getMeta();
+ throw meta.throwIndexOutOfBoundsExceptionBoundary("invalid cpi", cpi, receiver.constantPool.length());
+ }
+ return switch (receiver.constantPool.tagAt(cpi)) {
+ case INTEGER -> receiver.constantPool.intAt(cpi);
+ case LONG -> receiver.constantPool.longAt(cpi);
+ case FLOAT -> receiver.constantPool.floatAt(cpi);
+ case DOUBLE -> receiver.constantPool.doubleAt(cpi);
+ case STRING -> receiver.constantPool.resolvedStringAt(cpi);
+ case METHODHANDLE, METHODTYPE -> {
+ ResolvedConstant resolvedConstant;
+ if (resolve) {
+ resolvedConstant = receiver.constantPool.resolvedAt(receiver.constantPool.getHolder(), cpi);
+ } else {
+ Meta meta = EspressoContext.get(node).getMeta();
+ resolvedConstant = receiver.constantPool.peekResolvedOrNull(cpi, meta);
+ }
+ if (resolvedConstant == null) {
+ yield StaticObject.NULL;
+ }
+ yield resolvedConstant.value();
+ }
+ case DYNAMIC -> {
+ Meta meta = EspressoContext.get(node).getMeta();
+ ResolvedConstant resolvedConstant = receiver.constantPool.peekResolvedOrNull(cpi, meta);
+ if (resolvedConstant == null) {
+ yield StaticObject.NULL;
+ }
+ yield switch (TypeSymbols.getJavaKind(receiver.constantPool.dynamicType(cpi))) {
+ case Boolean -> ((Integer) resolvedConstant.value() != 0);
+ case Byte -> (byte) (int) resolvedConstant.value();
+ case Short -> (short) (int) resolvedConstant.value();
+ case Char -> (char) (int) resolvedConstant.value();
+ case Int -> (int) resolvedConstant.value();
+ case Float -> (float) resolvedConstant.value();
+ case Double -> (double) resolvedConstant.value();
+ case Long -> (long) resolvedConstant.value();
+ case Object -> resolvedConstant.value();
+ default -> throw meta.throwIllegalArgumentExceptionBoundary();
+ };
+ }
+ default -> false;
+ };
+ }
+
+ @Specialization(guards = "GET_TAG_BYTE_AT.equals(member)")
+ static byte getTagByteAt(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer cpi)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ Meta meta = EspressoContext.get(node).getMeta();
+ return safeTagAt(receiver.constantPool, cpi, meta).getValue();
+ }
+
+ @Specialization(guards = "LOOKUP_REFERENCED_TYPE.equals(member)")
+ static Object lookupReferencedType(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer index)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ Object result = JVMCIConstantPoolUtils.lookupReferencedType(receiver.constantPool, index, opcode, context);
+ if (result instanceof Klass klass) {
+ return new TypeWrapper(klass);
+ } else {
+ ByteSequence type = (ByteSequence) result;
+ return type.toString();
+ }
+ }
+
+ @Specialization(guards = "LOOKUP_TYPE.equals(member)")
+ static Object lookupType(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer index)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ if (safeTagAt(receiver.constantPool, index, context.getMeta()) == ConstantPool.Tag.CLASS) {
+ ResolvedConstant resolvedConstant = receiver.constantPool.peekResolvedOrNull(index, context.getMeta());
+ if (resolvedConstant == null || !resolvedConstant.isSuccess()) {
+ return TypeSymbols.nameToType(receiver.constantPool.className(index)).toString();
+ }
+ Klass klass = (Klass) resolvedConstant.value();
+ return new TypeWrapper(klass);
+ }
+ if (safeTagAt(receiver.constantPool, index, context.getMeta()) == ConstantPool.Tag.UTF8) {
+ return TypeSymbols.nameToType(receiver.constantPool.utf8At(index)).toString();
+ }
+ throw context.getMeta().throwIllegalArgumentExceptionBoundary();
+ }
+
+ @Specialization(guards = "LOOKUP_BOOTSTRAP_METHOD_INVOCATION.equals(member)")
+ static Object lookupBootstrapMethodInvocation(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer index)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ if (!(arguments[1] instanceof Integer opcode)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ InteropBootstrapMethodInvocation builder = new InteropBootstrapMethodInvocation();
+ JVMCIConstantPoolUtils.lookupBootstrapMethodInvocation(receiver.constantPool, index, opcode, context, builder);
+ if (builder.isInitialised()) {
+ return builder;
+ }
+ return StaticObject.NULL;
+ }
+
+ @Specialization(guards = "LOOKUP_INDY_BOOTSTRAP_METHOD_INVOCATION.equals(member)")
+ static Object lookupIndyBootstrapMethodInvocation(InteropConstantPoolWrapper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof Integer index)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments);
+ }
+ EspressoContext context = EspressoContext.get(node);
+ JVMCIIndyData indyData = JVMCIIndyData.getExisting(receiver.constantPool.getHolder(), context.getMeta());
+ if (index < 0 || index >= indyData.getLocationCount()) {
+ context.getMeta().throwIndexOutOfBoundsExceptionBoundary("Invalid site index", index, indyData.getLocationCount());
+ }
+ int indyCpi = indyData.recoverFullCpi(index);
+ InteropBootstrapMethodInvocation builder = new InteropBootstrapMethodInvocation();
+ JVMCIConstantPoolUtils.lookupBootstrapMethodInvocation(receiver.constantPool, indyCpi, INVOKEDYNAMIC, context, builder);
+ assert builder.isInitialised();
+ return builder;
+ }
+
+ @Fallback
+ @SuppressWarnings("unused")
+ static Object doUnknown(InteropConstantPoolWrapper receiver, String member, Object[] arguments) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ @TruffleBoundary
+ public boolean isMemberReadable(String member) {
+ return READABLE_MEMBERS.contains(member);
+ }
+
+ @ExportMessage
+ @TruffleBoundary
+ @SuppressWarnings("static-method")
+ public boolean isMemberInvocable(String member) {
+ return INVOCABLE_MEMBERS.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropLineNumberTableHelper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropLineNumberTableHelper.java
new file mode 100644
index 000000000000..6d3ac195e27e
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropLineNumberTableHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.InvalidArrayIndexException;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+
+@ExportLibrary(InteropLibrary.class)
+public class InteropLineNumberTableHelper implements TruffleObject {
+ private final char[] data;
+
+ public InteropLineNumberTableHelper(char[] data) {
+ this.data = data;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasArrayElements() {
+ return true;
+ }
+
+ @ExportMessage
+ public boolean isArrayElementReadable(long index) {
+ return index >= 0 && index < getArraySize();
+ }
+
+ @ExportMessage
+ public long getArraySize() {
+ return data.length;
+ }
+
+ @ExportMessage
+ public int readArrayElement(long index) throws InvalidArrayIndexException {
+ if (!isArrayElementReadable(index)) {
+ throw InvalidArrayIndexException.create(index);
+ }
+ return data[(int) index];
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/JVMCIInteropHelper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/JVMCIInteropHelper.java
new file mode 100644
index 000000000000..71bc59fc1305
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/JVMCIInteropHelper.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Bind;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Cached.Exclusive;
+import com.oracle.truffle.api.dsl.Cached.Shared;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.ArityException;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.interop.UnsupportedMessageException;
+import com.oracle.truffle.api.interop.UnsupportedTypeException;
+import com.oracle.truffle.api.library.CachedLibrary;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.profiles.InlinedBranchProfile;
+import com.oracle.truffle.espresso.EspressoLanguage;
+import com.oracle.truffle.espresso.classfile.attributes.Attribute;
+import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence;
+import com.oracle.truffle.espresso.classfile.descriptors.ParserSymbols.ParserNames;
+import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
+import com.oracle.truffle.espresso.classfile.descriptors.Type;
+import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols;
+import com.oracle.truffle.espresso.classfile.descriptors.Validation;
+import com.oracle.truffle.espresso.impl.ContextAccess;
+import com.oracle.truffle.espresso.impl.Field;
+import com.oracle.truffle.espresso.impl.KeysArray;
+import com.oracle.truffle.espresso.impl.Klass;
+import com.oracle.truffle.espresso.impl.Method;
+import com.oracle.truffle.espresso.impl.ObjectKlass;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils;
+import com.oracle.truffle.espresso.meta.Meta;
+import com.oracle.truffle.espresso.runtime.EspressoContext;
+import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
+
+@ExportLibrary(InteropLibrary.class)
+public final class JVMCIInteropHelper implements ContextAccess, TruffleObject {
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] members = {
+ InvokeMember.GET_FLAGS,
+ InvokeMember.GET_NAME,
+ InvokeMember.GET_INSTANCE_FIELDS,
+ InvokeMember.GET_STATIC_FIELDS,
+ InvokeMember.LOOKUP_INSTANCE_TYPE,
+ InvokeMember.IS_ASSIGNABLE_FROM,
+ InvokeMember.GET_INTERFACES,
+ InvokeMember.INITIALIZE,
+ InvokeMember.LINK,
+ InvokeMember.IS_INITIALIZED,
+ InvokeMember.IS_LINKED,
+ InvokeMember.GET_CLASS_INITIALIZER,
+ InvokeMember.GET_DECLARED_METHODS,
+ InvokeMember.GET_DECLARED_CONSTRUCTORS,
+ InvokeMember.GET_ALL_METHODS,
+ InvokeMember.GET_ANNOTATION_DATA,
+ InvokeMember.HAS_SAME_CLASSLOADER,
+ InvokeMember.DECLARES_DEFAULT_METHODS,
+ InvokeMember.HAS_DEFAULT_METHODS,
+ InvokeMember.IS_LEAF_CLASS,
+ InvokeMember.GET_CONSTANT_POOL,
+ InvokeMember.GET_SOURCE_FILENAME,
+ InvokeMember.ESPRESSO_SINGLE_IMPLEMENTOR,
+ InvokeMember.TO_GUEST_STRING,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ private final EspressoContext context;
+
+ public JVMCIInteropHelper(EspressoContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public EspressoContext getContext() {
+ return context;
+ }
+
+ @ExportMessage
+ abstract static class InvokeMember {
+ static final String GET_FLAGS = "getFlags";
+ static final String GET_NAME = "getName";
+ static final String GET_INSTANCE_FIELDS = "getInstanceFields";
+ static final String GET_STATIC_FIELDS = "getStaticFields";
+ static final String LOOKUP_INSTANCE_TYPE = "lookupInstanceType";
+ static final String INITIALIZE = "initialize";
+ static final String LINK = "link";
+ static final String IS_INITIALIZED = "isInitialized";
+ static final String IS_LINKED = "isLinked";
+ static final String IS_ASSIGNABLE_FROM = "isAssignableFrom";
+ static final String GET_INTERFACES = "getInterfaces";
+ static final String GET_CLASS_INITIALIZER = "getClassInitializer";
+ static final String GET_DECLARED_METHODS = "getDeclaredMethods";
+ static final String GET_DECLARED_CONSTRUCTORS = "getDeclaredConstructors";
+ static final String GET_ALL_METHODS = "getAllMethods";
+ static final String GET_ANNOTATION_DATA = "getAnnotationData";
+ static final String HAS_SAME_CLASSLOADER = "hasSameClassLoader";
+ static final String DECLARES_DEFAULT_METHODS = "declaresDefaultMethods";
+ static final String HAS_DEFAULT_METHODS = "hasDefaultMethods";
+ static final String IS_LEAF_CLASS = "isLeafClass";
+ static final String GET_CONSTANT_POOL = "getConstantPool";
+ static final String GET_SOURCE_FILENAME = "getSourceFileName";
+ static final String ESPRESSO_SINGLE_IMPLEMENTOR = "espressoSingleImplementor";
+ static final String TO_GUEST_STRING = "toGuestString";
+
+ @Specialization(guards = "GET_FLAGS.equals(member)")
+ static int getFlags(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.getModifiers();
+ }
+
+ @Specialization(guards = "GET_NAME.equals(member)")
+ static String getName(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.getType().toString();
+ }
+
+ @Specialization(guards = "GET_INSTANCE_FIELDS.equals(member)")
+ static Object getInstanceFields(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return new KeysArray<>(klass.getAllDeclaredInstanceFields());
+ }
+
+ @Specialization(guards = "GET_STATIC_FIELDS.equals(member)")
+ static Object getStaticFields(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return new KeysArray<>(klass.getStaticFieldTable());
+ }
+
+ @Specialization(guards = "GET_INTERFACES.equals(member)")
+ static Object getInterfaces(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ ObjectKlass[] interfaces = klass.getSuperInterfaces();
+ if (interfaces.length == 0) {
+ return StaticObject.NULL;
+ }
+ return new KeysArray<>(interfaces);
+ }
+
+ @Specialization(guards = "LOOKUP_INSTANCE_TYPE.equals(member)")
+ static Object lookupInstanceType(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @CachedLibrary(limit = "2") InteropLibrary stringInterop,
+ @CachedLibrary(limit = "2") InteropLibrary booleanInterop,
+ @Cached @Exclusive InlinedBranchProfile typeError,
+ @Cached @Exclusive InlinedBranchProfile arityError,
+ @Cached @Exclusive InlinedBranchProfile valueError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 3) {
+ arityError.enter(node);
+ throw ArityException.create(3, 3, arguments.length);
+ }
+ String type;
+ try {
+ type = stringInterop.asString(arguments[0]);
+ } catch (UnsupportedMessageException e) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected a string as first argument");
+ }
+ if (type.charAt(0) != 'L' || type.charAt(type.length() - 1) != ';') {
+ valueError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type descriptor as first argument (e.g., Lfoo/Bar;)");
+ }
+ if (!(arguments[1] instanceof ObjectKlass accessingKlass)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type as second argument");
+ }
+ boolean resolve;
+ try {
+ resolve = booleanInterop.asBoolean(arguments[2]);
+ } catch (UnsupportedMessageException e) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected a boolean as third argument");
+ }
+ EspressoContext context = EspressoContext.get(node);
+ ByteSequence typeBytes = ByteSequence.create(type);
+ if (!Validation.validTypeDescriptor(typeBytes, false)) {
+ valueError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected a valid instance type descriptor as first argument (e.g., Lfoo/Bar;)");
+ }
+ Symbol typeSymbol = context.getTypes().lookupValidType(typeBytes);
+ if (typeSymbol == null) {
+ return StaticObject.NULL;
+ }
+ assert !TypeSymbols.isArray(typeSymbol);
+ Meta meta = context.getMeta();
+ StaticObject loader = accessingKlass.getDefiningClassLoader();
+ if (resolve) {
+ return meta.loadKlassOrFail(typeSymbol, loader, accessingKlass.protectionDomain());
+ } else {
+ Klass klass = meta.getRegistries().findLoadedClass(typeSymbol, loader);
+ if (klass == null) {
+ return StaticObject.NULL;
+ }
+ return klass;
+ }
+ }
+
+ @Specialization(guards = "INITIALIZE.equals(member)")
+ static Object initialize(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ klass.initialize();
+ return StaticObject.NULL;
+ }
+
+ @Specialization(guards = "LINK.equals(member)")
+ static Object link(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ klass.ensureLinked();
+ return StaticObject.NULL;
+ }
+
+ @Specialization(guards = "IS_INITIALIZED.equals(member)")
+ static boolean isInitialized(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.isInitialized();
+ }
+
+ @Specialization(guards = "IS_LINKED.equals(member)")
+ static boolean isLinked(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.isLinked();
+ }
+
+ @Specialization(guards = "IS_ASSIGNABLE_FROM.equals(member)")
+ static boolean isAssignableFrom(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof ObjectKlass selfKlass)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type");
+ }
+ if (!(arguments[1] instanceof ObjectKlass otherKlass)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type");
+ }
+ return selfKlass.isAssignableFrom(otherKlass);
+ }
+
+ @Specialization(guards = "GET_CLASS_INITIALIZER.equals(member)")
+ static Object getClassInitializer(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ Method classInitializer = klass.getClassInitializer();
+ if (classInitializer == null) {
+ return StaticObject.NULL;
+ }
+ return classInitializer;
+ }
+
+ @Specialization(guards = "GET_DECLARED_METHODS.equals(member)")
+ static Object getDeclaredMethods(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ Method[] methods = klass.getDeclaredMethods();
+ if (methods.length == 0) {
+ return StaticObject.NULL;
+ }
+ return new KeysArray<>(methods);
+ }
+
+ @Specialization(guards = "GET_DECLARED_CONSTRUCTORS.equals(member)")
+ static Object getDeclaredConstructors(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ Method[] methods = klass.getDeclaredConstructors();
+ if (methods.length == 0) {
+ return StaticObject.NULL;
+ }
+ return new KeysArray<>(methods);
+ }
+
+ @Specialization(guards = "GET_ALL_METHODS.equals(member)")
+ static Object getAllMethods(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ Method[] methods = JVMCIUtils.getAllMethods(klass, Method[]::new, (array, i, m) -> array[i] = m);
+ if (methods.length == 0) {
+ return StaticObject.NULL;
+ }
+ return new KeysArray<>(methods);
+ }
+
+ // This would be less awkward if we could return Klass as non-statics JVMCI object like
+ // field and method
+ @Specialization(guards = "GET_ANNOTATION_DATA.equals(member)")
+ static Object getAnnotationData(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError
+ // @Cached("create(meta.java_nio_ByteBuffer_wrap.getCallTargetForceInit())") DirectCallNode
+ // wrap
+ ) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ byte[] data = null;
+ if (arguments[0] instanceof ObjectKlass klass) {
+ Attribute annotations = klass.getAttribute(ParserNames.RuntimeVisibleAnnotations);
+ if (annotations != null) {
+ data = annotations.getData();
+ }
+ } else if (arguments[0] instanceof Method method) {
+ Attribute annotations = method.getAttribute(ParserNames.RuntimeVisibleAnnotations);
+ if (annotations != null) {
+ data = annotations.getData();
+ }
+ } else if (arguments[0] instanceof Field field) {
+ Attribute annotations = field.getAttribute(ParserNames.RuntimeVisibleAnnotations);
+ if (annotations != null) {
+ data = annotations.getData();
+ }
+ } else {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type, a method, or a field as first argument");
+ }
+ if (data == null) {
+ return StaticObject.NULL;
+ }
+ // TODO should byte[] also support the buffer messages?
+ return EspressoContext.get(node).getMeta().java_nio_ByteBuffer_wrap.invokeDirectStatic(StaticObject.wrap(data, EspressoContext.get(node).getMeta()));
+ }
+
+ @Specialization(guards = "HAS_SAME_CLASSLOADER.equals(member)")
+ static boolean hasSameClassLoader(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 2) {
+ arityError.enter(node);
+ throw ArityException.create(2, 2, arguments.length);
+ }
+ if (!(arguments[0] instanceof ObjectKlass klass1)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type");
+ }
+ if (!(arguments[1] instanceof ObjectKlass klass2)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type");
+ }
+ return klass1.getDefiningClassLoader() == klass2.getDefiningClassLoader();
+ }
+
+ @Specialization(guards = "DECLARES_DEFAULT_METHODS.equals(member)")
+ static boolean declaresDefaultMethods(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.hasDeclaredDefaultMethods();
+ }
+
+ @Specialization(guards = "HAS_DEFAULT_METHODS.equals(member)")
+ static boolean hasDefaultMethods(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.hasDefaultMethods();
+ }
+
+ @Specialization(guards = "IS_LEAF_CLASS.equals(member)")
+ static boolean isLeafClass(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ EspressoContext context = EspressoContext.get(node);
+ return context.getClassHierarchyOracle().isLeafKlass(klass).isValid();
+ }
+
+ @Specialization(guards = "GET_CONSTANT_POOL.equals(member)")
+ static Object getConstantPool(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return new InteropConstantPoolWrapper(klass.getConstantPool());
+ }
+
+ @Specialization(guards = "GET_SOURCE_FILENAME.equals(member)")
+ static Object getSourceFileName(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ return klass.getSourceFile();
+ }
+
+ @Specialization(guards = "ESPRESSO_SINGLE_IMPLEMENTOR.equals(member)")
+ static Object espressoSingleImplementor(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ ObjectKlass klass = getSingleKlassArgument(arguments, node, typeError, arityError);
+ EspressoContext context = EspressoContext.get(node);
+ ObjectKlass result = context.getClassHierarchyOracle().readSingleImplementor(klass).get();
+ if (result == null) {
+ return StaticObject.NULL;
+ }
+ return result;
+ }
+
+ @Specialization(guards = "TO_GUEST_STRING.equals(member)")
+ static Object toGuestString(JVMCIInteropHelper receiver, @SuppressWarnings("unused") String member, Object[] arguments,
+ @Bind Node node,
+ @CachedLibrary(limit = "1") InteropLibrary library,
+ @Cached @Shared InlinedBranchProfile typeError,
+ @Cached @Shared InlinedBranchProfile arityError) throws ArityException, UnsupportedTypeException {
+ assert receiver != null;
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ try {
+ String string = library.asString(arguments[0]);
+ EspressoContext context = EspressoContext.get(node);
+ return context.getMeta().toGuestString(string);
+ } catch (UnsupportedMessageException e) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an string");
+ }
+ }
+
+ @Fallback
+ @SuppressWarnings("unused")
+ static Object doUnknown(JVMCIInteropHelper receiver, String member, Object[] arguments) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+
+ private static ObjectKlass getSingleKlassArgument(Object[] arguments, Node node, InlinedBranchProfile typeError, InlinedBranchProfile arityError)
+ throws ArityException, UnsupportedTypeException {
+ assert EspressoLanguage.get(node).isExternalJVMCIEnabled();
+ if (arguments.length != 1) {
+ arityError.enter(node);
+ throw ArityException.create(1, 1, arguments.length);
+ }
+ if (!(arguments[0] instanceof ObjectKlass klass)) {
+ typeError.enter(node);
+ throw UnsupportedTypeException.create(arguments, "Expected an instance type");
+ }
+ return klass;
+ }
+ }
+
+ @ExportMessage
+ @TruffleBoundary
+ @SuppressWarnings("static-method")
+ public boolean isMemberInvocable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/LocalInteropWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/LocalInteropWrapper.java
new file mode 100644
index 000000000000..82a515328438
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/LocalInteropWrapper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.espresso.classfile.attributes.Local;
+import com.oracle.truffle.espresso.impl.KeysArray;
+
+@ExportLibrary(InteropLibrary.class)
+public final class LocalInteropWrapper implements TruffleObject {
+ private final Local local;
+
+ public LocalInteropWrapper(Local local) {
+ this.local = local;
+ }
+
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ static {
+ String[] members = {
+ ReadMember.START_BCI,
+ ReadMember.END_BCI,
+ ReadMember.SLOT,
+ ReadMember.NAME,
+ ReadMember.TYPE,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String START_BCI = "startBCI";
+ static final String END_BCI = "endBCI";
+ static final String SLOT = "slot";
+ static final String NAME = "name";
+ static final String TYPE = "catchType";
+
+ @Specialization(guards = "START_BCI.equals(member)")
+ static int getStartBCI(LocalInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.local.getStartBCI();
+ }
+
+ @Specialization(guards = "END_BCI.equals(member)")
+ static int getEndBCI(LocalInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.local.getEndBCI();
+ }
+
+ @Specialization(guards = "SLOT.equals(member)")
+ static int getSlot(LocalInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.local.getSlot();
+ }
+
+ @Specialization(guards = "NAME.equals(member)")
+ static String getName(LocalInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.local.getName().toString();
+ }
+
+ @Specialization(guards = "TYPE.equals(member)")
+ static String getType(LocalInteropWrapper receiver, @SuppressWarnings("unused") String member) {
+ return receiver.local.getTypeOrDesc().toString();
+ }
+
+ @Fallback
+ @SuppressWarnings("unused")
+ public static Object doUnknown(LocalInteropWrapper receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ @TruffleBoundary
+ @SuppressWarnings("static-method")
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/TypeWrapper.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/TypeWrapper.java
new file mode 100644
index 000000000000..1646390745b6
--- /dev/null
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/TypeWrapper.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.espresso.impl.jvmci.external;
+
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Fallback;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.InteropLibrary;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.UnknownIdentifierException;
+import com.oracle.truffle.api.library.ExportLibrary;
+import com.oracle.truffle.api.library.ExportMessage;
+import com.oracle.truffle.espresso.EspressoLanguage;
+import com.oracle.truffle.espresso.impl.ArrayKlass;
+import com.oracle.truffle.espresso.impl.KeysArray;
+import com.oracle.truffle.espresso.impl.Klass;
+
+/**
+ * Interop wrapper used when returning types of unknown kind (primitive, array, instance). See
+ * {@code com.oracle.truffle.espresso.vmaccess.EspressoExternalVMAccess.toResolvedJavaType}.
+ */
+@ExportLibrary(InteropLibrary.class)
+public class TypeWrapper implements TruffleObject {
+ private static final KeysArray ALL_MEMBERS;
+ private static final Set ALL_MEMBERS_SET;
+
+ private final Klass klass;
+
+ static {
+ String[] members = {
+ ReadMember.KIND,
+ ReadMember.ELEMENTAL,
+ ReadMember.DIMENSIONS,
+ ReadMember.META,
+ };
+ ALL_MEMBERS = new KeysArray<>(members);
+ ALL_MEMBERS_SET = Set.of(members);
+ }
+
+ public TypeWrapper(Klass klass) {
+ this.klass = klass;
+ }
+
+ @ExportMessage
+ abstract static class ReadMember {
+ static final String KIND = "kind";
+ static final String ELEMENTAL = "elemental";
+ static final String DIMENSIONS = "dimensions";
+ static final String META = "meta";
+
+ @Specialization(guards = "KIND.equals(member)")
+ static int kind(TypeWrapper receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ if (receiver.klass.isArray()) {
+ return '[';
+ }
+ return receiver.klass.getJavaKind().getTypeChar();
+ }
+
+ @Specialization(guards = "ELEMENTAL.equals(member)")
+ static TypeWrapper elemental(TypeWrapper receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return new TypeWrapper(receiver.klass.getElementalType());
+ }
+
+ @Specialization(guards = "DIMENSIONS.equals(member)")
+ static int dimensions(TypeWrapper receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ if (receiver.klass instanceof ArrayKlass arrayKlass) {
+ return arrayKlass.getDimension();
+ }
+ return 0;
+ }
+
+ @Specialization(guards = "META.equals(member)")
+ static Klass meta(TypeWrapper receiver, @SuppressWarnings("unused") String member) {
+ assert EspressoLanguage.get(null).isExternalJVMCIEnabled();
+ return receiver.klass;
+ }
+
+ @Fallback
+ public static Object doUnknown(@SuppressWarnings("unused") TypeWrapper receiver, String member) throws UnknownIdentifierException {
+ throw UnknownIdentifierException.create(member);
+ }
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ @CompilerDirectives.TruffleBoundary
+ public boolean isMemberReadable(String member) {
+ return ALL_MEMBERS_SET.contains(member);
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public boolean hasMembers() {
+ return true;
+ }
+
+ @ExportMessage
+ @SuppressWarnings("static-method")
+ public Object getMembers(@SuppressWarnings("unused") boolean includeInternal) {
+ return ALL_MEMBERS;
+ }
+}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java
index 70d652f3f8a3..d0120eb96ef0 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/meta/Meta.java
@@ -2283,10 +2283,10 @@ private JVMCISupport() {
EspressoResolvedInstanceType = knownKlass(Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType);
EspressoResolvedInstanceType_init = EspressoResolvedInstanceType.requireDeclaredMethod(Names._init_, Signatures._void);
HIDDEN_OBJECTKLASS_MIRROR = EspressoResolvedInstanceType.requireHiddenField(Names.HIDDEN_OBJECTKLASS_MIRROR);
- EspressoResolvedInstanceType_DECLARED_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType, Names.DECLARED_ANNOTATIONS);
- EspressoResolvedInstanceType_PARAMETER_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType, Names.PARAMETER_ANNOTATIONS);
- EspressoResolvedInstanceType_TYPE_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType, Names.TYPE_ANNOTATIONS);
- EspressoResolvedInstanceType_ANNOTATION_DEFAULT_VALUE = getIntConstant(EspressoResolvedInstanceType, Names.ANNOTATION_DEFAULT_VALUE);
+ EspressoResolvedInstanceType_DECLARED_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType.getSuperKlass(), Names.DECLARED_ANNOTATIONS);
+ EspressoResolvedInstanceType_PARAMETER_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType.getSuperKlass(), Names.PARAMETER_ANNOTATIONS);
+ EspressoResolvedInstanceType_TYPE_ANNOTATIONS = getIntConstant(EspressoResolvedInstanceType.getSuperKlass(), Names.TYPE_ANNOTATIONS);
+ EspressoResolvedInstanceType_ANNOTATION_DEFAULT_VALUE = getIntConstant(EspressoResolvedInstanceType.getSuperKlass(), Names.ANNOTATION_DEFAULT_VALUE);
EspressoResolvedJavaField = knownKlass(Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField);
EspressoResolvedJavaField_init = EspressoResolvedJavaField.requireDeclaredMethod(Names._init_, Signatures._void_EspressoResolvedInstanceType);
@@ -2295,7 +2295,8 @@ private JVMCISupport() {
EspressoResolvedJavaMethod = knownKlass(Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod);
EspressoResolvedJavaMethod_init = EspressoResolvedJavaMethod.requireDeclaredMethod(Names._init_, Signatures._void_EspressoResolvedInstanceType_boolean);
HIDDEN_METHOD_MIRROR = EspressoResolvedJavaMethod.requireHiddenField(Names.HIDDEN_METHOD_MIRROR);
- EspressoResolvedJavaMethod_holder = EspressoResolvedJavaMethod.requireDeclaredField(Names.holder, Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType);
+ EspressoResolvedJavaMethod_holder = EspressoResolvedJavaMethod.getSuperKlass().requireDeclaredField(Names.holder,
+ Types.com_oracle_truffle_espresso_jvmci_meta_AbstractEspressoResolvedInstanceType);
EspressoResolvedJavaRecordComponent = knownKlass(Types.com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent);
EspressoResolvedJavaRecordComponent_init = EspressoResolvedJavaRecordComponent.requireDeclaredMethod(Names._init_, Signatures._void_EspressoResolvedInstanceType_int_int_int);
@@ -2315,7 +2316,7 @@ private JVMCISupport() {
EspressoBootstrapMethodInvocation = knownKlass(Types.com_oracle_truffle_espresso_jvmci_meta_EspressoBootstrapMethodInvocation);
EspressoBootstrapMethodInvocation_init = EspressoBootstrapMethodInvocation.requireDeclaredMethod(Names._init_,
- Signatures._void_boolean_EspressoResolvedJavaMethod_String_JavaConstant_JavaConstant_array_int_EspressoConstantPool);
+ Signatures._void_boolean_AbstractEspressoResolvedJavaMethod_String_JavaConstant_JavaConstant_array_int_AbstractEspressoConstantPool);
Services = knownKlass(Types.jdk_vm_ci_services_Services);
Services_openJVMCITo = Services.requireDeclaredMethod(Names.openJVMCITo, Signatures._void_Module);
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java
index 33be769b253f..408071b1ca9d 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java
@@ -116,7 +116,6 @@ public boolean hasScope(@SuppressWarnings("unused") Frame frame) {
}
@ExportMessage
- @SuppressWarnings("static-method")
public Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) {
return new SubstitutionScope(frame.getArguments(), getMethodVersion());
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java
index 6232d043902f..6d2284e7e64e 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java
@@ -69,7 +69,6 @@ public boolean hasScope(@SuppressWarnings("unused") Frame frame) {
}
@ExportMessage
- @SuppressWarnings("static-method")
public Object getScope(Frame frame, @SuppressWarnings("unused") boolean nodeEnter) {
return new SubstitutionScope(frame.getArguments(), getMethodVersion());
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/AddPathToBindingsNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/AddPathToBindingsNode.java
index b3f755ac1166..11a110eb142f 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/AddPathToBindingsNode.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/commands/AddPathToBindingsNode.java
@@ -62,6 +62,7 @@ public boolean isExecutable() {
}
@ExportMessage
+ @SuppressWarnings("static-method")
public Object execute(Object[] args,
@Cached AddPathToBindingsNode addPath) throws ArityException, UnsupportedTypeException {
// Args are checked in addPathToBindingsNode.
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java
index 5ff8339eeb00..0e39aee98bb2 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java
@@ -48,6 +48,7 @@ public abstract class BaseQuickNode extends EspressoNode implements BciProvider,
public abstract int execute(VirtualFrame frame, boolean isContinuationResume);
+ @Override
public boolean isInstrumentable() {
return true;
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java
index 9559829ecd3b..a63864731e6e 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/EspressoContext.java
@@ -618,7 +618,8 @@ private void spawnVM() throws ContextPatchingException {
bindingsLoader = createBindingsLoader(systemClassLoader);
topBindings = new EspressoBindings(
getEnv().getOptions().get(EspressoOptions.ExposeNativeJavaVM),
- bindingsLoader != systemClassLoader);
+ bindingsLoader != systemClassLoader,
+ getLanguage().isExternalJVMCIEnabled());
initDoneTimeNanos = System.nanoTime();
long elapsedNanos = initDoneTimeNanos - initStartTimeNanos;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/MapInterop.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/MapInterop.java
index 47965aef13b5..97e5b228336e 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/MapInterop.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/runtime/dispatch/staticobject/MapInterop.java
@@ -177,7 +177,6 @@ public static void removeHashEntry(StaticObject receiver, Object key,
}
}
- @SuppressWarnings("static-method")
@ExportMessage
public static Object getHashEntriesIterator(StaticObject receiver,
@CachedLibrary(limit = "1") InteropLibrary setLibrary,
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_EspressoJVMCIRuntime.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_EspressoJVMCIRuntime.java
index c45811e38b19..521ef79e0455 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_EspressoJVMCIRuntime.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_EspressoJVMCIRuntime.java
@@ -22,8 +22,8 @@
*/
package com.oracle.truffle.espresso.substitutions.jvmci;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.LOGGER;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.findObjectType;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.findObjectType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIInstanceType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIObjectType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIPrimitiveType;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool.java
index 3f09922b0244..6e0287e94a3b 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool.java
@@ -22,27 +22,9 @@
*/
package com.oracle.truffle.espresso.substitutions.jvmci;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.ANEWARRAY;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.CHECKCAST;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.GETFIELD;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.GETSTATIC;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INSTANCEOF;
import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEDYNAMIC;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEINTERFACE;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKESPECIAL;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKESTATIC;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.INVOKEVIRTUAL;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC2_W;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.LDC_W;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.MULTIANEWARRAY;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.NEW;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.PUTFIELD;
-import static com.oracle.truffle.espresso.classfile.bytecode.Bytecodes.PUTSTATIC;
-import static com.oracle.truffle.espresso.jvmci.JVMCIIndyData.indyCpi;
-import static com.oracle.truffle.espresso.jvmci.JVMCIIndyData.isIndyCPI;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.LOGGER;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.findObjectType;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIConstantPoolUtils.safeTagAt;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantReflectionProvider.wrapEspressoObjectConstant;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIInstanceType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIObjectType;
@@ -53,38 +35,22 @@
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.espresso.classfile.ConstantPool;
import com.oracle.truffle.espresso.classfile.JavaKind;
-import com.oracle.truffle.espresso.classfile.ParserKlass;
-import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute;
-import com.oracle.truffle.espresso.classfile.bytecode.Bytecodes;
+import com.oracle.truffle.espresso.classfile.descriptors.ByteSequence;
import com.oracle.truffle.espresso.classfile.descriptors.Name;
-import com.oracle.truffle.espresso.classfile.descriptors.Signature;
-import com.oracle.truffle.espresso.classfile.descriptors.SignatureSymbols;
import com.oracle.truffle.espresso.classfile.descriptors.Symbol;
-import com.oracle.truffle.espresso.classfile.descriptors.Type;
import com.oracle.truffle.espresso.classfile.descriptors.TypeSymbols;
-import com.oracle.truffle.espresso.constantpool.CallSiteLink;
import com.oracle.truffle.espresso.constantpool.ResolvedConstant;
-import com.oracle.truffle.espresso.constantpool.ResolvedDynamicConstant;
-import com.oracle.truffle.espresso.constantpool.ResolvedInvokeDynamicConstant;
-import com.oracle.truffle.espresso.constantpool.ResolvedWithInvokerClassMethodRefConstant;
import com.oracle.truffle.espresso.constantpool.RuntimeConstantPool;
-import com.oracle.truffle.espresso.constantpool.SuccessfulCallSiteLink;
import com.oracle.truffle.espresso.impl.Field;
import com.oracle.truffle.espresso.impl.Klass;
import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
-import com.oracle.truffle.espresso.jvmci.JVMCIIndyData;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIConstantPoolUtils;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData;
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.Meta;
-import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
import com.oracle.truffle.espresso.runtime.EspressoContext;
-import com.oracle.truffle.espresso.runtime.EspressoException;
-import com.oracle.truffle.espresso.runtime.EspressoLinkResolver;
import com.oracle.truffle.espresso.runtime.staticobject.StaticObject;
-import com.oracle.truffle.espresso.shared.meta.SignaturePolymorphicIntrinsic;
-import com.oracle.truffle.espresso.shared.resolver.CallSiteType;
-import com.oracle.truffle.espresso.shared.resolver.FieldAccessType;
-import com.oracle.truffle.espresso.shared.resolver.ResolvedCall;
import com.oracle.truffle.espresso.substitutions.EspressoSubstitutions;
import com.oracle.truffle.espresso.substitutions.Inject;
import com.oracle.truffle.espresso.substitutions.JavaType;
@@ -96,13 +62,6 @@ final class Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool {
private Target_com_oracle_truffle_espresso_jvmci_meta_EspressoConstantPool() {
}
- private static ConstantPool.Tag safeTagAt(RuntimeConstantPool pool, int cpi, Meta meta) {
- if (cpi < 0 || pool.length() <= cpi) {
- throw meta.throwIndexOutOfBoundsExceptionBoundary("Invalid constant pool index", cpi, pool.length());
- }
- return pool.tagAt(cpi);
- }
-
@Substitution(hasReceiver = true)
public static int length(StaticObject self, @Inject EspressoContext context) {
assert context.getLanguage().isInternalJVMCIEnabled();
@@ -120,7 +79,6 @@ private static RuntimeConstantPool getRuntimeConstantPool(StaticObject self, Met
}
@Substitution(hasReceiver = true)
- @TruffleBoundary
public static @JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaField;") StaticObject lookupResolvedField(
StaticObject self, int cpi,
@JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;") StaticObject jvmciMethod,
@@ -130,67 +88,17 @@ private static RuntimeConstantPool getRuntimeConstantPool(StaticObject self, Met
if (StaticObject.isNull(jvmciMethod)) {
throw meta.throwNullPointerExceptionBoundary();
}
- if (!(opcode == GETSTATIC || opcode == PUTSTATIC || opcode == GETFIELD || opcode == PUTFIELD)) {
- throw meta.throwIllegalArgumentExceptionBoundary();
- }
Method method = (Method) meta.jvmci.HIDDEN_METHOD_MIRROR.getHiddenObject(jvmciMethod);
StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.FIELD_REF) {
- throw meta.throwException(meta.java_lang_ClassFormatError);
- }
- Klass symbolicHolder = lookupSymbolicHolder(cpi, constantPool, meta);
- if (symbolicHolder == null) {
- LOGGER.fine(() -> "ECP.lookupResolvedField cannot resolve symbolic holder: " + constantPool.memberClassName(cpi));
- return StaticObject.NULL;
- }
- Field resolved = lookupResolvedField(cpi, symbolicHolder, constantPool, method, opcode, meta);
+ Field resolved = JVMCIConstantPoolUtils.lookupResolvedField(constantPool, cpi, method, opcode, context);
if (resolved == null) {
return StaticObject.NULL;
}
- LOGGER.finer(() -> "ECP.lookupResolvedField found " + resolved);
return toJVMCIField(resolved, cpHolder, cpHolderKlass, meta);
}
- private static Field lookupResolvedField(int fieldIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Method method, int opcode, Meta meta) {
- Field symbolicResolution;
- ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(fieldIndex, meta);
- if (resolvedConstant != null) {
- symbolicResolution = (Field) resolvedConstant.value();
- } else {
- symbolicResolution = tryResolveField(fieldIndex, symbolicHolder, constantPool, meta);
- if (symbolicResolution == null) {
- LOGGER.fine(() -> "ECP.lookupResolvedField failed symbolic lookup for " + symbolicHolder + ", " + constantPool.fieldName(fieldIndex) + ", " +
- constantPool.fieldType(fieldIndex));
- return null;
- }
- }
- // Note that constantPool.getHolder() may be different from method.getDeclaringKlass()
- // in particular this is true in native image where the method might be a JDK method
- // (e.g.,// Ljava/lang/ClassValue;.()V)
- // and the constant pool might be the one of its substitution
- // (e.g., com/oracle/svm/core/jdk/Target_java_lang_ClassValue)
- if (!EspressoLinkResolver.checkFieldAccess(meta.getContext(), symbolicResolution, FieldAccessType.fromOpCode(opcode), constantPool.getHolder(), method)) {
- LOGGER.fine(() -> {
- if (constantPool.getHolder() == method.getDeclaringKlass()) {
- return "ECP.lookupResolvedField failed access checks for " + symbolicResolution + " from " + method + " with " + Bytecodes.nameOf(opcode);
- } else {
- return "ECP.lookupResolvedField failed access checks for " + symbolicResolution + " from " + method + " (currentKlass=" + constantPool.getHolder() + ") with " +
- Bytecodes.nameOf(opcode);
- }
- });
- return null;
- }
- return symbolicResolution;
- }
-
- private static Field tryResolveField(int fieldIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Meta meta) {
- Symbol name = constantPool.fieldName(fieldIndex);
- Symbol type = constantPool.fieldType(fieldIndex);
- return EspressoLinkResolver.resolveFieldSymbolOrNull(meta.getContext(), constantPool.getHolder(), name, type, symbolicHolder, true, true);
- }
-
@Substitution(hasReceiver = true)
public static @JavaType(String.class) StaticObject lookupUtf8(StaticObject self, int cpi, @Inject EspressoContext context) {
assert context.getLanguage().isInternalJVMCIEnabled();
@@ -228,7 +136,6 @@ private static StaticObject resolvedConstantToJVMCIObjectType(ResolvedConstant r
}
@Substitution(hasReceiver = true)
- @TruffleBoundary
public static @JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;") StaticObject lookupResolvedMethod(StaticObject self, int cpi, int opcode,
@JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;") StaticObject callerMirror,
@Inject EspressoContext context) {
@@ -236,77 +143,16 @@ private static StaticObject resolvedConstantToJVMCIObjectType(ResolvedConstant r
Meta meta = context.getMeta();
StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
+ Method caller = null;
if (!StaticObject.isNull(callerMirror)) {
- Method caller = (Method) meta.jvmci.HIDDEN_METHOD_MIRROR.getHiddenObject(callerMirror);
- if (caller.getDeclaringKlass() != cpHolderKlass) {
- LOGGER.finer(() -> "ECP.lookupResolvedMethod caller declaring class (" + caller.getDeclaringKlass() + ") doesn't match constant pool holder (" + cpHolderKlass + ")");
- }
+ caller = (Method) meta.jvmci.HIDDEN_METHOD_MIRROR.getHiddenObject(callerMirror);
}
RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- if (opcode == INVOKEDYNAMIC) {
- LOGGER.finer(() -> "ECP.lookupResolvedMethod resolving indy in CP of %s at cpi=0x%08x".formatted(cpHolderKlass, cpi));
- CallSiteLink callSiteLink = getCallSiteLink(self, cpi, meta);
- if (!(callSiteLink instanceof SuccessfulCallSiteLink successfulCallSiteLink)) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod no call site link or failed link in CP of %s at cpi=0x%08x".formatted(cpHolderKlass, cpi));
- return StaticObject.NULL;
- }
- Method target = (Method) meta.HIDDEN_VMTARGET.getHiddenObject(successfulCallSiteLink.getMemberName());
- StaticObject holder = toJVMCIInstanceType(target.getDeclaringKlass(), meta);
- return toJVMCIMethod(target, holder, meta);
- }
- if (!Bytecodes.isInvoke(opcode) || !(safeTagAt(constantPool, cpi, meta).isMethod())) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod opcode=" + Bytecodes.nameOf(opcode) + " poolConstant=" + constantPool.toString(cpi));
- throw meta.throwIllegalArgumentExceptionBoundary("Not an invoke or method ref");
- }
- Klass symbolicHolder = lookupSymbolicHolder(cpi, constantPool, meta);
- if (symbolicHolder == null) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod couldn't find symbolic holder klass " + constantPool.memberClassName(cpi));
- return StaticObject.NULL;
- }
- Method symbolicResolution;
- ResolvedConstant resolvedConstantOrNull = constantPool.peekResolvedOrNull(cpi, meta);
- if (resolvedConstantOrNull != null) {
- symbolicResolution = (Method) resolvedConstantOrNull.value();
- } else {
- symbolicResolution = tryResolveMethod(cpi, symbolicHolder, constantPool, meta);
- if (symbolicResolution == null) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod lookup method failed symbolic lookup for " + symbolicHolder + ", " + constantPool.toString(cpi));
- return StaticObject.NULL;
- }
- }
- ResolvedCall resolvedCall = EspressoLinkResolver.resolveCallSiteOrNull(context, cpHolderKlass, symbolicResolution, CallSiteType.fromOpCode(opcode), symbolicHolder);
- if (resolvedCall == null) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod failed call site resolution for " + symbolicResolution + " from " + cpHolderKlass + " with " + Bytecodes.nameOf(opcode));
+ Method result = JVMCIConstantPoolUtils.lookupResolvedMethod(constantPool, cpi, opcode, caller, context);
+ if (result == null) {
return StaticObject.NULL;
}
- Method method;
- if (resolvedConstantOrNull instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker) {
- MHInvokeGenericNode.MethodHandleInvoker invoker = withInvoker.invoker();
- method = invoker.method();
- } else {
- method = resolvedCall.getResolvedMethod();
- }
- // we don't return the invoker for unresolved InvokeGeneric cases;
- // this seems to be in line with HotSpot
- if (method.isInvokeIntrinsic()) {
- LOGGER.fine(() -> "ECP.lookupResolvedMethod lookup method found InvokeGeneric that was not resolved yet: " + method);
- }
- LOGGER.finer(() -> "ECP.lookupResolvedMethod found " + symbolicResolution);
- return toJVMCIMethod(method, cpHolder, cpHolderKlass, meta);
- }
-
- private static Klass lookupSymbolicHolder(int cpi, RuntimeConstantPool constantPool, Meta meta) {
- int holderClassIndex = constantPool.memberClassIndex(cpi);
- return findObjectType(holderClassIndex, constantPool, false, true, meta);
- }
-
- private static Method tryResolveMethod(int methodIndex, Klass symbolicHolder, RuntimeConstantPool constantPool, Meta meta) {
- Symbol name = constantPool.methodName(methodIndex);
- Symbol signature = constantPool.methodSignature(methodIndex);
- ConstantPool.Tag tag = safeTagAt(constantPool, methodIndex, meta);
- return EspressoLinkResolver.resolveMethodSymbolOrNull(meta.getContext(), constantPool.getHolder(), name, signature, symbolicHolder,
- tag == ConstantPool.Tag.INTERFACE_METHOD_REF, true,
- true);
+ return toJVMCIMethod(result, cpHolder, cpHolderKlass, meta);
}
@Substitution(hasReceiver = true)
@@ -315,18 +161,7 @@ private static Method tryResolveMethod(int methodIndex, Klass symbolicHolder, Ru
assert context.getLanguage().isInternalJVMCIEnabled();
Meta meta = context.getMeta();
RuntimeConstantPool constantPool = getRuntimeConstantPool(self, meta);
- int index = isIndyCPI(cpi) ? indyCpi(cpi) : cpi;
- ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
- if (tag.isMember()) {
- LOGGER.finer(() -> "ECP.lookupName found " + constantPool.memberName(index));
- return meta.toGuestString(constantPool.memberName(index));
- }
- if (tag == ConstantPool.Tag.INVOKEDYNAMIC) {
- LOGGER.finer(() -> "ECP.lookupName found " + constantPool.invokeDynamicName(index));
- return meta.toGuestString(constantPool.invokeDynamicName(index));
- }
- LOGGER.warning(() -> "Unsupported CP entry type for lookupName: " + tag + " " + constantPool.toString(index));
- throw meta.throwIllegalArgumentExceptionBoundary();
+ return meta.toGuestString(JVMCIConstantPoolUtils.lookupName(constantPool, cpi, context));
}
@Substitution(hasReceiver = true)
@@ -335,20 +170,7 @@ private static Method tryResolveMethod(int methodIndex, Klass symbolicHolder, Ru
assert context.getLanguage().isInternalJVMCIEnabled();
Meta meta = context.getMeta();
RuntimeConstantPool constantPool = getRuntimeConstantPool(self, meta);
- int index = isIndyCPI(cpi) ? indyCpi(cpi) : cpi;
- ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
-
- if (tag.isMember()) {
- LOGGER.finer(() -> "ECP.lookupDescriptor found " + constantPool.memberDescriptor(index));
- return meta.toGuestString(constantPool.memberDescriptor(index));
- }
- if (tag == ConstantPool.Tag.INVOKEDYNAMIC) {
- Symbol indySignature = constantPool.invokeDynamicSignature(index);
- LOGGER.finer(() -> "ECP.lookupDescriptor found " + indySignature);
- return meta.toGuestString(indySignature);
- }
- LOGGER.warning(() -> "Unsupported CP entry type for lookupDescriptor: " + tag);
- throw meta.throwIllegalArgumentExceptionBoundary();
+ return meta.toGuestString(JVMCIConstantPoolUtils.lookupDescriptor(constantPool, cpi, context));
}
@Substitution(hasReceiver = true)
@@ -360,55 +182,17 @@ private static Method tryResolveMethod(int methodIndex, Klass symbolicHolder, Ru
StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- int classCpi;
- switch (opcode) {
- case CHECKCAST:
- case INSTANCEOF:
- case NEW:
- case ANEWARRAY:
- case MULTIANEWARRAY:
- case LDC:
- case LDC_W:
- case LDC2_W:
- if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.CLASS) {
- throw meta.throwIllegalArgumentExceptionBoundary("Opcode and constant pool entry types mismatch");
- }
- classCpi = cpi;
- break;
- case GETSTATIC:
- case PUTSTATIC:
- case GETFIELD:
- case PUTFIELD:
- case INVOKEVIRTUAL:
- case INVOKESPECIAL:
- case INVOKESTATIC:
- case INVOKEINTERFACE:
- if (!safeTagAt(constantPool, cpi, meta).isMember()) {
- throw meta.throwIllegalArgumentExceptionBoundary("Opcode and constant pool entry types mismatch");
- }
- classCpi = constantPool.memberClassIndex(cpi);
- break;
- default:
- LOGGER.warning(() -> "Unsupported CP entry type for lookupReferencedType: " + safeTagAt(constantPool, cpi, meta) + " " + constantPool.toString(cpi) + " for " +
- Bytecodes.nameOf(opcode));
- throw meta.throwIllegalArgumentExceptionBoundary("Unsupported CP entry type");
- }
- Klass klass;
- try {
- klass = findObjectType(classCpi, constantPool, false, true, meta);
- } catch (EspressoException e) {
- throw EspressoError.shouldNotReachHere("findObjectType with resolve=false should never throw", e);
- }
- if (klass == null) {
- Symbol className = constantPool.className(classCpi);
- return toJVMCIUnresolvedType(TypeSymbols.nameToType(className), meta);
+
+ Object result = JVMCIConstantPoolUtils.lookupReferencedType(constantPool, cpi, opcode, context);
+ if (result instanceof Klass klass) {
+ return toJVMCIObjectType(klass, meta);
+ } else {
+ ByteSequence type = (ByteSequence) result;
+ return toJVMCIUnresolvedType(type, meta);
}
- LOGGER.finer(() -> "ECP.lookupReferencedType found " + klass);
- return toJVMCIObjectType(klass, meta);
}
@Substitution(hasReceiver = true)
- @TruffleBoundary
public static boolean loadReferencedType0(StaticObject self, int cpi, int opcode,
@Inject EspressoContext context) {
assert context.getLanguage().isInternalJVMCIEnabled();
@@ -416,66 +200,7 @@ public static boolean loadReferencedType0(StaticObject self, int cpi, int opcode
StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- switch (opcode) {
- case CHECKCAST:
- case INSTANCEOF:
- case NEW:
- case ANEWARRAY:
- case MULTIANEWARRAY: {
- Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, cpi);
- LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
- return true;
- }
- case LDC:
- case LDC_W:
- case LDC2_W: {
- if (safeTagAt(constantPool, cpi, meta) == ConstantPool.Tag.CLASS) {
- Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, cpi);
- LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
- return true;
- }
- return false;
- }
- case INVOKEDYNAMIC: {
- // resolve this indy and call boostrap method
- assert isIndyCPI(cpi);
- JVMCIIndyData indyData = JVMCIIndyData.getExisting(cpHolderKlass, meta);
- LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") Looking up CallSiteLink for index=" + cpi + " in " + cpHolderKlass);
- JVMCIIndyData.Location location = indyData.getLocation(cpi);
- assert location != null;
- int indyCpi = indyCpi(cpi);
- if (!(safeTagAt(constantPool, indyCpi, meta) == ConstantPool.Tag.INVOKEDYNAMIC)) {
- throw meta.throwIllegalArgumentExceptionBoundary();
- }
- constantPool.linkInvokeDynamic(cpHolderKlass, indyCpi, location.method(), location.bci());
- return false;
- }
- case GETSTATIC:
- case PUTSTATIC:
- case GETFIELD:
- case PUTFIELD:
- case INVOKEVIRTUAL:
- case INVOKESPECIAL:
- case INVOKESTATIC:
- case INVOKEINTERFACE: {
- Klass klass = constantPool.resolvedKlassAt(cpHolderKlass, constantPool.memberClassIndex(cpi));
- LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") found " + klass);
- if ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && ParserKlass.isSignaturePolymorphicHolderType(klass.getType())) {
- ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(cpi, meta);
- if (resolvedConstant == null) {
- Symbol methodName = constantPool.memberName(cpi);
- if (SignaturePolymorphicIntrinsic.getId(methodName, klass) != null) {
- // trigger resolution for method handle intrinsics
- Method method = constantPool.resolvedMethodAt(cpHolderKlass, cpi);
- LOGGER.finer(() -> "ECP.loadReferencedType0(" + Bytecodes.nameOf(opcode) + ") resolved MH intrinsic to " + method);
- }
- }
- }
- return true;
- }
- default:
- return false;
- }
+ return JVMCIConstantPoolUtils.loadReferencedType0(cpi, opcode, constantPool, cpHolderKlass, meta);
}
@Substitution(hasReceiver = true)
@@ -565,56 +290,15 @@ public static boolean loadReferencedType0(StaticObject self, int cpi, int opcode
public static @JavaType(internalName = "Ljdk/vm/ci/meta/JavaConstant;") StaticObject lookupAppendix(StaticObject self, int index, int opcode, @Inject EspressoContext context) {
assert context.getLanguage().isInternalJVMCIEnabled();
Meta meta = context.getMeta();
- if (opcode != INVOKEDYNAMIC && opcode != INVOKEVIRTUAL) {
- throw meta.throwIllegalArgumentExceptionBoundary("Expected INVOKEDYNAMIC or INVOKEVIRTUAL");
- }
- if (opcode == INVOKEDYNAMIC) {
- LOGGER.finer(() -> "ECP.lookupAppendix: Looking up CallSiteLink for index=" + Integer.toHexString(index) + " in " +
- meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(meta.jvmci.EspressoConstantPool_holder.getObject(self)));
- CallSiteLink callSiteLink = getCallSiteLink(self, index, meta);
- if (!(callSiteLink instanceof SuccessfulCallSiteLink successfulCallSiteLink)) {
- return StaticObject.NULL;
- }
- return wrapEspressoObjectConstant(successfulCallSiteLink.getUnboxedAppendix(), meta);
- } else {
- assert opcode == INVOKEVIRTUAL;
- StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
- ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
- RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
-
- ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(index, meta);
- if (safeTagAt(constantPool, index, meta) != ConstantPool.Tag.METHOD_REF) {
- throw meta.throwIllegalArgumentExceptionBoundary("The index does not reference a MethodRef");
- }
- if (!(resolvedConstant instanceof ResolvedWithInvokerClassMethodRefConstant withInvoker)) {
- return StaticObject.NULL;
- }
- MHInvokeGenericNode.MethodHandleInvoker invoker = withInvoker.invoker();
- assert invoker != null;
- return wrapEspressoObjectConstant(invoker.appendix(), meta);
- }
- }
-
- private static CallSiteLink getCallSiteLink(StaticObject self, int index, Meta meta) {
- StaticObject cpHolder = meta.jvmci.EspressoConstantPool_holder.getObject(self);
- ObjectKlass cpHolderKlass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(cpHolder);
- JVMCIIndyData indyData = JVMCIIndyData.getExisting(cpHolderKlass, meta);
- JVMCIIndyData.Location location = indyData.getLocation(index);
- assert isIndyCPI(index);
- int cpi = indyCpi(index);
- RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- ResolvedConstant resolvedConstantOrNull = constantPool.peekResolvedOrNull(cpi, meta);
- if (safeTagAt(constantPool, cpi, meta) != ConstantPool.Tag.INVOKEDYNAMIC) {
- throw meta.throwIllegalArgumentExceptionBoundary();
- }
- if (!(resolvedConstantOrNull instanceof ResolvedInvokeDynamicConstant resolvedIndy)) {
- return null;
+ RuntimeConstantPool constantPool = getRuntimeConstantPool(self, meta);
+ StaticObject appendix = JVMCIConstantPoolUtils.lookupAppendix(constantPool, index, opcode, context);
+ if (StaticObject.isNull(appendix)) {
+ return StaticObject.NULL;
}
- return resolvedIndy.getCallSiteLink(location.method(), location.bci());
+ return wrapEspressoObjectConstant(appendix, meta);
}
@Substitution(hasReceiver = true)
- @TruffleBoundary
public static @JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation;") StaticObject lookupIndyBootstrapMethodInvocation(StaticObject self,
int siteIndex,
@Inject EspressoContext context) {
@@ -628,7 +312,6 @@ private static CallSiteLink getCallSiteLink(StaticObject self, int index, Meta m
}
@Substitution(hasReceiver = true)
- @TruffleBoundary
public static @JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoBootstrapMethodInvocation;") StaticObject lookupBootstrapMethodInvocation(StaticObject self, int cpi,
int opcode,
@Inject EspressoContext context) {
@@ -639,88 +322,6 @@ private static CallSiteLink getCallSiteLink(StaticObject self, int index, Meta m
return lookupBootstrapMethodInvocation(self, cpi, opcode, cpHolderKlass, cpHolder, context);
}
- private static StaticObject lookupBootstrapMethodInvocation(StaticObject self, int cpi, int opcode, ObjectKlass cpHolderKlass, StaticObject cpHolder, EspressoContext context) {
- Meta meta = context.getMeta();
- RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
- int index;
- if (opcode == -1) {
- assert !isIndyCPI(cpi);
- index = cpi;
- } else if (opcode == INVOKEDYNAMIC) {
- assert isIndyCPI(cpi);
- index = indyCpi(cpi);
- } else {
- throw meta.throwIllegalArgumentExceptionBoundary("Unexpected opcode: " + opcode);
- }
- ConstantPool.Tag tag = safeTagAt(constantPool, index, meta);
- if (tag == ConstantPool.Tag.DYNAMIC || tag == ConstantPool.Tag.INVOKEDYNAMIC) {
- BootstrapMethodsAttribute bms = cpHolderKlass.getAttribute(BootstrapMethodsAttribute.NAME, BootstrapMethodsAttribute.class);
- int bsmAttrIndex = constantPool.bsmBootstrapMethodAttrIndex(index);
- BootstrapMethodsAttribute.Entry bsmEntry = bms.at(bsmAttrIndex);
- StaticObject methodHandle = constantPool.getMethodHandle(bsmEntry, cpHolderKlass);
- methodHandle = (StaticObject) meta.java_lang_invoke_MethodHandle_asFixedArity.invokeDirectVirtual(methodHandle);
- assert meta.java_lang_invoke_DirectMethodHandle.isAssignableFrom(methodHandle.getKlass());
- StaticObject member = meta.java_lang_invoke_DirectMethodHandle_member.getObject(methodHandle);
-
- boolean isIndy = tag == ConstantPool.Tag.INVOKEDYNAMIC;
- Method bootstrapMethod = (Method) meta.HIDDEN_VMTARGET.getHiddenObject(member);
- Symbol name = constantPool.bsmName(index);
- StaticObject type;
- if (isIndy) {
- Symbol invokeSignature = SignatureSymbols.fromDescriptor(constantPool.invokeDynamicSignature(index));
- Symbol[] parsedInvokeSignature = meta.getSignatures().parsed(invokeSignature);
- type = RuntimeConstantPool.signatureToMethodType(parsedInvokeSignature, cpHolderKlass, meta.getContext().getJavaVersion().java8OrEarlier(), meta);
- } else {
- Symbol typeSymbol = TypeSymbols.fromSymbol(constantPool.dynamicType(index));
- Klass klass = meta.resolveSymbolOrFail(typeSymbol, cpHolderKlass.getDefiningClassLoader(), cpHolderKlass.protectionDomain());
- type = klass.mirror();
- }
- StaticObject wrappedArgs = meta.jvmci.JavaConstant.allocateReferenceArray(bsmEntry.numBootstrapArguments());
- StaticObject[] unwrappedArgs = wrappedArgs.unwrap(meta.getLanguage());
- for (int i = 0; i < bsmEntry.numBootstrapArguments(); i++) {
- char entryCPI = bsmEntry.argAt(i);
- ConstantPool.Tag entryTag = safeTagAt(constantPool, entryCPI, meta);
- if (entryTag == ConstantPool.Tag.DYNAMIC) {
- ResolvedConstant resolvedConstant = constantPool.peekResolvedOrNull(entryCPI, meta);
- if (resolvedConstant instanceof ResolvedDynamicConstant resolvedDynamicConstant) {
- unwrappedArgs[i] = resolvedDynamicConstant.guestBoxedValue(meta);
- } else {
- unwrappedArgs[i] = meta.jvmci.boxInt(entryCPI);
- }
- } else {
- StaticObject obj = switch (entryTag) {
- case METHODHANDLE -> constantPool.resolvedMethodHandleAt(cpHolderKlass, entryCPI);
- case METHODTYPE -> constantPool.resolvedMethodTypeAt(cpHolderKlass, entryCPI);
- case CLASS -> constantPool.resolvedKlassAt(cpHolderKlass, entryCPI).mirror();
- case STRING -> constantPool.resolvedStringAt(entryCPI);
- case INTEGER -> meta.boxInteger(constantPool.intAt(entryCPI));
- case LONG -> meta.boxLong(constantPool.longAt(entryCPI));
- case DOUBLE -> meta.boxDouble(constantPool.doubleAt(entryCPI));
- case FLOAT -> meta.boxFloat(constantPool.floatAt(entryCPI));
- default -> throw EspressoError.shouldNotReachHere(entryTag.toString());
- };
- unwrappedArgs[i] = wrapEspressoObjectConstant(obj, meta);
- }
- }
-
- StaticObject methodHolderMirror;
- if (bootstrapMethod.getDeclaringKlass() == cpHolderKlass) {
- methodHolderMirror = cpHolder;
- } else {
- methodHolderMirror = toJVMCIInstanceType(bootstrapMethod.getDeclaringKlass(), meta);
- }
- StaticObject methodMirror = toJVMCIMethod(bootstrapMethod, methodHolderMirror, meta);
- StaticObject wrappedType = wrapEspressoObjectConstant(type, meta);
-
- StaticObject result = meta.jvmci.EspressoBootstrapMethodInvocation.allocateInstance(context);
- LOGGER.finer(() -> "ECP.lookupBootstrapMethodInvocation: returning EspressoBootstrapMethodInvocation isIndy: " + isIndy + " method: " + bootstrapMethod + " name: " + name + " type: " +
- type + " cpi:" + cpi);
- meta.jvmci.EspressoBootstrapMethodInvocation_init.invokeDirectSpecial(result, isIndy, methodMirror, meta.toGuestString(name), wrappedType, wrappedArgs, cpi, self);
- return result;
- }
- return StaticObject.NULL;
- }
-
@Substitution(hasReceiver = true)
public static int getNumIndyEntries(StaticObject self, @Inject EspressoContext context) {
assert context.getLanguage().isInternalJVMCIEnabled();
@@ -741,4 +342,75 @@ public static byte getTagByteAt(StaticObject self, int cpi, @Inject EspressoCont
RuntimeConstantPool runtimeConstantPool = getRuntimeConstantPool(self, meta);
return safeTagAt(runtimeConstantPool, cpi, meta).getValue();
}
+
+ private static final class InternalBootstrapMethodInvocationBuilder implements JVMCIConstantPoolUtils.BootstrapMethodInvocationBuilder {
+ private final Meta meta;
+ private StaticObject[] unwrappedArgs;
+ StaticObject wrappedArgs;
+ boolean isIndy;
+ Method bootstrapMethod;
+ Symbol name;
+ StaticObject type;
+
+ private InternalBootstrapMethodInvocationBuilder(Meta meta) {
+ this.meta = meta;
+ }
+
+ @Override
+ public void setupStaticArguments(int length) {
+ assert wrappedArgs == null;
+ assert unwrappedArgs == null;
+ wrappedArgs = meta.jvmci.JavaConstant.allocateReferenceArray(length);
+ unwrappedArgs = wrappedArgs.unwrap(meta.getLanguage());
+ }
+
+ @Override
+ public void staticArgument(int i, StaticObject value) {
+ assert StaticObject.isNull(unwrappedArgs[i]);
+ unwrappedArgs[i] = wrapEspressoObjectConstant(value, meta);
+ }
+
+ @Override
+ public void staticArgumentUnresolvedDynamic(int i, int cpi) {
+ assert StaticObject.isNull(unwrappedArgs[i]);
+ unwrappedArgs[i] = meta.jvmci.boxInt(cpi);
+ }
+
+ @Override
+ public void finalize(boolean finalIsIndy, Method finalBootstrapMethod, Symbol finalName, StaticObject finalType) {
+ assert !this.isIndy;
+ assert this.bootstrapMethod == null;
+ assert this.name == null;
+ assert this.type == null;
+ this.isIndy = finalIsIndy;
+ this.bootstrapMethod = finalBootstrapMethod;
+ this.name = finalName;
+ this.type = finalType;
+ }
+ }
+
+ private static StaticObject lookupBootstrapMethodInvocation(StaticObject self, int cpi, int opcode, ObjectKlass cpHolderKlass, StaticObject cpHolder, EspressoContext context) {
+ Meta meta = context.getMeta();
+ RuntimeConstantPool constantPool = cpHolderKlass.getConstantPool();
+ InternalBootstrapMethodInvocationBuilder builder = new InternalBootstrapMethodInvocationBuilder(meta);
+ JVMCIConstantPoolUtils.lookupBootstrapMethodInvocation(constantPool, cpi, opcode, context, builder);
+ if (builder.bootstrapMethod == null) {
+ return StaticObject.NULL;
+ }
+ StaticObject methodHolderMirror;
+ if (builder.bootstrapMethod.getDeclaringKlass() == cpHolderKlass) {
+ methodHolderMirror = cpHolder;
+ } else {
+ methodHolderMirror = toJVMCIInstanceType(builder.bootstrapMethod.getDeclaringKlass(), meta);
+ }
+ StaticObject methodMirror = toJVMCIMethod(builder.bootstrapMethod, methodHolderMirror, meta);
+ StaticObject wrappedType = wrapEspressoObjectConstant(builder.type, meta);
+
+ StaticObject result = meta.jvmci.EspressoBootstrapMethodInvocation.allocateInstance(context);
+ LOGGER.finer(() -> "ECP.lookupBootstrapMethodInvocation: returning EspressoBootstrapMethodInvocation isIndy: " + builder.isIndy + " method: " + builder.bootstrapMethod + " name: " +
+ builder.name + " type: " +
+ builder.type + " cpi:" + cpi);
+ meta.jvmci.EspressoBootstrapMethodInvocation_init.invokeDirectSpecial(result, builder.isIndy, methodMirror, meta.toGuestString(builder.name), wrappedType, builder.wrappedArgs, cpi, self);
+ return result;
+ }
}
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMethodHandleAccessProvider.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMethodHandleAccessProvider.java
index ec93097e02f4..126ce67dc6ac 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMethodHandleAccessProvider.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMethodHandleAccessProvider.java
@@ -22,7 +22,7 @@
*/
package com.oracle.truffle.espresso.substitutions.jvmci;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.LOGGER;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIInstanceType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.toJVMCIMethod;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.java
index 1e86a02f4ed7..a971f7e6c69e 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.java
@@ -39,6 +39,7 @@
import com.oracle.truffle.espresso.impl.Field;
import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.meta.Meta.JVMCISupport;
import com.oracle.truffle.espresso.nodes.bytecodes.InitCheck;
@@ -493,49 +494,20 @@ public static int getVtableLength(StaticObject self, @Inject EspressoContext con
}
@Substitution(hasReceiver = true)
- abstract static class GetAllMethods0 extends SubstitutionNode {
- abstract @JavaType(internalName = "[Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;") StaticObject execute(StaticObject self);
-
- @Specialization
- static StaticObject doDefault(StaticObject self,
- @Bind("getContext()") EspressoContext context,
- @Cached("create(context.getMeta().jvmci.EspressoResolvedJavaMethod_init.getCallTarget())") DirectCallNode methodConstructor) {
- assert context.getLanguage().isInternalJVMCIEnabled();
- Meta meta = context.getMeta();
-
- ObjectKlass klass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(self);
-
- Method.MethodVersion[] declaredMethodVersions = klass.getDeclaredMethodVersions();
- Method.MethodVersion[] mirandaMethods = klass.getMirandaMethods();
- int resultSize = declaredMethodVersions.length;
- if (mirandaMethods != null) {
- for (Method.MethodVersion mirandaMethod : mirandaMethods) {
- if (mirandaMethod.getMethod().hasPoisonPill()) {
- resultSize++;
- }
- }
- }
- StaticObject result = meta.jvmci.EspressoResolvedJavaMethod.allocateReferenceArray(resultSize);
- StaticObject[] underlying = result.unwrap(context.getLanguage());
- int i = 0;
- for (Method.MethodVersion methodVersion : declaredMethodVersions) {
- underlying[i++] = toJVMCIMethod(methodVersion.getMethod(), self, methodConstructor, context, meta);
- }
- if (resultSize != declaredMethodVersions.length) {
- for (Method.MethodVersion mirandaMethod : mirandaMethods) {
- if (mirandaMethod.getMethod().hasPoisonPill()) {
- StaticObject holder;
- if (mirandaMethod.getDeclaringKlass() == klass) {
- holder = self;
- } else {
- holder = toJVMCIInstanceType(mirandaMethod.getDeclaringKlass(), meta);
- }
- underlying[i++] = toJVMCIMethod(mirandaMethod.getMethod(), holder, methodConstructor, context, meta);
- }
- }
+ public static @JavaType(internalName = "[Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedJavaMethod;") StaticObject getAllMethods0(StaticObject self, @Inject EspressoContext context) {
+ assert context.getLanguage().isInternalJVMCIEnabled();
+ Meta meta = context.getMeta();
+ ObjectKlass klass = (ObjectKlass) meta.jvmci.HIDDEN_OBJECTKLASS_MIRROR.getHiddenObject(self);
+ return JVMCIUtils.getAllMethods(klass, meta.jvmci.EspressoResolvedJavaMethod::allocateReferenceArray, (array, i, m) -> {
+ StaticObject holder;
+ if (m.getDeclaringKlass() == klass) {
+ holder = self;
+ } else {
+ holder = toJVMCIInstanceType(m.getDeclaringKlass(), meta);
}
- return result;
- }
+ StaticObject[] underlying = array.unwrap(context.getLanguage());
+ underlying[i] = toJVMCIMethod(m, holder, meta);
+ });
}
@Substitution(hasReceiver = true)
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField.java
index f54a714f3a43..ae70970c41e8 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaField.java
@@ -22,8 +22,8 @@
*/
package com.oracle.truffle.espresso.substitutions.jvmci;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.LOGGER;
-import static com.oracle.truffle.espresso.jvmci.JVMCIUtils.findType;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.LOGGER;
+import static com.oracle.truffle.espresso.impl.jvmci.JVMCIUtils.findType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoMetaAccessProvider.toJVMCIType;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedInstanceType.getRawAnnotationBytes;
import static com.oracle.truffle.espresso.substitutions.jvmci.Target_jdk_vm_ci_runtime_JVMCI.checkJVMCIAvailable;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod.java
index 9e7970be616b..5ea1cb7e8155 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaMethod.java
@@ -52,7 +52,7 @@
import com.oracle.truffle.espresso.impl.Klass;
import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
-import com.oracle.truffle.espresso.jvmci.JVMCIIndyData;
+import com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData;
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.runtime.EspressoContext;
diff --git a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent.java
index bd5e9466462a..b5298fd8aa31 100644
--- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent.java
+++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/substitutions/jvmci/Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecordComponent.java
@@ -43,7 +43,7 @@ private Target_com_oracle_truffle_espresso_jvmci_meta_EspressoResolvedJavaRecord
}
@Substitution
- abstract static class GetRawAnnotationBytes extends SubstitutionNode {
+ abstract static class GetRawAnnotationBytes0 extends SubstitutionNode {
abstract @JavaType(byte[].class) StaticObject execute(@JavaType(internalName = "Lcom/oracle/truffle/espresso/jvmci/meta/EspressoResolvedInstanceType;") StaticObject holder,
int index, int category);
diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoader.java
index 8703f7801975..8e68f0cbdfaf 100644
--- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoader.java
+++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageClassLoader.java
@@ -159,8 +159,8 @@ CodeSource codeSource() {
localNameToModule = Collections.unmodifiableMap(nameToModule);
localPackageToModule = Collections.unmodifiableMap(packageToModule);
/*
- * Other than in {@code jdk.internal.loader.Loader} we initialize remotePackageToLoader here
- * which allows us to use an unmodifiable map instead of a ConcurrentHashMap.
+ * Unlike {@code jdk.internal.loader.Loader}, we initialize remotePackageToLoader here which
+ * allows us to use an unmodifiable map instead of a ConcurrentHashMap.
*/
remotePackageToLoader = initRemotePackageMap(configuration, List.of(ModuleLayer.boot()));
@@ -185,7 +185,7 @@ public static URL toURL(URI uri) {
}
/**
- * See {@code jdk.internal.loader.Loader#initRemotePackageMap}.
+ * See {@link jdk.internal.loader.Loader#initRemotePackageMap}.
*/
private Map initRemotePackageMap(Configuration cf, List parentModuleLayers) {
Map remotePackageMap = new HashMap<>();
@@ -220,16 +220,8 @@ private Map initRemotePackageMap(Configuration cf, List remotePackage(remotePackageMap, pn, l));
} else {
- String target = resolvedModule.name();
for (ModuleDescriptor.Exports e : descriptor.exports()) {
- boolean delegate;
- if (e.isQualified()) {
- delegate = (other.configuration() == cf) && e.targets().contains(target);
- } else {
- delegate = true;
- }
-
- if (delegate) {
+ if (!e.isQualified()) {
remotePackage(remotePackageMap, e.source(), loader);
}
}