From 93190ed2fd9f4c9271e940e2be32e2eac238bb6b Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sat, 27 Sep 2025 17:46:39 +0200 Subject: [PATCH 1/7] Fix import order in InterpreterConstantPool --- .../svm/interpreter/metadata/InterpreterConstantPool.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java index 9272c871b6d9..dcc04474877c 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java @@ -28,8 +28,6 @@ import java.util.List; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.PrimitiveConstant; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -42,8 +40,10 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaField; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaMethod; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.Signature; import jdk.vm.ci.meta.UnresolvedJavaField; From 296016f0426a5ff30c242d760c19df45f24365b7 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sat, 27 Sep 2025 17:57:19 +0200 Subject: [PATCH 2/7] CremaSupport: improve type resolution * Allow resolving array types * Add methods for resolving symbols directly --- .../svm/core/hub/crema/CremaSupport.java | 28 ++++++- .../svm/interpreter/CremaRuntimeAccess.java | 34 +------- .../svm/interpreter/CremaSupportImpl.java | 84 +++++++++++++------ 3 files changed, 87 insertions(+), 59 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java index dec9d1b7a76c..652da78f899b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java @@ -31,7 +31,11 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.espresso.classfile.ParserKlass; +import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.classfile.descriptors.Type; import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -63,11 +67,29 @@ interface CremaDispatchTable { Class toClass(ResolvedJavaType resolvedJavaType); - Class resolveOrThrow(JavaType unresolvedJavaType, ResolvedJavaType accessingClass); + default Class resolveOrThrow(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { + ByteSequence type = ByteSequence.create(unresolvedJavaType.getName()); + Symbol symbolicType = SymbolsSupport.getTypes().getOrCreateValidType(type); + return resolveOrThrow(symbolicType, accessingClass); + } + + Class resolveOrThrow(Symbol type, ResolvedJavaType accessingClass); + + default Class resolveOrNull(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { + ByteSequence type = ByteSequence.create(unresolvedJavaType.getName()); + Symbol symbolicType = SymbolsSupport.getTypes().getOrCreateValidType(type); + return resolveOrNull(symbolicType, accessingClass); + } + + Class resolveOrNull(Symbol type, ResolvedJavaType accessingClass); - Class resolveOrNull(JavaType unresolvedJavaType, ResolvedJavaType accessingClass); + default Class findLoadedClass(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { + ByteSequence type = ByteSequence.create(unresolvedJavaType.getName()); + Symbol symbolicType = SymbolsSupport.getTypes().getOrCreateValidType(type); + return findLoadedClass(symbolicType, accessingClass); + } - Class findLoadedClass(JavaType unresolvedJavaType, ResolvedJavaType accessingClass); + Class findLoadedClass(Symbol type, ResolvedJavaType accessingClass); Object getStaticStorage(Class cls, boolean primitives, int layerNum); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaRuntimeAccess.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaRuntimeAccess.java index 2aa4aed2f08f..2797c66b50ff 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaRuntimeAccess.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaRuntimeAccess.java @@ -28,7 +28,7 @@ import java.lang.invoke.MethodType; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.hub.registry.ClassRegistries; +import com.oracle.svm.core.hub.crema.CremaSupport; import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.JavaVersion; @@ -92,37 +92,11 @@ public RuntimeException throwError(ErrorType errorType, String messageFormat, Ob throw fatal(message); } - private static String toClassForName(Symbol type) { - String typeString = type.toString(); - if (TypeSymbols.isArray(type)) { - return typeString.replace('/', '.'); - } - // Primitives cannot be resolved via Class.forName, but provide name for completeness. - if (TypeSymbols.isPrimitive(type)) { - // I -> int - // Z -> boolean - // ... - return TypeSymbols.getJavaKind(type).toJavaClass().getName(); - } - assert typeString.startsWith("L") && typeString.endsWith(";"); - return typeString.substring(1, typeString.length() - 1); // drop L and ; - } - - @SuppressWarnings("unchecked") - public static RuntimeException uncheckedThrow(Throwable t) throws T { - throw (T) t; - } - @Override public InterpreterResolvedObjectType lookupOrLoadType(Symbol type, InterpreterResolvedJavaType accessingClass) { - String className = toClassForName(type); - try { - Class result = ClassRegistries.forName(className, accessingClass.getJavaClass().getClassLoader()); - assert !result.isPrimitive(); - return (InterpreterResolvedObjectType) DynamicHub.fromClass(result).getInterpreterType(); - } catch (ClassNotFoundException e) { - throw uncheckedThrow(e); - } + Class result = CremaSupport.singleton().resolveOrThrow(type, accessingClass); + assert !result.isPrimitive(); + return (InterpreterResolvedObjectType) DynamicHub.fromClass(result).getInterpreterType(); } @Override diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java index b433fd0ea853..eac37bcc6e2a 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java @@ -52,18 +52,20 @@ import com.oracle.svm.core.hub.registry.ClassRegistries; import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ConstantPool; +import com.oracle.svm.espresso.classfile.JavaKind; import com.oracle.svm.espresso.classfile.ParserField; import com.oracle.svm.espresso.classfile.ParserKlass; import com.oracle.svm.espresso.classfile.ParserMethod; import com.oracle.svm.espresso.classfile.attributes.Attribute; import com.oracle.svm.espresso.classfile.attributes.ConstantValueAttribute; -import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; import com.oracle.svm.espresso.classfile.descriptors.Name; import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols; import com.oracle.svm.espresso.classfile.descriptors.Signature; import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; +import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; import com.oracle.svm.espresso.shared.vtable.MethodTableException; import com.oracle.svm.espresso.shared.vtable.PartialMethod; import com.oracle.svm.espresso.shared.vtable.PartialType; @@ -80,7 +82,6 @@ import com.oracle.svm.interpreter.metadata.InterpreterResolvedObjectType; import jdk.graal.compiler.word.Word; -import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; @@ -638,50 +639,81 @@ public Class toClass(ResolvedJavaType resolvedJavaType) { } @Override - public Class resolveOrThrow(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { + public Class resolveOrThrow(Symbol type, ResolvedJavaType accessingClass) { + int arrayDimensions = TypeSymbols.getArrayDimensions(type); + Symbol elementalType; + if (arrayDimensions == 0) { + elementalType = type; + } else { + elementalType = SymbolsSupport.getTypes().getOrCreateValidType(type.subSequence(arrayDimensions)); + } try { - Class result = loadClass(unresolvedJavaType, (InterpreterResolvedJavaType) accessingClass); + Class result = loadClass(elementalType, (InterpreterResolvedJavaType) accessingClass); if (result == null) { - throw new NoClassDefFoundError(getTypeName(unresolvedJavaType)); - } else { - return result; + throw new NoClassDefFoundError(elementalType.toString()); + } + if (arrayDimensions > 0) { + while (arrayDimensions-- > 0) { + result = DynamicHub.toClass(DynamicHub.fromClass(result).arrayType()); + } } + return result; } catch (ClassNotFoundException e) { - NoClassDefFoundError error = new NoClassDefFoundError(getTypeName(unresolvedJavaType)); + NoClassDefFoundError error = new NoClassDefFoundError(elementalType.toString()); error.initCause(e); throw error; } } - private static String getTypeName(JavaType unresolvedJavaType) { - assert unresolvedJavaType.getElementalType().getName().startsWith("L"); - - String internalName = unresolvedJavaType.getElementalType().getName(); - return internalName.substring(1, internalName.length() - 1); - } - @Override - public Class resolveOrNull(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { + public Class resolveOrNull(Symbol type, ResolvedJavaType accessingClass) { + int arrayDimensions = TypeSymbols.getArrayDimensions(type); + Symbol elementalType; + if (arrayDimensions == 0) { + elementalType = type; + } else { + elementalType = SymbolsSupport.getTypes().getOrCreateValidType(type.subSequence(arrayDimensions)); + } try { - return loadClass(unresolvedJavaType, (InterpreterResolvedJavaType) accessingClass); + Class result = loadClass(elementalType, (InterpreterResolvedJavaType) accessingClass); + if (result == null) { + return null; + } + if (arrayDimensions > 0) { + while (arrayDimensions-- > 0) { + result = DynamicHub.toClass(DynamicHub.fromClass(result).arrayType()); + } + } + return result; } catch (ClassNotFoundException e) { return null; } } - private static Class loadClass(JavaType unresolvedJavaType, InterpreterResolvedJavaType accessingClass) throws ClassNotFoundException { - ByteSequence type = ByteSequence.create(unresolvedJavaType.getName()); - Symbol symbolicType = SymbolsSupport.getTypes().getOrCreateValidType(type); - AbstractClassRegistry registry = ClassRegistries.singleton().getRegistry(accessingClass.getJavaClass().getClassLoader()); - return registry.loadClass(symbolicType); + private static Class loadClass(Symbol type, InterpreterResolvedJavaType accessingClass) throws ClassNotFoundException { + JavaKind kind = TypeSymbols.getJavaKind(type); + return switch (kind) { + case Object -> { + AbstractClassRegistry registry = ClassRegistries.singleton().getRegistry(accessingClass.getJavaClass().getClassLoader()); + yield registry.loadClass(type); + } + case Boolean -> boolean.class; + case Byte -> byte.class; + case Short -> short.class; + case Char -> char.class; + case Int -> int.class; + case Long -> long.class; + case Float -> float.class; + case Double -> double.class; + case Void -> void.class; + default -> throw VMError.shouldNotReachHere(kind.toString()); + }; } @Override - public Class findLoadedClass(JavaType unresolvedJavaType, ResolvedJavaType accessingClass) { - ByteSequence type = ByteSequence.create(unresolvedJavaType.getName()); - Symbol symbolicType = SymbolsSupport.getTypes().getOrCreateValidType(type); + public Class findLoadedClass(Symbol type, ResolvedJavaType accessingClass) { AbstractClassRegistry registry = ClassRegistries.singleton().getRegistry(((InterpreterResolvedJavaType) accessingClass).getJavaClass().getClassLoader()); - return registry.findLoadedClass(symbolicType); + return registry.findLoadedClass(type); } @Override From 3920473c3f258025e200c703d288a17da14cf29d Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sun, 28 Sep 2025 16:48:32 +0200 Subject: [PATCH 3/7] Add BooleanSupplier for crema on/off --- .../svm/core/hub/RuntimeClassLoading.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/RuntimeClassLoading.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/RuntimeClassLoading.java index 66b6a649c1af..058f9832f9a0 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/RuntimeClassLoading.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/RuntimeClassLoading.java @@ -27,8 +27,11 @@ import static jdk.graal.compiler.options.OptionStability.EXPERIMENTAL; import java.security.ProtectionDomain; +import java.util.function.BooleanSupplier; import org.graalvm.collections.EconomicMap; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.hub.crema.CremaSupport; @@ -122,6 +125,22 @@ public static boolean isSupported() { return Options.RuntimeClassLoading.getValue(); } + @Platforms(Platform.HOSTED_ONLY.class) + public static final class NoRuntimeClassLoading implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return !isSupported(); + } + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static final class WithRuntimeClassLoading implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return isSupported(); + } + } + public static Class defineClass(ClassLoader loader, String expectedName, byte[] b, int off, int len, ClassDefinitionInfo info) { if (PredefinedClassesSupport.hasBytecodeClasses()) { Class knownClass = PredefinedClassesSupport.knownClass(b, off, len); From 6c0fe6e20be43479b1a8ffc639dacab77a7561d9 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sun, 28 Sep 2025 16:41:23 +0200 Subject: [PATCH 4/7] Crema: Resolve METHODTYPE constant pool entries --- ..._java_lang_invoke_MethodHandleNatives.java | 7 ++++ .../metadata/InterpreterConstantPool.java | 7 ++++ .../oracle/svm/interpreter/Interpreter.java | 14 ++++++++ .../RuntimeInterpreterConstantPool.java | 32 +++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java index 3a400f53cc02..0dbd8d4774cd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java @@ -29,6 +29,7 @@ import java.lang.invoke.CallSite; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -48,7 +49,9 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.invoke.Target_java_lang_invoke_MemberName; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; @@ -215,6 +218,10 @@ public static Target_java_lang_invoke_MemberName resolve(Target_java_lang_invoke return resolved; } + @Alias + @TargetElement(onlyWith = RuntimeClassLoading.WithRuntimeClassLoading.class) + public static native MethodType findMethodHandleType(Class rtype, Class[] ptypes); + @Delete static native MethodHandle linkMethodHandleConstant(Class callerClass, int refKind, Class defc, String name, Object type); } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java index dcc04474877c..d1cb39b29f6a 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java @@ -26,6 +26,7 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.INVOKEDYNAMIC; +import java.lang.invoke.MethodType; import java.util.List; import org.graalvm.nativeimage.Platform; @@ -270,6 +271,12 @@ public String resolveStringAt(int cpi) { return (String) resolvedEntry; } + public MethodType resolvedMethodTypeAt(char cpi, InterpreterResolvedObjectType accessingClass) { + Object resolvedEntry = resolvedAt(cpi, accessingClass); + assert resolvedEntry != null; + return (MethodType) resolvedEntry; + } + @Override public int intAt(int index) { checkTag(index, CONSTANT_Integer); diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index d2a9c0669308..6cc78aa7dbaa 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -264,6 +264,8 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.TABLESWITCH; import static com.oracle.svm.interpreter.metadata.Bytecodes.WIDE; +import java.lang.invoke.MethodType; + import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ConstantPool; @@ -1207,6 +1209,9 @@ private static void loadConstant(InterpreterFrame frame, InterpreterResolvedJava String string = pool.resolveStringAt(cpi); putObject(frame, top, string); } + case METHODTYPE -> { + putObject(frame, top, resolveMethodType(pool, method, opcode, cpi)); + } case INVOKEDYNAMIC -> { // TODO(peterssen): GR-68576 Storing the pre-resolved appendix in the CP is a // workaround for the JDWP debugger until proper INVOKEDYNAMIC resolution is @@ -1281,6 +1286,15 @@ private static int invoke(InterpreterFrame callerFrame, InterpreterResolvedJavaM return retStackEffect - Bytecodes.stackEffectOf(opcode); } + private static MethodType resolveMethodType(InterpreterConstantPool pool, InterpreterResolvedJavaMethod method, int opcode, char cpi) { + assert opcode == LDC || opcode == LDC_W; + try { + return pool.resolvedMethodTypeAt(cpi, method.getDeclaringClass()); + } catch (Throwable t) { + throw SemanticJavaException.raise(t); + } + } + // region Class/Method/Field resolution private static InterpreterResolvedJavaType resolveType(InterpreterResolvedJavaMethod method, int opcode, char cpi) { diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java index 7aeebb9c4da0..4f67d2f8d994 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java @@ -24,11 +24,16 @@ */ package com.oracle.svm.interpreter; +import java.lang.invoke.MethodType; + +import com.oracle.svm.core.hub.crema.CremaSupport; import com.oracle.svm.core.hub.registry.SymbolsSupport; +import com.oracle.svm.core.methodhandles.Target_java_lang_invoke_MethodHandleNatives; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ParserConstantPool; import com.oracle.svm.espresso.classfile.descriptors.Name; import com.oracle.svm.espresso.classfile.descriptors.Signature; +import com.oracle.svm.espresso.classfile.descriptors.SignatureSymbols; import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.espresso.classfile.descriptors.Type; import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; @@ -62,10 +67,17 @@ protected Object resolve(int cpi, InterpreterResolvedObjectType accessingClass) case INTERFACE_METHOD_REF -> resolveInterfaceMethodRefConstant(cpi, accessingClass); case METHOD_REF -> resolveClassMethodRefConstant(cpi, accessingClass); case CLASS -> resolveClassConstant(cpi, accessingClass); + case METHODTYPE -> resolveMethodType(cpi, accessingClass); default -> throw VMError.unimplemented("Unimplemented CP resolution for " + tag); }; } + private Object resolveMethodType(int cpi, InterpreterResolvedObjectType accessingClass) { + Symbol sig = this.methodTypeSignature(cpi); + Symbol[] parsed = SymbolsSupport.getSignatures().parsed(sig); + return signatureToMethodType(parsed, accessingClass); + } + private String resolveStringConstant(int stringIndex, @SuppressWarnings("unused") InterpreterResolvedObjectType accessingKlass) { int utf8Index = this.stringUtf8Index(stringIndex); String string = this.utf8At(utf8Index).toString().intern(); // intern? @@ -223,4 +235,24 @@ private InterpreterResolvedJavaMethod resolveInterfaceMethodRefConstant(int inte return interfaceMethod; } + + public static MethodType signatureToMethodType(Symbol[] signature, InterpreterResolvedObjectType accessingClass) { + Symbol rt = SignatureSymbols.returnType(signature); + int pcount = SignatureSymbols.parameterCount(signature); + Class[] ptypes = new Class[pcount]; + Class rtype; + for (int i = 0; i < pcount; i++) { + Symbol paramType = SignatureSymbols.parameterType(signature, i); + ptypes[i] = resolveSymbolAndAccessCheck(accessingClass, paramType); + } + rtype = resolveSymbolAndAccessCheck(accessingClass, rt); + + return Target_java_lang_invoke_MethodHandleNatives.findMethodHandleType(rtype, ptypes); + } + + private static Class resolveSymbolAndAccessCheck(InterpreterResolvedObjectType accessingClass, Symbol type) { + Class clazz = CremaSupport.singleton().resolveOrThrow(type, accessingClass); + // GR-62339 check access + return clazz; + } } From 34889f11f6dec4e6273c9c321c452b4ef1588b99 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sat, 27 Sep 2025 18:06:14 +0200 Subject: [PATCH 5/7] Crema: Resolve METHODHANDLE in constant pool Use the JDK's `MethodHandleNatives.linkMethodHandleConstant` to create the resolved `MethodHandle`. --- ..._java_lang_invoke_MethodHandleNatives.java | 7 ++- ...java_lang_invoke_MethodHandles_Lookup.java | 9 ++++ .../metadata/InterpreterConstantPool.java | 7 +++ .../oracle/svm/interpreter/Interpreter.java | 13 +++++ .../RuntimeInterpreterConstantPool.java | 49 +++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java index 0dbd8d4774cd..9d778fb6709d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandleNatives.java @@ -223,7 +223,12 @@ public static Target_java_lang_invoke_MemberName resolve(Target_java_lang_invoke public static native MethodType findMethodHandleType(Class rtype, Class[] ptypes); @Delete - static native MethodHandle linkMethodHandleConstant(Class callerClass, int refKind, Class defc, String name, Object type); + @TargetElement(onlyWith = RuntimeClassLoading.NoRuntimeClassLoading.class, name = "linkMethodHandleConstant") + static native MethodHandle linkMethodHandleConstantDeleted(Class callerClass, int refKind, Class defc, String name, Object type); + + @Alias + @TargetElement(onlyWith = RuntimeClassLoading.WithRuntimeClassLoading.class) + public static native MethodHandle linkMethodHandleConstant(Class callerClass, int refKind, Class defc, String name, Object type); } /** diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandles_Lookup.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandles_Lookup.java index 0cbaa2d8b499..7c3259eb21ce 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandles_Lookup.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandles_Lookup.java @@ -35,13 +35,21 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.invoke.Target_java_lang_invoke_MemberName; @TargetClass(value = MethodHandles.class, innerClass = "Lookup") final class Target_java_lang_invoke_MethodHandles_Lookup { // Checkstyle: stop @Delete // + @TargetElement(onlyWith = RuntimeClassLoading.NoRuntimeClassLoading.class, name = "LOOKASIDE_TABLE") // + static ConcurrentHashMap LOOKASIDE_TABLE_DELETED; + + @Alias // + @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.NewInstance, declClass = ConcurrentHashMap.class)// + @TargetElement(onlyWith = RuntimeClassLoading.WithRuntimeClassLoading.class) // static ConcurrentHashMap LOOKASIDE_TABLE; // Checkstyle: resume @@ -87,5 +95,6 @@ private IllegalAccessException makeAccessException(Class targetClass) { } @Delete + @TargetElement(onlyWith = RuntimeClassLoading.NoRuntimeClassLoading.class) // native MethodHandle linkMethodHandleConstant(byte refKind, Class defc, String name, Object type) throws ReflectiveOperationException; } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java index d1cb39b29f6a..8f483c1f96e1 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java @@ -26,6 +26,7 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.INVOKEDYNAMIC; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import java.util.List; @@ -271,6 +272,12 @@ public String resolveStringAt(int cpi) { return (String) resolvedEntry; } + public MethodHandle resolvedMethodHandleAt(int cpi, InterpreterResolvedObjectType accessingClass) { + Object resolvedEntry = resolvedAt(cpi, accessingClass); + assert resolvedEntry != null; + return (MethodHandle) resolvedEntry; + } + public MethodType resolvedMethodTypeAt(char cpi, InterpreterResolvedObjectType accessingClass) { Object resolvedEntry = resolvedAt(cpi, accessingClass); assert resolvedEntry != null; diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index 6cc78aa7dbaa..d157ada93095 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -264,6 +264,7 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.TABLESWITCH; import static com.oracle.svm.interpreter.metadata.Bytecodes.WIDE; +import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; import com.oracle.svm.core.jdk.InternalVMMethod; @@ -1212,6 +1213,9 @@ private static void loadConstant(InterpreterFrame frame, InterpreterResolvedJava case METHODTYPE -> { putObject(frame, top, resolveMethodType(pool, method, opcode, cpi)); } + case METHODHANDLE -> { + putObject(frame, top, resolveMethodHandle(pool, method, opcode, cpi)); + } case INVOKEDYNAMIC -> { // TODO(peterssen): GR-68576 Storing the pre-resolved appendix in the CP is a // workaround for the JDWP debugger until proper INVOKEDYNAMIC resolution is @@ -1295,6 +1299,15 @@ private static MethodType resolveMethodType(InterpreterConstantPool pool, Interp } } + private static MethodHandle resolveMethodHandle(InterpreterConstantPool pool, InterpreterResolvedJavaMethod method, int opcode, char cpi) { + assert opcode == LDC || opcode == LDC_W; + try { + return pool.resolvedMethodHandleAt(cpi, method.getDeclaringClass()); + } catch (Throwable t) { + throw SemanticJavaException.raise(t); + } + } + // region Class/Method/Field resolution private static InterpreterResolvedJavaType resolveType(InterpreterResolvedJavaMethod method, int opcode, char cpi) { diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java index 4f67d2f8d994..b2ed151010e3 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/RuntimeInterpreterConstantPool.java @@ -43,6 +43,7 @@ import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaType; import com.oracle.svm.interpreter.metadata.InterpreterResolvedObjectType; +import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.UnresolvedJavaField; import jdk.vm.ci.meta.UnresolvedJavaMethod; import jdk.vm.ci.meta.UnresolvedJavaType; @@ -68,10 +69,52 @@ protected Object resolve(int cpi, InterpreterResolvedObjectType accessingClass) case METHOD_REF -> resolveClassMethodRefConstant(cpi, accessingClass); case CLASS -> resolveClassConstant(cpi, accessingClass); case METHODTYPE -> resolveMethodType(cpi, accessingClass); + case METHODHANDLE -> resolveMethodHandle(cpi, accessingClass); default -> throw VMError.unimplemented("Unimplemented CP resolution for " + tag); }; } + private Object resolveMethodHandle(int cpi, InterpreterResolvedObjectType accessingClass) { + Object mtype; + InterpreterResolvedJavaType mklass; + Symbol refName; + + int memberIndex = this.methodHandleMemberIndex(cpi); + + Tag refTag = this.tagAt(memberIndex); + if (refTag == Tag.METHOD_REF || refTag == Tag.INTERFACE_METHOD_REF) { + InterpreterResolvedJavaMethod target = this.resolvedMethodAt(accessingClass, memberIndex); + Symbol[] parsed = target.getParsedSymbolicSignature(CremaRuntimeAccess.getInstance().getSymbolPool()); + + mtype = signatureToMethodType(parsed, accessingClass); + /* + * we should use the klass from the method ref here rather than the declaring klass of + * the target this is because the resolved target might come from a default method and + * have an interface as declaring klass however if the refKind is invokeVirtual, it + * would be illegal to use the interface type + */ + int holderIndex = this.memberClassIndex(memberIndex); + mklass = this.resolvedTypeAt(accessingClass, holderIndex); + refName = target.getSymbolicName(); + } else { + assert refTag == Tag.FIELD_REF; + InterpreterResolvedJavaField field = this.resolvedFieldAt(accessingClass, memberIndex); + JavaType fieldType = field.getType(); + if (fieldType instanceof InterpreterResolvedJavaType resolvedJavaType) { + mtype = resolvedJavaType.getJavaClass(); + } else { + mtype = resolveSymbolAndAccessCheck(field.getDeclaringClass(), (UnresolvedJavaType) fieldType); + } + mklass = field.getDeclaringClass(); + refName = field.getSymbolicName(); + } + String mname = refName.toString(); + int refKind = this.methodHandleRefKind(cpi); + return Target_java_lang_invoke_MethodHandleNatives.linkMethodHandleConstant( + accessingClass.getJavaClass(), refKind, + mklass.getJavaClass(), mname, mtype); + } + private Object resolveMethodType(int cpi, InterpreterResolvedObjectType accessingClass) { Symbol sig = this.methodTypeSignature(cpi); Symbol[] parsed = SymbolsSupport.getSignatures().parsed(sig); @@ -255,4 +298,10 @@ private static Class resolveSymbolAndAccessCheck(InterpreterResolvedObjectTyp // GR-62339 check access return clazz; } + + private static Class resolveSymbolAndAccessCheck(InterpreterResolvedObjectType accessingClass, UnresolvedJavaType type) { + Class clazz = CremaSupport.singleton().resolveOrThrow(type, accessingClass); + // GR-62339 check access + return clazz; + } } From f1fcdcd21fe8a71c94afee41abeb36b7181334d5 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sat, 27 Sep 2025 18:00:08 +0200 Subject: [PATCH 6/7] Crema: implement getExceptionHandlers() for crema-loaded types --- .../svm/core/hub/registry/SVMSymbols.java | 1 + .../metadata/CremaResolvedJavaMethodImpl.java | 59 +++++++++++++++++++ .../metadata/InterpreterConstantPool.java | 26 ++++++++ .../InterpreterResolvedJavaMethod.java | 5 +- .../interpreter/BuildTimeConstantPool.java | 4 +- 5 files changed, 90 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java index 016b9953fc11..a2c0fceb5b46 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java @@ -47,6 +47,7 @@ public static void ensureInitialized() { public static final class SVMTypes { public static final Symbol com_oracle_svm_core_hub_Hybrid = SYMBOLS.putType("Lcom/oracle/svm/core/hub/Hybrid;"); + public static final Symbol java_lang_Throwable = SYMBOLS.putType("Ljava/lang/Throwable;"); private SVMTypes() { } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaResolvedJavaMethodImpl.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaResolvedJavaMethodImpl.java index 25fcd74aadac..cbf327deb9ea 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaResolvedJavaMethodImpl.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaResolvedJavaMethodImpl.java @@ -24,24 +24,83 @@ */ package com.oracle.svm.interpreter.metadata; +import java.lang.invoke.VarHandle; + import com.oracle.svm.core.hub.crema.CremaResolvedJavaMethod; +import com.oracle.svm.core.hub.registry.SVMSymbols; import com.oracle.svm.core.reflect.CremaConstructorAccessor; import com.oracle.svm.core.reflect.CremaMethodAccessor; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.espresso.classfile.ExceptionHandler; import com.oracle.svm.espresso.classfile.ParserMethod; +import com.oracle.svm.espresso.classfile.attributes.CodeAttribute; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.classfile.descriptors.Type; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaType; public final class CremaResolvedJavaMethodImpl extends InterpreterResolvedJavaMethod implements CremaResolvedJavaMethod { + private final ExceptionHandler[] rawExceptionHandlers; private CremaResolvedJavaMethodImpl(InterpreterResolvedObjectType declaringClass, ParserMethod parserMethod, int vtableIndex) { super(declaringClass, parserMethod, vtableIndex); + CodeAttribute codeAttribute = (CodeAttribute) parserMethod.getAttribute(CodeAttribute.NAME); + if (codeAttribute != null) { + this.rawExceptionHandlers = codeAttribute.getExceptionHandlers(); + } else { + this.rawExceptionHandlers = null; + } } public static InterpreterResolvedJavaMethod create(InterpreterResolvedObjectType declaringClass, ParserMethod m, int vtableIndex) { return new CremaResolvedJavaMethodImpl(declaringClass, m, vtableIndex); } + @Override + public jdk.vm.ci.meta.ExceptionHandler[] getExceptionHandlers() { + /* + * GR-70247 The interpreter should primarily use classfile.ExceptionHandler. This would + * avoid having to deal with the JavaType which is not needed during interpretation. + */ + jdk.vm.ci.meta.ExceptionHandler[] result = exceptionHandlers; + if (result == null) { + boolean canCache = true; + if (rawExceptionHandlers == null || rawExceptionHandlers.length == 0) { + result = EMPTY_EXCEPTION_HANDLERS; + } else { + result = new jdk.vm.ci.meta.ExceptionHandler[rawExceptionHandlers.length]; + InterpreterConstantPool constantPool = getConstantPool(); + for (int i = 0; i < rawExceptionHandlers.length; i++) { + ExceptionHandler exceptionHandler = rawExceptionHandlers[i]; + Symbol catchTypeSymbol = exceptionHandler.getCatchType(); + int catchTypeCPI = exceptionHandler.catchTypeCPI(); + JavaType catchType; + if (SVMSymbols.SVMTypes.java_lang_Throwable.equals(catchTypeSymbol)) { + catchTypeCPI = 0; + catchType = null; + } else if (catchTypeCPI != 0) { + catchType = constantPool.findClassAt(catchTypeCPI); + canCache = canCache && (catchType instanceof ResolvedJavaType); + } else { + assert catchTypeSymbol == null; + catchType = null; + } + result[i] = new jdk.vm.ci.meta.ExceptionHandler(exceptionHandler.getStartBCI(), + exceptionHandler.getEndBCI(), + exceptionHandler.getHandlerBCI(), + catchTypeCPI, + catchType); + } + VarHandle.fullFence(); + } + if (canCache) { + this.exceptionHandlers = result; + } + } + return result; + } + @Override public JavaType[] getDeclaredExceptions() { // (GR-69097) diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java index 8f483c1f96e1..c72ef218633a 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterConstantPool.java @@ -35,9 +35,17 @@ import com.oracle.svm.core.BuildPhaseProvider.AfterAnalysis; import com.oracle.svm.core.heap.UnknownObjectField; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.crema.CremaSupport; +import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ConstantPool; import com.oracle.svm.espresso.classfile.ParserConstantPool; +import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; +import com.oracle.svm.espresso.classfile.descriptors.Name; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.classfile.descriptors.Type; +import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; import com.oracle.svm.interpreter.metadata.serialization.VisibleForSerialization; import jdk.vm.ci.meta.JavaConstant; @@ -331,4 +339,22 @@ public long longAt(int index) { } return super.longAt(index); } + + public JavaType findClassAt(int cpi) { + if (peekCachedEntry(cpi) instanceof InterpreterResolvedObjectType type) { + return type; + } + Symbol nameSymbol = className(cpi); + ByteSequence typeBytes = TypeSymbols.nameToType(nameSymbol); + Symbol typeSymbol = SymbolsSupport.getTypes().lookupValidType(typeBytes); + if (typeSymbol == null) { + return null; + } + Class cls = CremaSupport.singleton().findLoadedClass(typeSymbol, getHolder()); + if (cls == null) { + return UnresolvedJavaType.create(typeBytes.toString()); + } else { + return DynamicHub.fromClass(cls).getInterpreterType(); + } + } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java index f39abb384eb8..c16804f4227f 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaMethod.java @@ -68,6 +68,7 @@ public class InterpreterResolvedJavaMethod implements ResolvedJavaMethod, CremaMethodAccess { public static final InterpreterResolvedJavaMethod[] EMPTY_ARRAY = new InterpreterResolvedJavaMethod[0]; public static final LocalVariableTable EMPTY_LOCAL_VARIABLE_TABLE = new LocalVariableTable(new Local[0]); + public static final ExceptionHandler[] EMPTY_EXCEPTION_HANDLERS = new ExceptionHandler[0]; public static final int UNKNOWN_METHOD_ID = 0; @@ -89,7 +90,7 @@ public class InterpreterResolvedJavaMethod implements ResolvedJavaMethod, CremaM private final LineNumberTable lineNumberTable; - private ExceptionHandler[] exceptionHandlers; + protected ExceptionHandler[] exceptionHandlers; private LocalVariableTable localVariableTable; @@ -340,7 +341,7 @@ public final boolean isConstructor() { } @Override - public final ExceptionHandler[] getExceptionHandlers() { + public ExceptionHandler[] getExceptionHandlers() { ExceptionHandler[] result = exceptionHandlers; VMError.guarantee(result != null); return result; diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java index 96692a3997b3..b4363a2bba98 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeConstantPool.java @@ -41,6 +41,7 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.NEW; import static com.oracle.svm.interpreter.metadata.Bytecodes.PUTFIELD; import static com.oracle.svm.interpreter.metadata.Bytecodes.PUTSTATIC; +import static com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaMethod.EMPTY_EXCEPTION_HANDLERS; import java.util.List; @@ -88,9 +89,6 @@ */ @Platforms(Platform.HOSTED_ONLY.class) final class BuildTimeConstantPool { - - private static final ExceptionHandler[] EMPTY_EXCEPTION_HANDLERS = new ExceptionHandler[0]; - private final ConstantPoolBuilder poolBuilder; /** From a4bdaca18163556db8eb767ac62a2a9be794a27e Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Tue, 30 Sep 2025 23:55:13 +0200 Subject: [PATCH 7/7] Crema reflection: wrap exceptions in InvocationTargetException --- .../svm/core/hub/crema/CremaSupport.java | 6 +++++- .../core/reflect/CremaConstructorAccessor.java | 18 ++++++++++++++---- .../svm/core/reflect/CremaMethodAccessor.java | 11 +++++++---- .../svm/interpreter/CremaSupportImpl.java | 4 ++-- .../oracle/svm/interpreter/Interpreter.java | 11 ----------- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java index 652da78f899b..3505d161004e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/crema/CremaSupport.java @@ -61,7 +61,11 @@ interface CremaDispatchTable { void fillDynamicHubInfo(DynamicHub hub, CremaDispatchTable table, List> transitiveSuperInterfaces, int[] interfaceIndices); - Object newInstance(ResolvedJavaMethod targetMethod, Object[] args); + /** + * Creates a new instance of {@code type} without running any constructor yet. The caller should + * make sure to run a constructor before publishing the result. + */ + Object rawNewInstance(ResolvedJavaType type); Object execute(ResolvedJavaMethod targetMethod, Object[] args); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaConstructorAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaConstructorAccessor.java index 84bfa7bb4723..83f86addd09e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaConstructorAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaConstructorAccessor.java @@ -24,14 +24,14 @@ */ package com.oracle.svm.core.reflect; +import java.lang.reflect.InvocationTargetException; + import com.oracle.svm.core.hub.crema.CremaSupport; import com.oracle.svm.core.jdk.InternalVMMethod; import jdk.internal.reflect.ConstructorAccessor; import jdk.vm.ci.meta.ResolvedJavaMethod; -import org.graalvm.nativeimage.ImageSingletons; - @InternalVMMethod public final class CremaConstructorAccessor extends AbstractCremaAccessor implements ConstructorAccessor { @@ -40,9 +40,19 @@ public CremaConstructorAccessor(ResolvedJavaMethod targetMethod, Class declar } @Override - public Object newInstance(Object[] args) { + public Object newInstance(Object[] args) throws InvocationTargetException { verifyArguments(args); ensureDeclaringClassInitialized(); - return ImageSingletons.lookup(CremaSupport.class).newInstance(targetMethod, args); + + Object newReference = CremaSupport.singleton().rawNewInstance(targetMethod.getDeclaringClass()); + Object[] finalArgs = new Object[args.length + 1]; + finalArgs[0] = newReference; + System.arraycopy(args, 0, finalArgs, 1, args.length); + try { + CremaSupport.singleton().execute(targetMethod, finalArgs); + } catch (Throwable t) { + throw new InvocationTargetException(t); + } + return newReference; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaMethodAccessor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaMethodAccessor.java index cf08575142db..c40551e4f82b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaMethodAccessor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/CremaMethodAccessor.java @@ -24,16 +24,15 @@ */ package com.oracle.svm.core.reflect; +import java.lang.reflect.InvocationTargetException; + import com.oracle.svm.core.hub.crema.CremaSupport; import com.oracle.svm.core.jdk.InternalVMMethod; import com.oracle.svm.core.util.VMError; import jdk.internal.reflect.MethodAccessor; - import jdk.vm.ci.meta.ResolvedJavaMethod; -import java.lang.reflect.InvocationTargetException; - @InternalVMMethod public final class CremaMethodAccessor extends AbstractCremaAccessor implements MethodAccessor { @@ -54,7 +53,11 @@ public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, Object[] finalArgs = new Object[args.length + 1]; finalArgs[0] = obj; System.arraycopy(args, 0, finalArgs, 1, args.length); - return CremaSupport.singleton().execute(targetMethod, finalArgs); + try { + return CremaSupport.singleton().execute(targetMethod, finalArgs); + } catch (Throwable t) { + throw new InvocationTargetException(t); + } } @Override diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java index eac37bcc6e2a..d4fea906becc 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaSupportImpl.java @@ -727,7 +727,7 @@ public Object execute(ResolvedJavaMethod targetMethod, Object[] args) { } @Override - public Object newInstance(ResolvedJavaMethod targetMethod, Object[] args) { - return Interpreter.newInstance((InterpreterResolvedJavaMethod) targetMethod, args); + public Object rawNewInstance(ResolvedJavaType type) { + return InterpreterToVM.createNewReference((InterpreterResolvedJavaType) type); } } diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java index d157ada93095..f07b9c1e4b90 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/Interpreter.java @@ -370,17 +370,6 @@ public static Object execute(InterpreterResolvedJavaMethod method, Object[] args return execute0(method, frame, forceStayInInterpreter); } - public static Object newInstance(InterpreterResolvedJavaMethod method, Object[] args) { - // this is a constructor call, so we have to allocate a new instance, - // expand this into args[0] and then execute - Object newReference = InterpreterToVM.createNewReference(method.getDeclaringClass()); - Object[] finalArgs = new Object[args.length + 1]; - finalArgs[0] = newReference; - System.arraycopy(args, 0, finalArgs, 1, args.length); - execute(method, finalArgs, false); - return newReference; - } - private static Object execute0(InterpreterResolvedJavaMethod method, InterpreterFrame frame, boolean stayInInterpreter) { try { if (method.isSynchronized()) {