From 0bb6ddc9d43a5a974bfdbd0c2b6919855b7d5998 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 11 Sep 2025 10:03:35 +0200 Subject: [PATCH 1/4] Minor warning cleanups --- .../src/com/oracle/truffle/espresso/ffi/Callback.java | 2 +- .../oracle/truffle/espresso/nodes/IntrinsicSubstitutorNode.java | 1 - .../truffle/espresso/nodes/IntrinsifiedNativeMethodNode.java | 1 - .../truffle/espresso/nodes/commands/AddPathToBindingsNode.java | 1 + .../com/oracle/truffle/espresso/nodes/quick/BaseQuickNode.java | 1 + .../espresso/runtime/dispatch/staticobject/MapInterop.java | 1 - 6 files changed, 3 insertions(+), 4 deletions(-) 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/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/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, From 399be808db45471a1df8407fb97b358028b7fcca Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 11 Sep 2025 10:19:00 +0200 Subject: [PATCH 2/4] Move VM-side jvmci package to impl package This avoids conflict with the guest JVMCI package --- .../espresso/{ => impl}/jvmci/JVMCIIndyData.java | 4 ++-- .../truffle/espresso/{ => impl}/jvmci/JVMCIUtils.java | 2 +- ...le_truffle_espresso_jvmci_EspressoJVMCIRuntime.java | 4 ++-- ...uffle_espresso_jvmci_meta_EspressoConstantPool.java | 10 +++++----- ..._jvmci_meta_EspressoMethodHandleAccessProvider.java | 2 +- ..._espresso_jvmci_meta_EspressoResolvedJavaField.java | 4 ++-- ...espresso_jvmci_meta_EspressoResolvedJavaMethod.java | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{ => impl}/jvmci/JVMCIIndyData.java (98%) rename espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/{ => impl}/jvmci/JVMCIUtils.java (98%) 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 98% 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..e9620f126a57 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,7 @@ * 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 com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLogger; 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..042267fe13f8 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 @@ -39,10 +39,10 @@ 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.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 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; @@ -73,7 +73,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.nodes.methodhandle.MHInvokeGenericNode; 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_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; From f509af8cdef61a1c09c15340a77623ff77a28975 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 13 Nov 2025 10:24:33 +0100 Subject: [PATCH 3/4] Simplify NativeImageClassLoader.initRemotePackageMap --- .../svm/hosted/NativeImageClassLoader.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) 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); } } From ef70972abd7a3305a51193d1ac10a8b1cd6514f4 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Mon, 15 Sep 2025 17:45:10 +0200 Subject: [PATCH 4/4] Introduce VMAccess API The `VMAccess` API can be used to retrieve JVMCI and Graal compiler providers that reflect the state of a JVM. `VMAccess.Builder` can be used to create such a JVM based on a class path, module path etc. This commit adds 2 implementations of `VMAccess.Builder` that can be looked up via service loaders: * A "host" VMAccess which reflects on the host JVM similar to how the native-image builder works today. * A "espresso-context" VMAccess which creates and reflects on an espresso context. The implementation of the espresso context vm access is partial and is done as a refactoring of Espresso's existing "internal" JVMCI implementation. * On the JVMCI side, the main espresso JVMCI implementation classes are turned into abstract classes that are extended by 2 sublasses: one for internal JVMCI which works as before via substitutions of native methods; and one for the new external JVMCI which is implemented via polyglot interop. * In espresso, some common code is moved from the existing substitution classes to some *Util classes. External JVMCI works over interop so some exisiting classes now implement interop and a number of new interop wrappers to util objects are introduced. --- compiler/mx.compiler/suite.py | 126 +++ ...com.oracle.graal.vmaccess.VMAccess$Builder | 1 + .../graal/hostvmaccess/HostVMAccess.java | 160 ++++ .../hostvmaccess/HostVMAccessBuilder.java | 162 ++++ .../hostvmaccess/HostVMAccessClassLoader.java | 754 ++++++++++++++++++ .../graal/vmaccess/InvocationException.java | 71 ++ .../oracle/graal/vmaccess/ModuleSupport.java | 75 ++ .../com/oracle/graal/vmaccess/VMAccess.java | 125 +++ .../mx.espresso-compiler-stub/suite.py | 110 ++- .../graal/EspressoConstantFieldProvider.java | 7 +- .../EspressoMetaAccessExtensionProvider.java | 13 +- ...com.oracle.graal.vmaccess.VMAccess$Builder | 1 + .../EspressoExternalConstantPool.java | 234 ++++++ ...ssoExternalConstantReflectionProvider.java | 223 ++++++ ...ressoExternalIdentityHashCodeProvider.java | 36 + .../EspressoExternalMetaAccessProvider.java | 224 ++++++ .../EspressoExternalObjectConstant.java | 120 +++ .../EspressoExternalResolvedArrayType.java | 80 ++ .../EspressoExternalResolvedInstanceType.java | 355 +++++++++ .../EspressoExternalResolvedJavaField.java | 91 +++ .../EspressoExternalResolvedJavaMethod.java | 355 +++++++++ ...EspressoExternalResolvedPrimitiveType.java | 54 ++ .../vmaccess/EspressoExternalSignature.java | 49 ++ ...essoExternalSnippetReflectionProvider.java | 73 ++ .../vmaccess/EspressoExternalVMAccess.java | 371 +++++++++ .../EspressoExternalVMAccessBuilder.java | 210 +++++ .../espresso/vmaccess/KlassConstant.java | 66 ++ .../attributes/LineNumberTableAttribute.java | 8 + espresso/mx.espresso/suite.py | 6 +- .../jvmci/DummyCodeCacheProvider.java | 47 ++ .../espresso/jvmci/EspressoJVMCIRuntime.java | 26 +- .../meta/AbstractEspressoConstantPool.java | 192 +++++ .../AbstractEspressoResolvedArrayType.java | 350 ++++++++ .../AbstractEspressoResolvedInstanceType.java | 545 +++++++++++++ .../AbstractEspressoResolvedJavaField.java | 155 ++++ .../AbstractEspressoResolvedJavaMethod.java | 321 ++++++++ ...ctEspressoResolvedJavaRecordComponent.java | 102 +++ ...AbstractEspressoResolvedPrimitiveType.java | 308 +++++++ .../jvmci/meta/AbstractEspressoSignature.java | 221 +++++ ...tantReflectionProviderWithStaticsBase.java | 30 + .../EspressoBootstrapMethodInvocation.java | 17 +- .../jvmci/meta/EspressoConstantPool.java | 194 +---- .../EspressoConstantReflectionProvider.java | 15 +- .../jvmci/meta/EspressoResolvedArrayType.java | 321 +------- .../meta/EspressoResolvedInstanceType.java | 525 ++---------- .../jvmci/meta/EspressoResolvedJavaField.java | 126 +-- .../meta/EspressoResolvedJavaMethod.java | 311 +------- .../EspressoResolvedJavaRecordComponent.java | 70 +- .../jvmci/meta/EspressoResolvedJavaType.java | 29 +- .../meta/EspressoResolvedObjectType.java | 10 +- .../meta/EspressoResolvedPrimitiveType.java | 286 +------ .../jvmci/meta/EspressoSignature.java | 190 +---- .../jvmci/meta/ExtendedModifiers.java | 16 +- .../espresso/jvmci/meta/KlassConstant.java | 2 +- .../truffle/espresso/EspressoBindings.java | 69 +- .../truffle/espresso/EspressoLanguage.java | 9 +- .../truffle/espresso/EspressoOptions.java | 6 + .../espresso/descriptors/EspressoSymbols.java | 10 +- .../oracle/truffle/espresso/impl/Field.java | 98 ++- .../oracle/truffle/espresso/impl/Method.java | 377 ++++++++- .../impl/jvmci/JVMCIConstantPoolUtils.java | 504 ++++++++++++ .../espresso/impl/jvmci/JVMCIUtils.java | 34 + .../ExceptionHandlerInteropWrapper.java | 119 +++ .../InteropBootstrapMethodInvocation.java | 186 +++++ .../external/InteropConstantPoolWrapper.java | 547 +++++++++++++ .../InteropLineNumberTableHelper.java | 62 ++ .../jvmci/external/JVMCIInteropHelper.java | 564 +++++++++++++ .../jvmci/external/LocalInteropWrapper.java | 119 +++ .../impl/jvmci/external/TypeWrapper.java | 127 +++ .../oracle/truffle/espresso/meta/Meta.java | 13 +- .../espresso/runtime/EspressoContext.java | 3 +- ...resso_jvmci_meta_EspressoConstantPool.java | 518 +++--------- ...mci_meta_EspressoResolvedInstanceType.java | 56 +- ...a_EspressoResolvedJavaRecordComponent.java | 2 +- 74 files changed, 9584 insertions(+), 2408 deletions(-) create mode 100644 compiler/src/com.oracle.graal.hostvmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder create mode 100644 compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccess.java create mode 100644 compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessBuilder.java create mode 100644 compiler/src/com.oracle.graal.hostvmaccess/src/com/oracle/graal/hostvmaccess/HostVMAccessClassLoader.java create mode 100644 compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/InvocationException.java create mode 100644 compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/ModuleSupport.java create mode 100644 compiler/src/com.oracle.graal.vmaccess/src/com/oracle/graal/vmaccess/VMAccess.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/META-INF/services/com.oracle.graal.vmaccess.VMAccess$Builder create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantPool.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalConstantReflectionProvider.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalIdentityHashCodeProvider.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalMetaAccessProvider.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalObjectConstant.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedArrayType.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedInstanceType.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaField.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedJavaMethod.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalResolvedPrimitiveType.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSignature.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalSnippetReflectionProvider.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccess.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/EspressoExternalVMAccessBuilder.java create mode 100644 espresso-compiler-stub/src/com.oracle.truffle.espresso.vmaccess/src/com/oracle/truffle/espresso/vmaccess/KlassConstant.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoConstantPool.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedArrayType.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedInstanceType.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaField.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaMethod.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedJavaRecordComponent.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoResolvedPrimitiveType.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/AbstractEspressoSignature.java create mode 100644 espresso/src/com.oracle.truffle.espresso.jvmci/src/com/oracle/truffle/espresso/jvmci/meta/ConstantReflectionProviderWithStaticsBase.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIConstantPoolUtils.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/ExceptionHandlerInteropWrapper.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropBootstrapMethodInvocation.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropConstantPoolWrapper.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/InteropLineNumberTableHelper.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/JVMCIInteropHelper.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/LocalInteropWrapper.java create mode 100644 espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/external/TypeWrapper.java 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 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 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 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 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 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 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 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/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 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/impl/jvmci/JVMCIUtils.java b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java index e9620f126a57..e633ada38477 100644 --- a/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java +++ b/espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/impl/jvmci/JVMCIUtils.java @@ -22,6 +22,8 @@ */ 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; import com.oracle.truffle.espresso.EspressoLanguage; @@ -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/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/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 042267fe13f8..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.impl.jvmci.JVMCIIndyData.indyCpi; -import static com.oracle.truffle.espresso.impl.jvmci.JVMCIIndyData.isIndyCPI; +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.impl.jvmci.JVMCIUtils.findObjectType; 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.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_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_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);