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); 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..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 @@ -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; @@ -57,17 +61,39 @@ 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); 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.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.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..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 @@ -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,8 +218,17 @@ 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); + @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.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.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 9272c871b6d9..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 @@ -26,24 +26,34 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.INVOKEDYNAMIC; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; 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; 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; 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; @@ -270,6 +280,18 @@ 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; + return (MethodType) resolvedEntry; + } + @Override public int intAt(int index) { checkTag(index, CONSTANT_Integer); @@ -317,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; /** 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..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 @@ -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 @@ -695,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 d2a9c0669308..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 @@ -264,6 +264,9 @@ 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; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ConstantPool; @@ -367,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()) { @@ -1207,6 +1199,12 @@ 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 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 @@ -1281,6 +1279,24 @@ 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); + } + } + + 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 7aeebb9c4da0..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 @@ -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; @@ -38,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; @@ -62,10 +68,59 @@ 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); + 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); + 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 +278,30 @@ 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; + } + + private static Class resolveSymbolAndAccessCheck(InterpreterResolvedObjectType accessingClass, UnresolvedJavaType type) { + Class clazz = CremaSupport.singleton().resolveOrThrow(type, accessingClass); + // GR-62339 check access + return clazz; + } }