From 510ac1ffebe349bcd7036edf2d33288c27162022 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 12 Jun 2025 15:31:59 +0200 Subject: [PATCH 1/6] Refactor `InterpreterResolvedJavaType.getDeclaredMethods` Call back to RuntimeClassLoading to request linking (not implemented yet) --- .../com/oracle/svm/core/hub/RuntimeClassLoading.java | 7 +++++++ .../svm/hosted/analysis/DynamicHubInitializer.java | 2 +- .../metadata/InterpreterResolvedJavaType.java | 11 +++++++++-- .../metadata/InterpreterResolvedObjectType.java | 5 ++++- .../metadata/InterpreterResolvedPrimitiveType.java | 6 ++++++ 5 files changed, 27 insertions(+), 4 deletions(-) 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 c27b665e952a..c4c5a9f28d97 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 @@ -258,4 +258,11 @@ public String toString() { public static ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType analysisType) { return ImageSingletons.lookup(CremaSupport.class).createInterpreterType(hub, analysisType); } + + public static void ensureLinked(DynamicHub dynamicHub) { + if (dynamicHub.isLinked()) { + return; + } + // GR-59739 runtime linking + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java index 665dad23abe4..4720adcab708 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/DynamicHubInitializer.java @@ -154,7 +154,7 @@ public void initializeMetaData(ImageHeapScanner heapScanner, AnalysisType type) ResolvedJavaType interpreterType = RuntimeClassLoading.createInterpreterType(hub, type); hub.setInterpreterType(interpreterType); heapScanner.rescanField(hub.getCompanion(), hubCompanionInterpreterType); - heapScanner.rescanObject(interpreterType.getDeclaredMethods()); + heapScanner.rescanObject(interpreterType.getDeclaredMethods(false)); } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java index cbd43dfa7791..6eaa20c67826 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java @@ -32,6 +32,8 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.word.WordBase; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.Assumptions; @@ -186,6 +188,11 @@ public final boolean isLinked() { throw VMError.intentionallyUnimplemented(); } + @Override + public void link() { + RuntimeClassLoading.ensureLinked(DynamicHub.fromClass(clazz)); + } + @Override public final boolean isInstance(JavaConstant obj) { throw VMError.intentionallyUnimplemented(); @@ -262,8 +269,8 @@ public final ResolvedJavaMethod[] getDeclaredConstructors() { } @Override - public ResolvedJavaMethod[] getDeclaredMethods() { - return NO_METHODS; + public final ResolvedJavaMethod[] getDeclaredMethods() { + return getDeclaredMethods(true); } @Override diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java index e65e639b3982..6bda32174c19 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java @@ -228,7 +228,10 @@ public VTableHolder getVtableHolder() { } @Override - public ResolvedJavaMethod[] getDeclaredMethods() { + public ResolvedJavaMethod[] getDeclaredMethods(boolean link) { + if (link) { + link(); + } return declaredMethods; } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java index 23052e89d08a..ef781407a94e 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java @@ -27,6 +27,7 @@ import java.lang.reflect.Modifier; import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public final class InterpreterResolvedPrimitiveType extends InterpreterResolvedJavaType { @@ -103,4 +104,9 @@ public ResolvedJavaType[] getInterfaces() { public boolean isAssignableFrom(ResolvedJavaType other) { return this.equals(other); } + + @Override + public ResolvedJavaMethod[] getDeclaredMethods(boolean link) { + return NO_METHODS; + } } From 5978ab700db341d576a6b041e4bd6ced3836c8b8 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Tue, 4 Mar 2025 11:09:00 +0100 Subject: [PATCH 2/6] Fix peekAtInterpreterVTable for dynamically loaded types --- .../src/com/oracle/svm/interpreter/InterpreterToVM.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java index ff8f674cfa9e..767b699d23d6 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterToVM.java @@ -707,12 +707,12 @@ private static CFunctionPointer getSVMVTableCodePointer(WordBase vtableEntry) { private static InterpreterResolvedJavaMethod peekAtInterpreterVTable(Class seedClass, Class thisClass, int vTableIndex, boolean isInvokeInterface) { ResolvedJavaType thisType; - if (DebuggerWithInterpreter.getValue()) { + if (RuntimeClassLoading.isSupported()) { + thisType = DynamicHub.fromClass(thisClass).getInterpreterType(); + } else { + assert DebuggerWithInterpreter.getValue(); DebuggerSupport interpreterSupport = ImageSingletons.lookup(DebuggerSupport.class); thisType = interpreterSupport.getUniverse().lookupType(thisClass); - } else { - assert RuntimeClassLoading.isSupported(); - throw VMError.unimplemented("obtain java type with vtable mirror"); } VMError.guarantee(thisType != null); VMError.guarantee(thisType instanceof InterpreterResolvedObjectType); From b098b686e9434f0137c8070a4fb348f1b0f80af5 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Sat, 7 Jun 2025 11:24:28 +0200 Subject: [PATCH 3/6] Espresso ClassfileParser: detect jdk.internal.ValueBased --- .../espresso/classfile/ClassfileParser.java | 21 ++++++++++++++++++- .../truffle/espresso/classfile/Constants.java | 3 ++- .../classfile/descriptors/ParserSymbols.java | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java index c4ce10892799..7ef3b4eae892 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java @@ -50,6 +50,7 @@ import static com.oracle.truffle.espresso.classfile.Constants.ACC_SUPER; import static com.oracle.truffle.espresso.classfile.Constants.ACC_SYNCHRONIZED; import static com.oracle.truffle.espresso.classfile.Constants.ACC_SYNTHETIC; +import static com.oracle.truffle.espresso.classfile.Constants.ACC_VALUE_BASED; import static com.oracle.truffle.espresso.classfile.Constants.ACC_VARARGS; import static com.oracle.truffle.espresso.classfile.Constants.ACC_VOLATILE; import static com.oracle.truffle.espresso.classfile.Constants.JVM_RECOGNIZED_CLASS_MODIFIERS; @@ -893,7 +894,7 @@ RuntimeVisibleAnnotationsAttribute parseRuntimeVisibleAnnotations(int attributeS flags = switch (location) { case Method -> parseMethodVMAnnotations(subStream); case Field -> parseFieldVMAnnotations(subStream); - case Class -> 0; + case Class -> parseClassVMAnnotations(subStream); }; } @@ -943,6 +944,20 @@ private int parseFieldVMAnnotations(ClassfileStream subStream) { } return flags; } + + private int parseClassVMAnnotations(ClassfileStream subStream) { + int flags = 0; + int count = subStream.readU2(); + for (int j = 0; j < count; j++) { + int typeIndex = parseAnnotation(subStream); + // Validation of the type is done at runtime by guest java code. + Symbol annotType = pool.utf8At(typeIndex, "annotation type"); + if (ParserTypes.jdk_internal_ValueBased.equals(annotType)) { + flags |= ACC_VALUE_BASED; + } + } + return flags; + } } @SuppressWarnings("unchecked") @@ -1248,6 +1263,10 @@ private Attribute[] parseClassAttributes() throws ValidationException { throw classFormatError("Duplicate PermittedSubclasses attribute"); } classAttributes[i] = permittedSubclasses = parsePermittedSubclasses(attributeName); + } else if (attributeName.equals(ParserNames.RuntimeVisibleAnnotations)) { + RuntimeVisibleAnnotationsAttribute annotations = commonAttributeParser.parseRuntimeVisibleAnnotations(attributeSize, AnnotationLocation.Class); + classFlags |= annotations.flags; + classAttributes[i] = annotations.attribute; } else { Attribute attr = commonAttributeParser.parseCommonAttribute(attributeName, attributeSize); // stream.skip(attributeSize); diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/Constants.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/Constants.java index 1c7f62dfc598..85e2ae1d28b2 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/Constants.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/Constants.java @@ -53,7 +53,6 @@ public final class Constants { // Not part of the spec, used internally by the VM. // Methods - public static final int ACC_FINALIZER = 0x00010000; public static final int ACC_FORCE_INLINE = 0x00020000; public static final int ACC_LAMBDA_FORM_COMPILED = 0x00040000; public static final int ACC_CALLER_SENSITIVE = 0x00080000; @@ -61,7 +60,9 @@ public final class Constants { public static final int ACC_SCOPED = 0x00200000; public static final int ACC_DONT_INLINE = 0x00400000; // Classes + public static final int ACC_FINALIZER = 0x00010000; public static final int ACC_IS_HIDDEN_CLASS = 0x04000000; // synchronized with JVM_ACC_IS_HIDDEN_CLASS + public static final int ACC_VALUE_BASED = 0x00020000; // Fields public static final int ACC_STABLE = 0x00010000; diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/ParserSymbols.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/ParserSymbols.java index a531f7b81490..996cd7323db4 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/ParserSymbols.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/ParserSymbols.java @@ -75,6 +75,7 @@ public static final class ParserTypes { public static final Symbol jdk_internal_vm_annotation_ForceInline = SYMBOLS.putType("Ljdk/internal/vm/annotation/ForceInline;"); public static final Symbol java_lang_invoke_DontInline = SYMBOLS.putType("Ljava/lang/invoke/DontInline;"); public static final Symbol jdk_internal_vm_annotation_DontInline = SYMBOLS.putType("Ljdk/internal/vm/annotation/DontInline;"); + public static final Symbol jdk_internal_ValueBased = SYMBOLS.putType("Ljdk/internal/ValueBased;"); // ScopedMemoryAccess public static final Symbol jdk_internal_misc_ScopedMemoryAccess$Scoped = SYMBOLS.putType("Ljdk/internal/misc/ScopedMemoryAccess$Scoped;"); From 8867b8e2922d1cff9378593fcd0dad67e7005e0a Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Mon, 16 Jun 2025 18:34:24 +0200 Subject: [PATCH 4/6] Add SymbolsSupport and SymbolsFeature --- .../descriptors/SignatureSymbols.java | 8 +- substratevm/mx.substratevm/mx_substratevm.py | 2 +- .../hub/registry/AbstractClassRegistry.java | 2 +- .../core/hub/registry/ClassRegistries.java | 30 ++----- .../svm/core/hub/registry/SymbolsSupport.java | 90 +++++++++++++++++++ .../svm/hosted/ClassRegistryFeature.java | 15 ++-- .../com/oracle/svm/hosted/SymbolsFeature.java | 52 +++++++++++ .../svm/interpreter/DebuggerFeature.java | 4 +- 8 files changed, 164 insertions(+), 39 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SymbolsFeature.java diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/SignatureSymbols.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/SignatureSymbols.java index 0f1d09b8dc54..ff3cd3bc8f28 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/SignatureSymbols.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/descriptors/SignatureSymbols.java @@ -140,7 +140,7 @@ public TypeSymbols getTypes() { * @return Array of Type symbols representing parameter types followed by return type */ public Symbol[] parsed(Symbol signature) { - return parse(SignatureSymbols.this.getTypes(), signature, 0); + return parse(SignatureSymbols.this.getTypes(), signature); } /** @@ -186,12 +186,12 @@ private static Symbol toBasic(Symbol type) { * @throws ParserException.ClassFormatError if {the signature is not well-formed */ @SuppressWarnings({"rawtypes", "unchecked"}) - static Symbol[] parse(TypeSymbols typeSymbols, Symbol signature, int startIndex) throws ParserException.ClassFormatError { - if ((startIndex > signature.length() - 3) || signature.byteAt(startIndex) != '(') { + public static Symbol[] parse(TypeSymbols typeSymbols, Symbol signature) throws ParserException.ClassFormatError { + if ((signature.length() < 3) || signature.byteAt(0) != '(') { throw new ParserException.ClassFormatError("Invalid method signature: " + signature); } final List> buf = new ArrayList<>(); - int i = startIndex + 1; + int i = 1; while (signature.byteAt(i) != ')') { final Symbol descriptor = typeSymbols.parse(signature, i, true); buf.add(descriptor); diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 76b3ca3f2096..ab9dcbf8b6e4 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1626,7 +1626,7 @@ def prevent_build_path_in_libgraal(): use_modules='image', jar_distributions=['substratevm:SVM_JDWP_SERVER'], build_args=libsvmjdwp_build_args + [ - '--features=com.oracle.svm.jdwp.server.ServerJDWPFeature', + '--features=com.oracle.svm.jdwp.server.ServerJDWPFeature,com.oracle.svm.hosted.SymbolsFeature', ], headers=False, ) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractClassRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractClassRegistry.java index a56aeadb0f65..aaefbc697da4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractClassRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractClassRegistry.java @@ -88,7 +88,7 @@ public final Class findLoadedClass(Symbol name) { @Platforms(Platform.HOSTED_ONLY.class) public final void addAOTType(Class cls) { assert !cls.isArray() && !cls.isPrimitive(); - TypeSymbols types = ClassRegistries.singleton().getTypes(); + TypeSymbols types = SymbolsSupport.getTypes(); ByteSequence typeBytes = ByteSequence.createTypeFromName(cls.getName()); Symbol key = types.getOrCreateValidType(typeBytes, true); assert key != null : typeBytes; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java index 40b80267d700..0bf7a8db17e4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java @@ -53,13 +53,9 @@ import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; import com.oracle.svm.espresso.classfile.descriptors.ModifiedUTF8; import com.oracle.svm.espresso.classfile.descriptors.Name; -import com.oracle.svm.espresso.classfile.descriptors.NameSymbols; -import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols; import com.oracle.svm.espresso.classfile.descriptors.Symbol; -import com.oracle.svm.espresso.classfile.descriptors.Symbols; import com.oracle.svm.espresso.classfile.descriptors.Type; import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; -import com.oracle.svm.espresso.classfile.descriptors.Utf8Symbols; import com.oracle.svm.espresso.classfile.perf.TimerCollection; import com.oracle.svm.util.ReflectionUtil; @@ -92,9 +88,6 @@ public final class ClassRegistries implements ParsingContext { @Platforms(Platform.HOSTED_ONLY.class)// private final ConcurrentHashMap buildTimeRegistries; - private final Utf8Symbols utf8; - private final NameSymbols names; - private final TypeSymbols types; private final AbstractClassRegistry bootRegistry; private final EconomicMap bootPackageToModule; @@ -105,13 +98,6 @@ public ClassRegistries() { } else { bootRegistry = new AOTClassRegistry(null); } - ParserSymbols.ensureInitialized(); - int initialSymbolTableCapacity = 4 * 1024; - Symbols symbols = Symbols.fromExisting(ParserSymbols.SYMBOLS.freeze(), initialSymbolTableCapacity, 0); - // let this resize when first used at runtime - utf8 = new Utf8Symbols(symbols); - names = new NameSymbols(symbols); - types = new TypeSymbols(symbols); buildTimeRegistries = new ConcurrentHashMap<>(); bootPackageToModule = computeBootPackageToModuleMap(); } @@ -149,10 +135,6 @@ public static String[] getSystemPackageNames() { return result; } - TypeSymbols getTypes() { - return types; - } - public static Class findBootstrapClass(String name) { try { return singleton().resolve(name, null); @@ -167,7 +149,7 @@ public static Class findLoadedClass(String name, ClassLoader loader) { return null; } ByteSequence typeBytes = ByteSequence.createTypeFromName(name); - Symbol type = singleton().getTypes().lookupValidType(typeBytes); + Symbol type = SymbolsSupport.getTypes().lookupValidType(typeBytes); Class result = null; if (type != null) { result = singleton().getRegistry(loader).findLoadedClass(type); @@ -285,7 +267,7 @@ private Class resolveInstanceType(String name, ClassLoader loader) throws Cla } private Class resolveInstanceType(ClassLoader loader, ByteSequence elementalType) throws ClassNotFoundException { - Symbol type = getTypes().getOrCreateValidType(elementalType); + Symbol type = SymbolsSupport.getTypes().getOrCreateValidType(elementalType); if (type == null) { return null; } @@ -321,7 +303,7 @@ public static Class defineClass(ClassLoader loader, String name, byte[] b, in throw sneakyThrow(new ClassNotFoundException(name)); } ByteSequence typeBytes = ByteSequence.createTypeFromName(name); - Symbol type = singleton().getTypes().getOrCreateValidType(typeBytes); + Symbol type = SymbolsSupport.getTypes().getOrCreateValidType(typeBytes); if (type == null) { throw new NoClassDefFoundError(name); } @@ -439,17 +421,17 @@ public Logger getLogger() { @Override public Symbol getOrCreateName(ByteSequence byteSequence) { - return ClassRegistries.singleton().names.getOrCreate(byteSequence); + return SymbolsSupport.getNames().getOrCreate(byteSequence); } @Override public Symbol getOrCreateTypeFromName(ByteSequence byteSequence) { - return ClassRegistries.singleton().getTypes().getOrCreateValidType(TypeSymbols.nameToType(byteSequence)); + return SymbolsSupport.getTypes().getOrCreateValidType(TypeSymbols.nameToType(byteSequence)); } @Override public Symbol getOrCreateUtf8(ByteSequence byteSequence) { // Note: all symbols are strong for now - return ClassRegistries.singleton().utf8.getOrCreateValidUtf8(byteSequence, true); + return SymbolsSupport.getUtf8().getOrCreateValidUtf8(byteSequence, true); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java new file mode 100644 index 000000000000..3acaeb1b9b33 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java @@ -0,0 +1,90 @@ +/* + * 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. 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.svm.core.hub.registry; + +import org.graalvm.nativeimage.ImageInfo; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.espresso.classfile.descriptors.NameSymbols; +import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols; +import com.oracle.svm.espresso.classfile.descriptors.SignatureSymbols; +import com.oracle.svm.espresso.classfile.descriptors.Symbols; +import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; +import com.oracle.svm.espresso.classfile.descriptors.Utf8Symbols; + +import jdk.graal.compiler.api.replacements.Fold; + +public final class SymbolsSupport { + @Platforms(Platform.HOSTED_ONLY.class) // + private static final SymbolsSupport TEST_SINGLETON = ImageInfo.inImageCode() ? null : new SymbolsSupport(); + + private final Utf8Symbols utf8; + private final NameSymbols names; + private final TypeSymbols types; + private final SignatureSymbols signatures; + + @Platforms(Platform.HOSTED_ONLY.class) + public SymbolsSupport() { + ParserSymbols.ensureInitialized(); + int initialSymbolTableCapacity = 4 * 1024; + Symbols symbols = Symbols.fromExisting(ParserSymbols.SYMBOLS.freeze(), initialSymbolTableCapacity, 0); + // let this resize when first used at runtime + utf8 = new Utf8Symbols(symbols); + names = new NameSymbols(symbols); + types = new TypeSymbols(symbols); + signatures = new SignatureSymbols(symbols, types); + } + + public static TypeSymbols getTypes() { + return singleton().types; + } + + public static SignatureSymbols getSignatures() { + return singleton().signatures; + } + + public static NameSymbols getNames() { + return singleton().names; + } + + public static Utf8Symbols getUtf8() { + return singleton().utf8; + } + + @Fold + public static SymbolsSupport singleton() { + if (TEST_SINGLETON != null) { + /* + * Some unit tests use com.oracle.svm.interpreter.metadata outside the context of + * native-image. + */ + assert !ImageInfo.inImageCode(); + return TEST_SINGLETON; + } + return ImageSingletons.lookup(SymbolsSupport.class); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java index 02ce91cf6ebe..80618a3cf8a1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java @@ -24,14 +24,14 @@ */ package com.oracle.svm.hosted; -import java.lang.reflect.Field; +import java.util.List; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.fieldvaluetransformer.NewInstanceFieldValueTransformer; import com.oracle.svm.core.hub.ClassForNameSupport; import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.hub.registry.ClassRegistries; @@ -43,6 +43,11 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { return ClassForNameSupport.respectClassLoader(); } + @Override + public List> getRequiredFeatures() { + return List.of(SymbolsFeature.class); + } + @Override public void afterRegistration(AfterRegistrationAccess access) { ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtBuildTime("com.oracle.svm.espresso.classfile", @@ -53,12 +58,6 @@ public void afterRegistration(AfterRegistrationAccess access) { public void beforeAnalysis(BeforeAnalysisAccess a) { FeatureImpl.BeforeAnalysisAccessImpl access = (FeatureImpl.BeforeAnalysisAccessImpl) a; access.registerSubtypeReachabilityHandler((unused, cls) -> onTypeReachable(cls), Object.class); - /* - * This works around issues when analysis concurrently scans the readWriteLock in - * SymbolsImpl and might add a Thread to the image heap. It could be generalized (GR-62530). - */ - Field readWriteLockField = access.findField("com.oracle.svm.espresso.classfile.descriptors.SymbolsImpl", "readWriteLock"); - access.registerFieldValueTransformer(readWriteLockField, new NewInstanceFieldValueTransformer()); } private static void onTypeReachable(Class cls) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SymbolsFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SymbolsFeature.java new file mode 100644 index 000000000000..ed0c469d277d --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SymbolsFeature.java @@ -0,0 +1,52 @@ +/* + * 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. 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.svm.hosted; + +import java.lang.reflect.Field; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.fieldvaluetransformer.NewInstanceFieldValueTransformer; +import com.oracle.svm.core.hub.registry.SymbolsSupport; + +public class SymbolsFeature implements InternalFeature { + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(SymbolsSupport.class, new SymbolsSupport()); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess a) { + FeatureImpl.BeforeAnalysisAccessImpl access = (FeatureImpl.BeforeAnalysisAccessImpl) a; + /* + * This works around issues when analysis concurrently scans the readWriteLock in + * SymbolsImpl and might add a Thread to the image heap. It could be generalized (GR-62530). + */ + Field readWriteLockField = access.findField("com.oracle.svm.espresso.classfile.descriptors.SymbolsImpl", "readWriteLock"); + access.registerFieldValueTransformer(readWriteLockField, new NewInstanceFieldValueTransformer()); + } +} diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java index 7d11a5ae4fa3..632fa838b8c0 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java @@ -77,6 +77,7 @@ import com.oracle.svm.graal.hosted.DeoptimizationFeature; import com.oracle.svm.hosted.FeatureImpl; import com.oracle.svm.hosted.NativeImageGenerator; +import com.oracle.svm.hosted.SymbolsFeature; import com.oracle.svm.hosted.code.CompileQueue; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; import com.oracle.svm.hosted.image.NativeImageHeap; @@ -151,7 +152,8 @@ public List> getRequiredFeatures() { return Arrays.asList( DeoptimizationFeature.class, InterpreterFeature.class, - IdentityMethodAddressResolverFeature.class); + IdentityMethodAddressResolverFeature.class, + SymbolsFeature.class); } private static Class getArgumentClass(GraphBuilderContext b, ResolvedJavaMethod targetMethod, int parameterIndex, ValueNode arg) { From 85207dac9605e991f7d5f09716d51978cb363300 Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 12 Jun 2025 15:08:36 +0200 Subject: [PATCH 5/6] Setup type and dispatch data for crema dynamic hubs * Setup type id * Setup open world type check slots * Setup dispatch tables (v&i-tables) * Setup layout encoding, identity hash & monitor offsets * Use unnamed module until modules are supported in crema Co-Authored-By: Thomas Garcia --- .../com/oracle/svm/core/hub/CremaSupport.java | 21 ++ .../com/oracle/svm/core/hub/DynamicHub.java | 106 ++++-- .../svm/core/hub/DynamicHubCompanion.java | 10 +- .../oracle/svm/core/hub/LayoutEncoding.java | 21 +- .../svm/core/hub/RuntimeClassLoading.java | 2 +- .../AbstractRuntimeClassRegistry.java | 156 +++++++- .../core/hub/registry/ClassRegistries.java | 16 +- .../svm/hosted/ClassRegistryFeature.java | 6 + .../oracle/svm/hosted/meta/HostedType.java | 15 + .../svm/hosted/meta/UniverseBuilder.java | 7 +- .../oracle/svm/hosted/meta/VTableBuilder.java | 36 +- .../metadata/CremaFieldAccess.java | 30 ++ .../metadata/CremaMethodAccess.java | 86 +++++ .../interpreter/metadata/CremaTypeAccess.java | 42 +++ .../metadata/InterpreterConstantPool.java | 31 +- .../InterpreterResolvedJavaField.java | 31 +- .../InterpreterResolvedJavaMethod.java | 119 +++++- .../metadata/InterpreterResolvedJavaType.java | 68 +++- .../InterpreterResolvedObjectType.java | 114 +++++- .../InterpreterResolvedPrimitiveType.java | 105 +++++- .../interpreter/metadata/WithModifiers.java | 113 ++++++ .../BuildTimeInterpreterUniverse.java | 18 +- .../oracle/svm/interpreter/CremaFeature.java | 23 +- .../svm/interpreter/CremaSupportImpl.java | 341 +++++++++++++++++- .../svm/interpreter/DebuggerFeature.java | 4 - .../svm/interpreter/DebuggerSupport.java | 38 +- .../svm/interpreter/InterpreterFeature.java | 4 + 27 files changed, 1417 insertions(+), 146 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaFieldAccess.java create mode 100644 substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java create mode 100644 substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaTypeAccess.java create mode 100644 substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/WithModifiers.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/CremaSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/CremaSupport.java index 66c9eecba03c..f330ed5c348f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/CremaSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/CremaSupport.java @@ -24,12 +24,33 @@ */ package com.oracle.svm.core.hub; +import java.util.List; + +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.espresso.classfile.ParserKlass; + import jdk.vm.ci.meta.ResolvedJavaType; public interface CremaSupport { @Platforms(Platform.HOSTED_ONLY.class) ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType analysisType); + + int getAfterFieldsOffset(DynamicHub hub); + + interface CremaDispatchTable { + int vtableLength(); + + int itableLength(Class iface); + } + + CremaDispatchTable getDispatchTable(ParserKlass parsed, Class superClass, List> superInterfaces); + + void fillDynamicHubInfo(DynamicHub hub, CremaDispatchTable table, List> transitiveSuperInterfaces, int[] interfaceIndices); + + static CremaSupport singleton() { + return ImageSingletons.lookup(CremaSupport.class); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index d8bad63d0146..0716ffe8da8b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -153,6 +153,7 @@ import jdk.internal.reflect.FieldAccessor; import jdk.internal.reflect.Reflection; import jdk.internal.reflect.ReflectionFactory; +import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaType; import sun.reflect.annotation.AnnotationType; import sun.reflect.generics.factory.GenericsFactory; @@ -432,47 +433,92 @@ public DynamicHub(Class hostedJavaClass, String name, byte hubType, Reference */ @NeverInline("Fields of DynamicHub are immutable. Immutable reads could float above ANY_LOCATION writes.") public static DynamicHub allocate(String name, DynamicHub superHub, Object interfacesEncoding, DynamicHub componentHub, String sourceFileName, - int modifiers, short flags, ClassLoader classLoader, Class nestHost, String simpleBinaryName, - Object declaringClass, String signature) { + int modifiers, short flags, ClassLoader classLoader, Class nestHost, String simpleBinaryName, Module module, + Object declaringClass, String signature, int typeID, + short numClassTypes, + short typeIDDepth, + short numInterfacesTypes, + int[] openTypeWorldTypeCheckSlots, int vTableEntries, + int afterFieldsOffset, boolean valueBased) { VMError.guarantee(RuntimeClassLoading.isSupported()); ReferenceType referenceType = ReferenceType.computeReferenceType(DynamicHub.toClass(superHub)); - // GR-59683: HubType.OBJECT_ARRAY? - byte hubType = HubType.INSTANCE; - if (referenceType != ReferenceType.None) { - hubType = HubType.REFERENCE_INSTANCE; + byte hubType; + if (componentHub != null) { + if (componentHub.isPrimitive()) { + hubType = HubType.PRIMITIVE_ARRAY; + } else { + hubType = HubType.OBJECT_ARRAY; + } + } else { + if (referenceType == ReferenceType.None) { + hubType = HubType.INSTANCE; + } else { + hubType = HubType.REFERENCE_INSTANCE; + } } - // GR-62339 - Module module = null; - - // GR-59683: Setup interpreter metadata at run-time. - ResolvedJavaType interpreterType = null; - - DynamicHubCompanion companion = DynamicHubCompanion.createAtRuntime(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature, - interpreterType); + DynamicHubCompanion companion = DynamicHubCompanion.createAtRuntime(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature); /* Always allow unsafe allocation for classes that were loaded at run-time. */ companion.canUnsafeAllocate = true; - // GR-59687: Correct size and content for vtable - int vTableEntries = 0x100; companion.classInitializationInfo = new ClassInitializationInfo(false); - // GR-60069: Determine size for instance and offsets for monitor and identityHashCode - int layoutEncoding = 0x40; - char monitorOffset = 0; - char identityHashOffset = 0; + assert !isFlagSet(flags, IS_PRIMITIVE_FLAG_BIT); + boolean isInterface = isFlagSet(flags, IS_INTERFACE_FLAG_BIT); + int layoutEncoding; + int monitorOffset = 0; + int identityHashOffset = 0; + + // See also similar logic in UniverseBuilder.buildHubs + ObjectLayout ol = ConfigurationValues.getObjectLayout(); + if (componentHub != null) { + // array + JavaKind componentKind = JavaKind.fromJavaClass(DynamicHub.toClass(componentHub)); + boolean isObject = (componentKind == JavaKind.Object); + layoutEncoding = LayoutEncoding.forArray(isObject, ol.getArrayBaseOffset(componentKind), ol.getArrayIndexShift(componentKind)); + if (ol.isIdentityHashFieldInObjectHeader() || ol.isIdentityHashFieldAtTypeSpecificOffset()) { + identityHashOffset = NumUtil.safeToInt(ol.getObjectHeaderIdentityHashOffset()); + } + } else if (isInterface) { + layoutEncoding = LayoutEncoding.forInterface(); + } else { + // instance class + assert !"java.lang.Class".equals(name); + // @Hybrid is ignored + if (Modifier.isAbstract(modifiers)) { + layoutEncoding = LayoutEncoding.forAbstract(); + } else { + int instanceSize = afterFieldsOffset; + + boolean needsMonitorOffset = !valueBased; + if (needsMonitorOffset) { + // GR-60069 could look for gaps + int size = ol.getReferenceSize(); + int bits = size - 1; + int alignmentAdjust = ((instanceSize + bits) & ~bits) - instanceSize; + monitorOffset = instanceSize + alignmentAdjust; + instanceSize = monitorOffset + size; + } - // GR-59687: Determine typecheck related infos - int typeID = 0; - short typeIDDepth = 0; - short numClassTypes = 2; - short numInterfacesTypes = 0; - int[] openTypeWorldTypeCheckSlots = new int[numClassTypes + (numInterfacesTypes * 2)]; + if (ol.isIdentityHashFieldInObjectHeader()) { + identityHashOffset = ol.getObjectHeaderIdentityHashOffset(); + } else if (ol.isIdentityHashFieldAtTypeSpecificOffset() || ol.isIdentityHashFieldOptional()) { + // GR-60069 could look for gaps + int bits = Integer.BYTES - 1; + int alignmentAdjust = ((instanceSize + bits) & ~bits) - instanceSize; + identityHashOffset = instanceSize + alignmentAdjust; + instanceSize = identityHashOffset + Integer.BYTES; + } else { + throw VMError.shouldNotReachHere("Unexpected identity hash mode"); + } + layoutEncoding = LayoutEncoding.forPureInstance(ol.alignUp(instanceSize)); + } + } companion.interfacesEncoding = interfacesEncoding; - // GR-59683: Proper value needed. + // GR-57813: setup a LazyFinalReference that calls `values` via reflection. companion.enumConstantsReference = null; /* @@ -519,8 +565,10 @@ public static DynamicHub allocate(String name, DynamicHub superHub, Object inter writeShort(hub, dynamicHubOffsets.getNumInterfaceTypesOffset(), numInterfacesTypes); writeObject(hub, dynamicHubOffsets.getOpenTypeWorldTypeCheckSlotsOffset(), openTypeWorldTypeCheckSlots); - writeChar(hub, dynamicHubOffsets.getMonitorOffsetOffset(), monitorOffset); - writeChar(hub, dynamicHubOffsets.getIdentityHashOffsetOffset(), identityHashOffset); + VMError.guarantee(monitorOffset == (char) monitorOffset); + VMError.guarantee(identityHashOffset == (char) identityHashOffset); + writeChar(hub, dynamicHubOffsets.getMonitorOffsetOffset(), (char) monitorOffset); + writeChar(hub, dynamicHubOffsets.getIdentityHashOffsetOffset(), (char) identityHashOffset); writeShort(hub, dynamicHubOffsets.getFlagsOffset(), flags); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java index 3cc715c0f262..ba874f6661b2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java @@ -149,18 +149,17 @@ public final class DynamicHubCompanion { static DynamicHubCompanion createHosted(Module module, DynamicHub superHub, String sourceFileName, int modifiers, Object classLoader, Class nestHost, String simpleBinaryName, Object declaringClass, String signature) { - return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature, null); + return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature); } static DynamicHubCompanion createAtRuntime(Module module, DynamicHub superHub, String sourceFileName, int modifiers, - ClassLoader classLoader, Class nestHost, String simpleBinaryName, Object declaringClass, String signature, - ResolvedJavaType interpreterType) { + ClassLoader classLoader, Class nestHost, String simpleBinaryName, Object declaringClass, String signature) { assert RuntimeClassLoading.isSupported(); - return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature, interpreterType); + return new DynamicHubCompanion(module, superHub, sourceFileName, modifiers, classLoader, nestHost, simpleBinaryName, declaringClass, signature); } private DynamicHubCompanion(Module module, DynamicHub superHub, String sourceFileName, int modifiers, - Object classLoader, Class nestHost, String simpleBinaryName, Object declaringClass, String signature, ResolvedJavaType interpreterType) { + Object classLoader, Class nestHost, String simpleBinaryName, Object declaringClass, String signature) { this.module = module; this.superHub = superHub; this.sourceFileName = sourceFileName; @@ -171,6 +170,5 @@ private DynamicHubCompanion(Module module, DynamicHub superHub, String sourceFil this.signature = signature; this.classLoader = classLoader; - this.interpreterType = interpreterType; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java index 8e310acac334..2de4ef47e337 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/LayoutEncoding.java @@ -118,7 +118,7 @@ private static void guaranteeEncoding(ResolvedJavaType type, boolean expected, b @Platforms(Platform.HOSTED_ONLY.class) public static int forPureInstance(ResolvedJavaType type, int size) { guaranteeEncoding(type, true, size > LAST_SPECIAL_VALUE, "Instance type size must be above special values for encoding: " + size); - int encoding = size; + int encoding = forPureInstance(size); guaranteeEncoding(type, true, isPureInstance(encoding), "Instance type encoding denotes an instance"); guaranteeEncoding(type, false, isArray(encoding) || isArrayLike(encoding), "Instance type encoding denotes an array-like object"); guaranteeEncoding(type, false, isHybrid(encoding), "Instance type encoding denotes a hybrid"); @@ -128,11 +128,20 @@ public static int forPureInstance(ResolvedJavaType type, int size) { return encoding; } + public static int forPureInstance(int size) { + assert size > LAST_SPECIAL_VALUE; + return size; + } + @Platforms(Platform.HOSTED_ONLY.class) public static int forArray(ResolvedJavaType type, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { return forArrayLike(type, false, objectElements, arrayBaseOffset, arrayIndexShift); } + public static int forArray(boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { + return forArrayLike(false, objectElements, arrayBaseOffset, arrayIndexShift); + } + @Platforms(Platform.HOSTED_ONLY.class) public static int forHybrid(ResolvedJavaType type, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { return forArrayLike(type, true, objectElements, arrayBaseOffset, arrayIndexShift); @@ -146,9 +155,7 @@ public static int forDynamicHub(ResolvedJavaType type, int vtableOffset, int vta @Platforms(Platform.HOSTED_ONLY.class) private static int forArrayLike(ResolvedJavaType type, boolean isHybrid, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { assert isHybrid != type.isArray(); - int tag = isHybrid ? (objectElements ? ARRAY_TAG_HYBRID_OBJECT_VALUE : ARRAY_TAG_HYBRID_PRIMITIVE_VALUE) - : (objectElements ? ARRAY_TAG_OBJECT_VALUE : ARRAY_TAG_PRIMITIVE_VALUE); - int encoding = (tag << ARRAY_TAG_SHIFT) | (arrayBaseOffset << ARRAY_BASE_SHIFT) | (arrayIndexShift << ARRAY_INDEX_SHIFT_SHIFT); + int encoding = forArrayLike(isHybrid, objectElements, arrayBaseOffset, arrayIndexShift); guaranteeEncoding(type, true, isArrayLike(encoding), "Array-like object encoding denotes an array-like object"); guaranteeEncoding(type, !isHybrid, isArray(encoding), "Encoding denotes an array"); @@ -165,6 +172,12 @@ private static int forArrayLike(ResolvedJavaType type, boolean isHybrid, boolean return encoding; } + private static int forArrayLike(boolean isHybrid, boolean objectElements, int arrayBaseOffset, int arrayIndexShift) { + int tag = isHybrid ? (objectElements ? ARRAY_TAG_HYBRID_OBJECT_VALUE : ARRAY_TAG_HYBRID_PRIMITIVE_VALUE) + : (objectElements ? ARRAY_TAG_OBJECT_VALUE : ARRAY_TAG_PRIMITIVE_VALUE); + return (tag << ARRAY_TAG_SHIFT) | (arrayBaseOffset << ARRAY_BASE_SHIFT) | (arrayIndexShift << ARRAY_INDEX_SHIFT_SHIFT); + } + public static boolean isPrimitive(int encoding) { return encoding == PRIMITIVE_VALUE; } 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 c4c5a9f28d97..0210a5da8df1 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 @@ -256,7 +256,7 @@ public String toString() { } public static ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType analysisType) { - return ImageSingletons.lookup(CremaSupport.class).createInterpreterType(hub, analysisType); + return CremaSupport.singleton().createInterpreterType(hub, analysisType); } public static void ensureLinked(DynamicHub dynamicHub) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java index e8877beb3486..0f666abd7fba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java @@ -25,12 +25,16 @@ package com.oracle.svm.core.hub.registry; import static com.oracle.svm.espresso.classfile.Constants.ACC_SUPER; +import static com.oracle.svm.espresso.classfile.Constants.ACC_VALUE_BASED; import static com.oracle.svm.espresso.classfile.Constants.JVM_ACC_WRITTEN_FLAGS; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -38,6 +42,8 @@ import org.graalvm.nativeimage.impl.ClassLoading; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.graal.meta.KnownOffsets; +import com.oracle.svm.core.hub.CremaSupport; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.hub.RuntimeClassLoading.ClassDefinitionInfo; @@ -47,7 +53,9 @@ import com.oracle.svm.espresso.classfile.ClassfileStream; import com.oracle.svm.espresso.classfile.ParserConstantPool; import com.oracle.svm.espresso.classfile.ParserException; +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.InnerClassesAttribute; import com.oracle.svm.espresso.classfile.attributes.NestHostAttribute; import com.oracle.svm.espresso.classfile.attributes.PermittedSubclassesAttribute; @@ -226,6 +234,30 @@ private ParserKlass parseClass(Symbol typeOrNull, ClassDefinitionInfo info } } + private static List> transitiveSuperInterfaces(Class superClass, Class[] superInterfaces) { + HashSet> result = new HashSet<>(); + Class current = superClass; + while (current != null) { + for (Class interfaceClass : current.getInterfaces()) { + collectInterfaces(interfaceClass, result); + } + current = current.getSuperclass(); + } + for (Class interfaceClass : superInterfaces) { + collectInterfaces(interfaceClass, result); + } + return new ArrayList<>(result); + } + + private static void collectInterfaces(Class interfaceClass, HashSet> result) { + // note that this is and must be called only _after_ class circularity detection + if (result.add(interfaceClass)) { + for (Class superInterface : interfaceClass.getInterfaces()) { + collectInterfaces(superInterface, result); + } + } + } + private Class createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbol type) { Symbol superKlassType = parsed.getSuperKlass(); assert superKlassType != null; // j.l.Object is always AOT @@ -261,13 +293,8 @@ private Class createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbo boolean isRecord = Modifier.isFinal(modifiers) && superClass == Record.class && parsed.getAttribute(RecordAttribute.NAME) != null; // GR-62320 This should be set based on build-time and run-time arguments. boolean assertionsEnabled = true; - // GR-59687 itable setup should set this - boolean declaresDefaultMethods = false; - boolean hasDefaultMethods = declaresDefaultMethods || hasInheritedDefaultMethods(superClass, superInterfaces); boolean isSealed = isSealed(parsed); - short flags = DynamicHub.makeFlags(false, isInterface, info.isHidden(), isRecord, assertionsEnabled, hasDefaultMethods, declaresDefaultMethods, isSealed, false, false, false, false); - Object interfacesEncoding = null; if (superInterfaces.length == 1) { interfacesEncoding = DynamicHub.fromClass(superInterfaces[0]); @@ -279,12 +306,127 @@ private Class createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbo interfacesEncoding = superHubs; } - DynamicHub hub = DynamicHub.allocate(externalName, DynamicHub.fromClass(superClass), interfacesEncoding, null, - sourceFile, modifiers, flags, getClassLoader(), nestHost, simpleBinaryName, enclosingClass, classSignature); + List> transitiveSuperInterfaces = transitiveSuperInterfaces(superClass, superInterfaces); + transitiveSuperInterfaces.sort(Comparator.comparing(c -> DynamicHub.fromClass(c).getTypeID())); + + CremaSupport.CremaDispatchTable dispatchTable = CremaSupport.singleton().getDispatchTable(parsed, superClass, transitiveSuperInterfaces); + + boolean declaresDefaultMethods = isInterface && declaresDefaultMethods(parsed); + boolean hasDefaultMethods = declaresDefaultMethods || hasInheritedDefaultMethods(superClass, superInterfaces); + + boolean isLambdaFormHidden = false; + boolean isProxyClass = false; + short flags = DynamicHub.makeFlags(false, isInterface, info.isHidden(), isRecord, assertionsEnabled, hasDefaultMethods, declaresDefaultMethods, isSealed, false, isLambdaFormHidden, false, + isProxyClass); + + /* + * The dispatch table will look like: + * @formatter:off + * [vtable..., itable(I1)..., itable(I2)...] + * ^ idx1 ^ idx2 + * @formatter:on + * First compute idx* in interfaceIndices + */ + int dispatchTableLength = dispatchTable.vtableLength(); + int[] interfaceIndices = new int[transitiveSuperInterfaces.size()]; + int i = 0; + for (Class iface : transitiveSuperInterfaces) { + interfaceIndices[i++] = dispatchTableLength; + dispatchTableLength += dispatchTable.itableLength(iface); + } + /* + * Compute the type check slots depending on the kind of type + * @formatter:off + * ## Instance types + * [Object.id, Super1.id, ..., Current.id, I1.id, off1, I2.id, off2, ...] + * - display with all super classes from Object to self (included) + * - followed by transitive interfaces (ordered by type id) + * - each interface is followed by its itable offset + * ## Interface types + * [Object.id, I1.id, bad, I2.id, bad] + * - display with Object + * - followed by transitive interfaces (ordered by type id, including self) + * - using 0xBADD0D1DL as interface starting index + * @formatter:on + */ + DynamicHub superHub = DynamicHub.fromClass(superClass); + int typeID = ClassRegistries.nextTypeId(); + short numInterfacesTypes = (short) transitiveSuperInterfaces.size(); + short numClassTypes; + short typeIDDepth; + if (isInterface) { + assert superHub.getNumClassTypes() == 1; + typeIDDepth = -1; + numClassTypes = 1; + } else { + int intDepth = superHub.getTypeIDDepth() + 1; + int intNumClassTypes = superHub.getNumClassTypes() + 1; + VMError.guarantee(intDepth == (short) intDepth, "Type depth overflow"); + VMError.guarantee(intNumClassTypes == (short) intNumClassTypes, "Num class types overflow"); + typeIDDepth = (short) intDepth; + numClassTypes = (short) intNumClassTypes; + } + int[] openTypeWorldTypeCheckSlots = new int[numClassTypes + (numInterfacesTypes * 2)]; + int[] superOpenTypeWorldTypeCheckSlots = superHub.getOpenTypeWorldTypeCheckSlots(); + System.arraycopy(superOpenTypeWorldTypeCheckSlots, 0, openTypeWorldTypeCheckSlots, 0, superHub.getNumClassTypes()); + if (!isInterface) { + openTypeWorldTypeCheckSlots[numClassTypes - 1] = typeID; + } + + i = 0; + long vTableBaseOffset = KnownOffsets.singleton().getVTableBaseOffset(); + long vTableEntrySize = KnownOffsets.singleton().getVTableEntrySize(); + for (Class superInterface : transitiveSuperInterfaces) { + openTypeWorldTypeCheckSlots[i * 2 + numClassTypes] = DynamicHub.fromClass(superInterface).getTypeID(); + int offset; + if (isInterface) { + offset = 0xBADD0D1D; + } else { + offset = Math.toIntExact(vTableBaseOffset + interfaceIndices[i] * vTableEntrySize); + } + openTypeWorldTypeCheckSlots[i * 2 + numClassTypes + 1] = offset; + i += 1; + } + + int afterFieldsOffset; + if (isInterface) { + afterFieldsOffset = 0; + } else { + int superAfterFieldsOffset = CremaSupport.singleton().getAfterFieldsOffset(superHub); + // GR-60069: field layout + int numDeclaredInstanceFields = 0; + for (ParserField field : parsed.getFields()) { + if (!field.isStatic()) { + numDeclaredInstanceFields += 1; + } + } + assert numDeclaredInstanceFields == 0; + afterFieldsOffset = Math.toIntExact(superAfterFieldsOffset); + } + boolean isValueBased = (parsed.getFlags() & ACC_VALUE_BASED) != 0; + + // GR-62339 + Module module = getClassLoader().getUnnamedModule(); + + DynamicHub hub = DynamicHub.allocate(externalName, superHub, interfacesEncoding, null, + sourceFile, modifiers, flags, getClassLoader(), nestHost, simpleBinaryName, module, enclosingClass, classSignature, + typeID, numClassTypes, typeIDDepth, numInterfacesTypes, openTypeWorldTypeCheckSlots, dispatchTableLength, afterFieldsOffset, isValueBased); + + CremaSupport.singleton().fillDynamicHubInfo(hub, dispatchTable, transitiveSuperInterfaces, interfaceIndices); return DynamicHub.toClass(hub); } + private static boolean declaresDefaultMethods(ParserKlass parsed) { + for (ParserMethod method : parsed.getMethods()) { + int flags = method.getFlags(); + if (!Modifier.isAbstract(flags) && !Modifier.isStatic(flags)) { + return true; + } + } + return false; + } + private static boolean isSealed(ParserKlass parsed) { PermittedSubclassesAttribute permittedSubclasses = (PermittedSubclassesAttribute) parsed.getAttribute(PermittedSubclassesAttribute.NAME); return permittedSubclasses != null && permittedSubclasses.getClasses().length > 0; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java index 0bf7a8db17e4..0a0ef8dfa1c5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/ClassRegistries.java @@ -28,6 +28,7 @@ import java.lang.reflect.Field; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import org.graalvm.collections.EconomicMap; @@ -91,6 +92,8 @@ public final class ClassRegistries implements ParsingContext { private final AbstractClassRegistry bootRegistry; private final EconomicMap bootPackageToModule; + private final AtomicInteger nextTypeId = new AtomicInteger(0); + @Platforms(Platform.HOSTED_ONLY.class) public ClassRegistries() { if (RuntimeClassLoading.isSupported()) { @@ -116,7 +119,7 @@ private static EconomicMap computeBootPackageToModuleMap() { } @Fold - static ClassRegistries singleton() { + public static ClassRegistries singleton() { return ImageSingletons.lookup(ClassRegistries.class); } @@ -135,6 +138,17 @@ public static String[] getSystemPackageNames() { return result; } + @Platforms(Platform.HOSTED_ONLY.class) + public static void setStartingTypeId(int id) { + singleton().nextTypeId.set(id); + } + + public static int nextTypeId() { + int nextTypeId = singleton().nextTypeId.getAndIncrement(); + VMError.guarantee(nextTypeId > 0); + return nextTypeId; + } + public static Class findBootstrapClass(String name) { try { return singleton().resolve(name, null); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java index 80618a3cf8a1..f6ebcb659997 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ClassRegistryFeature.java @@ -33,6 +33,7 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.hub.ClassForNameSupport; +import com.oracle.svm.core.hub.DynamicHubSupport; import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.hub.registry.ClassRegistries; @@ -68,4 +69,9 @@ private static void onTypeReachable(Class cls) { ClassRegistries.addAOTClass(ClassLoaderFeature.getRuntimeClassLoader(cls.getClassLoader()), cls); } } + + @Override + public void afterCompilation(AfterCompilationAccess access) { + ClassRegistries.setStartingTypeId(DynamicHubSupport.currentLayer().getMaxTypeId() + 1); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index 25e5709110dd..06e4ea72d724 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -32,6 +32,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.util.VMError; @@ -102,6 +103,11 @@ public abstract class HostedType extends HostedElement implements SharedType, Wr * Flattened array of all dispatch tables methods installed in the hub for this type. */ protected HostedMethod[] openTypeWorldDispatchTables; + /** + * The dispatch table metadata used by Crema: for interfaces, the i-table prototype; for + * abstract types, the vtable; for other types, the dispatch table (v- & i-tables). + */ + protected HostedMethod[] cremaOpenTypeWorldDispatchTables; /** * Used for tracking original call targets contained within the dispatch table. This is in * contrast with {@link #openTypeWorldDispatchTables}, which contains the resolved methods for @@ -171,6 +177,11 @@ public HostedMethod[] getOpenTypeWorldDispatchTables() { return openTypeWorldDispatchTables; } + public HostedMethod[] getCremaOpenTypeWorldDispatchTables() { + assert cremaOpenTypeWorldDispatchTables != null : this; + return cremaOpenTypeWorldDispatchTables; + } + public HostedMethod[] getOpenTypeWorldDispatchTableSlotTargets() { assert openTypeWorldDispatchTableSlotTargets != null; return openTypeWorldDispatchTableSlotTargets; @@ -180,6 +191,10 @@ public HostedMethod[] getVTable() { return SubstrateOptions.useClosedTypeWorldHubLayout() ? getClosedTypeWorldVTable() : getOpenTypeWorldDispatchTables(); } + public HostedMethod[] getInterpreterDispatchTable() { + return RuntimeClassLoading.isSupported() ? getCremaOpenTypeWorldDispatchTables() : getVTable(); + } + @Override public int getTypeID() { assert typeID != INVALID_TYPECHECK_ID; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index c7251c740b8b..1ea83805f2c6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -597,8 +597,10 @@ private void layoutInstanceFields(HostedInstanceClass clazz, HostedField[] super // Reserve "synthetic" fields in this class (but not subclasses) below. - // A reference to a {@link java.util.concurrent.locks.ReentrantLock for "synchronized" or - // Object.wait() and Object.notify() and friends. + /* + * A reference to a JavaMonitor instance for "synchronized" or Object.wait() and + * Object.notify() and friends. + */ if (clazz.needMonitorField()) { int size = layout.getReferenceSize(); int endOffset = usedBytes.length(); @@ -912,6 +914,7 @@ private void buildHubs() { for (HostedType type : hUniverse.getTypes()) { hUniverse.hostVM().recordActivity(); + // See also similar logic in DynamicHub.allocate int layoutHelper; int monitorOffset = 0; int identityHashOffset = 0; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java index 73572cdf63f6..3867d5fdd27a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/VTableBuilder.java @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.meta; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; @@ -39,6 +40,7 @@ import com.oracle.svm.core.InvalidMethodPointerHandler; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.hosted.imagelayer.LayeredDispatchTableFeature; @@ -290,6 +292,24 @@ private void generateOpenTypeWorldDispatchTable(HostedInstanceClass type, Map sourceTable; + if (type.isAbstract()) { + sourceTable = resultClassTableMethods; + } else { + sourceTable = Arrays.asList(type.openTypeWorldDispatchTableSlotTargets); + } + type.cremaOpenTypeWorldDispatchTables = new HostedMethod[sourceTable.size()]; + for (int i = 0; i < sourceTable.size(); i++) { + HostedMethod resultMethod = sourceTable.get(i); + var resolvedMethod = (HostedMethod) type.resolveConcreteMethod(resultMethod, type); + if (resolvedMethod != null) { + resultMethod = resolvedMethod; + } + type.cremaOpenTypeWorldDispatchTables[i] = resultMethod; + } + } for (HostedType subType : type.subTypes) { if (subType instanceof HostedInstanceClass instanceClass && openHubUtils.shouldIncludeType(subType)) { @@ -307,7 +327,14 @@ private void buildOpenTypeWorldDispatchTables() { * looking at their declared methods. */ if (type.isInterface() && openHubUtils.shouldIncludeType(type)) { - dispatchTablesMap.put(type, generateITable(type)); + List itable = generateITable(type); + dispatchTablesMap.put(type, itable); + if (RuntimeClassLoading.isSupported()) { + type.cremaOpenTypeWorldDispatchTables = new HostedMethod[itable.size()]; + for (int i = 0; i < itable.size(); i++) { + type.cremaOpenTypeWorldDispatchTables[i] = itable.get(i); + } + } } } @@ -319,6 +346,7 @@ private void buildOpenTypeWorldDispatchTables() { for (HostedType type : hUniverse.getTypes()) { if (type.isArray() && openHubUtils.shouldIncludeType(type)) { type.openTypeWorldDispatchTables = objectType.openTypeWorldDispatchTables; + type.cremaOpenTypeWorldDispatchTables = objectType.cremaOpenTypeWorldDispatchTables; type.openTypeWorldDispatchTableSlotTargets = objectType.openTypeWorldDispatchTableSlotTargets; type.itableStartingOffsets = objectType.itableStartingOffsets; if (openHubUtils.shouldRegisterType(type)) { @@ -331,6 +359,12 @@ private void buildOpenTypeWorldDispatchTables() { type.openTypeWorldDispatchTableSlotTargets = HostedMethod.EMPTY_ARRAY; type.itableStartingOffsets = emptyITableOffsets; } + if (RuntimeClassLoading.isSupported()) { + if (type.isPrimitive()) { + type.cremaOpenTypeWorldDispatchTables = HostedMethod.EMPTY_ARRAY; + } + assert type.cremaOpenTypeWorldDispatchTables != null : "No dispatch tables for type " + type; + } } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaFieldAccess.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaFieldAccess.java new file mode 100644 index 000000000000..770816c81447 --- /dev/null +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaFieldAccess.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. 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.svm.interpreter.metadata; + +import com.oracle.svm.espresso.shared.meta.FieldAccess; + +public interface CremaFieldAccess extends WithModifiers, FieldAccess { +} diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java new file mode 100644 index 000000000000..147a8f776363 --- /dev/null +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaMethodAccess.java @@ -0,0 +1,86 @@ +/* + * 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. 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.svm.interpreter.metadata; + +import java.util.List; + +import com.oracle.svm.espresso.classfile.attributes.LineNumberTableAttribute; +import com.oracle.svm.espresso.classfile.descriptors.ByteSequence; +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; +import com.oracle.svm.espresso.shared.meta.MethodAccess; + +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.LineNumberTable; +import jdk.vm.ci.meta.UnresolvedJavaType; + +public interface CremaMethodAccess extends WithModifiers, MethodAccess { + static LineNumberTable toJVMCI(LineNumberTableAttribute parserTable) { + List entries = parserTable.getEntries(); + int[] bcis = new int[entries.size()]; + int[] lineNumbers = new int[entries.size()]; + for (int i = 0; i < entries.size(); i++) { + LineNumberTableAttribute.Entry entry = entries.get(i); + bcis[i] = entry.getBCI(); + lineNumbers[i] = entry.getLineNumber(); + } + return new LineNumberTable(lineNumbers, bcis); + } + + static InterpreterUnresolvedSignature toJVMCI(Symbol parserSignature, TypeSymbols typeSymbols) { + Symbol[] parsed = SignatureSymbols.parse(typeSymbols, parserSignature); + assert parsed.length > 0; + int parameterCount = SignatureSymbols.parameterCount(parsed); + JavaType[] parameters = new JavaType[parameterCount]; + for (int i = 0; i < parameterCount; i++) { + parameters[i] = toJavaType(SignatureSymbols.parameterType(parsed, i)); + } + JavaType returnType = toJavaType(SignatureSymbols.returnType(parsed)); + return InterpreterUnresolvedSignature.create(returnType, parameters); + } + + static Symbol toSymbol(InterpreterUnresolvedSignature jvmciSignature, SignatureSymbols signatures) { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (int i = 0; i < jvmciSignature.getParameterCount(false); i++) { + sb.append(jvmciSignature.getParameterType(i, null).getName()); + } + sb.append(')'); + sb.append(jvmciSignature.getReturnType(null).getName()); + return signatures.getOrCreateValidSignature(ByteSequence.create(sb.toString())); + } + + static JavaType toJavaType(Symbol typeSymbol) { + if (TypeSymbols.isPrimitive(typeSymbol)) { + return InterpreterResolvedPrimitiveType.fromKind(JavaKind.fromPrimitiveOrVoidTypeChar((char) typeSymbol.byteAt(0))); + } else { + return UnresolvedJavaType.create(typeSymbol.toString()); + } + } +} diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaTypeAccess.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaTypeAccess.java new file mode 100644 index 000000000000..03f81f16fec4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/CremaTypeAccess.java @@ -0,0 +1,42 @@ +/* + * 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. 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.svm.interpreter.metadata; + +import com.oracle.svm.core.hub.registry.SymbolsSupport; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.classfile.descriptors.Type; +import com.oracle.svm.espresso.shared.meta.TypeAccess; + +public interface CremaTypeAccess extends WithModifiers, TypeAccess { + static Symbol jvmciNameToType(String name) { + // hidden classes and SVM stable proxy name contain a `.` + String typeString = name.replace('.', '+'); + Symbol type = SymbolsSupport.getTypes().getOrCreateValidType(typeString); + if (type == null) { + throw new IllegalArgumentException("Invalid type name: " + name); + } + return 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 c059236e00a1..47ec6cf8ac43 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,11 +26,15 @@ import static com.oracle.svm.interpreter.metadata.Bytecodes.INVOKEDYNAMIC; +import java.util.List; + import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.espresso.classfile.ParserConstantPool; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; import com.oracle.svm.interpreter.metadata.serialization.VisibleForSerialization; import jdk.vm.ci.meta.ConstantPool; @@ -41,15 +45,13 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.Signature; -import java.util.List; - -public final class InterpreterConstantPool implements ConstantPool { +public final class InterpreterConstantPool extends com.oracle.svm.espresso.classfile.ConstantPool implements ConstantPool { private final InterpreterResolvedObjectType holder; // Assigned after analysis. @UnknownObjectField(types = Object[].class) private Object[] entries; - Object at(int cpi) { + Object objAt(int cpi) { if (cpi == 0) { // 0 implies unknown (!= unresolved) e.g. unknown class, field, method ... // In this case it's not possible to even provide a name or symbolic representation for @@ -62,6 +64,7 @@ Object at(int cpi) { } private InterpreterConstantPool(InterpreterResolvedObjectType holder, Object[] entries) { + super(new byte[]{}, new int[]{}, Symbol.EMPTY_ARRAY, 0, 0); this.holder = MetadataUtil.requireNonNull(holder); this.entries = MetadataUtil.requireNonNull(entries); } @@ -78,12 +81,12 @@ public int length() { @Override public JavaField lookupField(int cpi, ResolvedJavaMethod method, int opcode) { - return (JavaField) at(cpi); + return (JavaField) objAt(cpi); } @Override public JavaMethod lookupMethod(int cpi, int opcode) { - return (JavaMethod) at(cpi); + return (JavaMethod) objAt(cpi); } @Override @@ -98,12 +101,12 @@ public List lookupBootstrapMethodInvocations(boolean @Override public JavaType lookupType(int cpi, int opcode) { - return (JavaType) at(cpi); + return (JavaType) objAt(cpi); } @Override public Object lookupConstant(int cpi) { - Object entry = at(cpi); + Object entry = objAt(cpi); if (entry instanceof JavaConstant) { return entry; } else if (entry instanceof JavaType) { @@ -120,7 +123,7 @@ public Object lookupConstant(int cpi, boolean resolve) { @Override public JavaConstant lookupAppendix(int cpi, int opcode) { assert opcode == INVOKEDYNAMIC; - return (JavaConstant) at(cpi); + return (JavaConstant) objAt(cpi); } @VisibleForSerialization @@ -135,6 +138,16 @@ public InterpreterResolvedObjectType getHolder() { // region Unimplemented methods + @Override + public RuntimeException classFormatError(String message) { + throw new ClassFormatError(message); + } + + @Override + public ParserConstantPool getParserConstantPool() { + throw VMError.unimplemented("getParserConstantPool"); + } + @Override public void loadReferencedType(int cpi, int opcode) { throw VMError.intentionallyUnimplemented(); diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaField.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaField.java index 9a8ac79393cd..7ce0b519e7fc 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaField.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaField.java @@ -25,19 +25,22 @@ package com.oracle.svm.interpreter.metadata; import java.lang.annotation.Annotation; +import java.util.function.Function; -import com.oracle.svm.core.hub.DynamicHub; -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.hub.DynamicHub; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.espresso.classfile.descriptors.Name; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.PrimitiveConstant; import jdk.vm.ci.meta.ResolvedJavaField; -public final class InterpreterResolvedJavaField implements ResolvedJavaField { +public final class InterpreterResolvedJavaField implements ResolvedJavaField, CremaFieldAccess { // Computed after analysis. private int offset; @@ -197,6 +200,26 @@ public int hashCode() { // region Unimplemented methods + @Override + public boolean shouldEnforceInitializerCheck() { + throw VMError.unimplemented("shouldEnforceInitializerCheck"); + } + + @Override + public boolean accessChecks(InterpreterResolvedJavaType accessingClass, InterpreterResolvedJavaType holderClass) { + throw VMError.unimplemented("accessChecks"); + } + + @Override + public void loadingConstraints(InterpreterResolvedJavaType accessingClass, Function errorHandler) { + throw VMError.unimplemented("loadingConstraints"); + } + + @Override + public Symbol getSymbolicName() { + throw VMError.unimplemented("getSymbolicName"); + } + @Override public boolean isInternal() { throw VMError.intentionallyUnimplemented(); 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 99e5d72e63e2..814274feb949 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 @@ -30,13 +30,23 @@ import java.lang.reflect.Type; import java.util.HashSet; import java.util.Set; +import java.util.function.Function; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.FunctionPointerHolder; +import com.oracle.svm.core.hub.RuntimeClassLoading; +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.Constants; +import com.oracle.svm.espresso.classfile.ParserMethod; +import com.oracle.svm.espresso.classfile.attributes.CodeAttribute; +import com.oracle.svm.espresso.classfile.descriptors.Name; +import com.oracle.svm.espresso.classfile.descriptors.Signature; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.shared.vtable.PartialMethod; import com.oracle.svm.interpreter.metadata.serialization.VisibleForSerialization; import jdk.graal.compiler.word.Word; @@ -54,16 +64,18 @@ * Encapsulates resolved methods used under close-world assumptions, compiled and interpretable, but * also abstract methods for vtable calls. */ -public final class InterpreterResolvedJavaMethod implements ResolvedJavaMethod { +public final class InterpreterResolvedJavaMethod implements ResolvedJavaMethod, CremaMethodAccess { public static final LocalVariableTable EMPTY_LOCAL_VARIABLE_TABLE = new LocalVariableTable(new Local[0]); public static final int UNKNOWN_METHOD_ID = 0; + private final Symbol signatureSymbol; + // Should be final (not its contents, it can be patched with BREAKPOINT). // These are the bytecodes executed by the interpreter e.g. can be patched with BREAKPOINT. private byte[] interpretedCode; - private final String name; + private final Symbol name; private final int maxLocals; private final int maxStackSize; private final int modifiers; @@ -121,7 +133,7 @@ public InlinedBy(InterpreterResolvedJavaMethod holder, Set name, int maxLocals, int maxStackSize, int modifiers, InterpreterResolvedObjectType declaringClass, InterpreterUnresolvedSignature signature, byte[] code, ExceptionHandler[] exceptionHandlers, LineNumberTable lineNumberTable, LocalVariableTable localVariableTable, ReferenceConstant nativeEntryPoint, int vtableIndex, int gotOffset, int enterStubOffset, int methodId) { @@ -132,7 +144,10 @@ private InterpreterResolvedJavaMethod(ResolvedJavaMethod originalMethod, String this.inlinedBy = new InterpreterResolvedJavaMethod.InlinedBy(this, new HashSet<>()); } - private InterpreterResolvedJavaMethod(String name, int maxLocals, int maxStackSize, int modifiers, InterpreterResolvedObjectType declaringClass, InterpreterUnresolvedSignature signature, + private InterpreterResolvedJavaMethod(Symbol name, + int maxLocals, int maxStackSize, + int modifiers, + InterpreterResolvedObjectType declaringClass, InterpreterUnresolvedSignature signature, byte[] code, ExceptionHandler[] exceptionHandlers, LineNumberTable lineNumberTable, LocalVariableTable localVariableTable, ReferenceConstant nativeEntryPoint, int vtableIndex, int gotOffset, int enterStubOffset, int methodId) { this.name = name; @@ -152,6 +167,43 @@ private InterpreterResolvedJavaMethod(String name, int maxLocals, int maxStackSi this.enterStubOffset = enterStubOffset; this.methodId = methodId; this.inlinedBy = new InlinedBy(this, new HashSet<>()); + + this.signatureSymbol = CremaMethodAccess.toSymbol(signature, SymbolsSupport.getSignatures()); + } + + private InterpreterResolvedJavaMethod(InterpreterResolvedObjectType declaringClass, ParserMethod m, int vtableIndex) { + assert RuntimeClassLoading.isSupported(); + this.name = m.getName(); + this.signatureSymbol = m.getSignature(); + + this.declaringClass = declaringClass; + this.modifiers = m.getFlags() & Constants.JVM_RECOGNIZED_METHOD_MODIFIERS; + CodeAttribute codeAttribute = (CodeAttribute) m.getAttribute(CodeAttribute.NAME); + if (codeAttribute != null) { + this.maxLocals = codeAttribute.getMaxLocals(); + this.maxStackSize = codeAttribute.getMaxStack(); + this.interpretedCode = codeAttribute.getOriginalCode(); + this.lineNumberTable = CremaMethodAccess.toJVMCI(codeAttribute.getLineNumberTableAttribute()); + } else { + this.maxLocals = 0; + this.maxStackSize = 0; + this.interpretedCode = null; + this.lineNumberTable = null; + } + this.signature = CremaMethodAccess.toJVMCI(m.getSignature(), SymbolsSupport.getTypes()); + + this.vtableIndex = vtableIndex; + this.nativeEntryPoint = null; + + this.gotOffset = -2 /* -GOT_NO_ENTRY */; + this.enterStubOffset = EST_NO_ENTRY; + this.methodId = UNKNOWN_METHOD_ID; + this.inlinedBy = new InlinedBy(this, new HashSet<>()); + + } + + public static InterpreterResolvedJavaMethod create(InterpreterResolvedObjectType declaringClass, ParserMethod m, int vtableIndex) { + return new InterpreterResolvedJavaMethod(declaringClass, m, vtableIndex); } @VisibleForSerialization @@ -159,17 +211,20 @@ public static InterpreterResolvedJavaMethod create(String name, int maxLocals, i InterpreterUnresolvedSignature signature, byte[] code, ExceptionHandler[] exceptionHandlers, LineNumberTable lineNumberTable, LocalVariableTable localVariableTable, ReferenceConstant nativeEntryPoint, int vtableIndex, int gotOffset, int enterStubOffset, int methodId) { - return new InterpreterResolvedJavaMethod(name, maxLocals, maxStackSize, modifiers, declaringClass, signature, code, + Symbol nameSymbol = SymbolsSupport.getNames().getOrCreate(name); + return new InterpreterResolvedJavaMethod(nameSymbol, maxLocals, maxStackSize, modifiers, declaringClass, signature, code, exceptionHandlers, lineNumberTable, localVariableTable, nativeEntryPoint, vtableIndex, gotOffset, enterStubOffset, methodId); } // Only called during universe building @Platforms(Platform.HOSTED_ONLY.class) - public static InterpreterResolvedJavaMethod create(ResolvedJavaMethod originalMethod, String name, int maxLocals, int maxStackSize, int modifiers, InterpreterResolvedObjectType declaringClass, + public static InterpreterResolvedJavaMethod create(ResolvedJavaMethod originalMethod, String name, int maxLocals, int maxStackSize, int modifiers, + InterpreterResolvedObjectType declaringClass, InterpreterUnresolvedSignature signature, byte[] code, ExceptionHandler[] exceptionHandlers, LineNumberTable lineNumberTable, LocalVariableTable localVariableTable, ReferenceConstant nativeEntryPoint, int vtableIndex, int gotOffset, int enterStubOffset, int methodId) { - return new InterpreterResolvedJavaMethod(originalMethod, name, maxLocals, maxStackSize, modifiers, declaringClass, signature, code, + Symbol nameSymbol = SymbolsSupport.getNames().getOrCreate(name); + return new InterpreterResolvedJavaMethod(originalMethod, nameSymbol, maxLocals, maxStackSize, modifiers, declaringClass, signature, code, exceptionHandlers, lineNumberTable, localVariableTable, nativeEntryPoint, vtableIndex, gotOffset, enterStubOffset, methodId); } @@ -238,10 +293,20 @@ public int getCodeSize() { } @Override - public String getName() { + public Symbol getSymbolicName() { return name; } + @Override + public Symbol getSymbolicSignature() { + return signatureSymbol; + } + + @Override + public String getName() { + return name.toString(); + } + @Override public InterpreterResolvedObjectType getDeclaringClass() { return declaringClass; @@ -430,6 +495,7 @@ public void setVTableIndex(int vtableIndex) { this.vtableIndex = vtableIndex; } + @Override public boolean hasVTableIndex() { return vtableIndex != VTBL_NO_ENTRY && vtableIndex != VTBL_ONE_IMPL; } @@ -485,8 +551,45 @@ public void setInterpreterExecToken(Object interpreterExecToken) { this.interpreterExecToken = interpreterExecToken; } + @Override + public InterpreterResolvedJavaMethod asMethodAccess() { + return this; + } + + @Override + public PartialMethod withVTableIndex(int index) { + assert vtableIndex == VTBL_NO_ENTRY; + vtableIndex = index; + return this; + } + // region Unimplemented methods + @Override + public boolean shouldSkipLoadingConstraints() { + throw VMError.unimplemented("shouldSkipLoadingConstraints"); + } + + @Override + public CodeAttribute getCodeAttribute() { + throw VMError.unimplemented("getCodeAttribute"); + } + + @Override + public boolean accessChecks(InterpreterResolvedJavaType accessingClass, InterpreterResolvedJavaType holderClass) { + throw VMError.unimplemented("accessChecks"); + } + + @Override + public void loadingConstraints(InterpreterResolvedJavaType accessingClass, Function errorHandler) { + throw VMError.unimplemented("loadingConstraints"); + } + + @Override + public com.oracle.svm.espresso.classfile.ExceptionHandler[] getSymbolicExceptionHandlers() { + throw VMError.unimplemented("getSymbolicExceptionHandlers"); + } + @Override public Annotation[][] getParameterAnnotations() { throw VMError.intentionallyUnimplemented(); diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java index 6eaa20c67826..a54abcceaa8c 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedJavaType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -34,7 +34,12 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.RuntimeClassLoading; +import com.oracle.svm.core.hub.registry.SymbolsSupport; import com.oracle.svm.core.util.VMError; +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 jdk.vm.ci.meta.Assumptions; import jdk.vm.ci.meta.JavaConstant; @@ -47,10 +52,10 @@ * Represents a primitive or reference resolved Java type, including additional capabilities of the * closed world e.g. instantiable, instantiated, effectively final ... */ -public abstract class InterpreterResolvedJavaType implements ResolvedJavaType { - public static final ResolvedJavaMethod[] NO_METHODS = new ResolvedJavaMethod[0]; +public abstract class InterpreterResolvedJavaType implements ResolvedJavaType, CremaTypeAccess { + public static final InterpreterResolvedJavaMethod[] NO_METHODS = new InterpreterResolvedJavaMethod[0]; - private final String name; + private final Symbol type; private final Class clazz; private final JavaConstant clazzConstant; private final boolean isWordType; @@ -59,23 +64,23 @@ public abstract class InterpreterResolvedJavaType implements ResolvedJavaType { // Only called at build time universe creation. @Platforms(Platform.HOSTED_ONLY.class) - protected InterpreterResolvedJavaType(String name, Class javaClass) { - this.name = MetadataUtil.requireNonNull(name); + protected InterpreterResolvedJavaType(Symbol type, Class javaClass) { + this.type = MetadataUtil.requireNonNull(type); this.clazzConstant = null; this.clazz = MetadataUtil.requireNonNull(javaClass); this.isWordType = WordBase.class.isAssignableFrom(javaClass); } // Called by the interpreter. - protected InterpreterResolvedJavaType(String name, Class javaClass, boolean isWordType) { - this.name = MetadataUtil.requireNonNull(name); + protected InterpreterResolvedJavaType(Symbol type, Class javaClass, boolean isWordType) { + this.type = MetadataUtil.requireNonNull(type); this.clazzConstant = null; this.clazz = MetadataUtil.requireNonNull(javaClass); this.isWordType = isWordType; } - protected InterpreterResolvedJavaType(String name, JavaConstant clazzConstant, boolean isWordType) { - this.name = MetadataUtil.requireNonNull(name); + protected InterpreterResolvedJavaType(Symbol type, JavaConstant clazzConstant, boolean isWordType) { + this.type = MetadataUtil.requireNonNull(type); this.clazzConstant = MetadataUtil.requireNonNull(clazzConstant); this.clazz = null; this.isWordType = isWordType; @@ -83,7 +88,7 @@ protected InterpreterResolvedJavaType(String name, JavaConstant clazzConstant, b @Override public final String getName() { - return name; + return type.toString(); } // This is only here for performance, otherwise the clazzConstant must be unwrapped every time. @@ -151,6 +156,45 @@ public boolean isMethodExitEvent() { return methodExitEventEnabled; } + @Override + public boolean isJavaLangObject() { + return ResolvedJavaType.super.isJavaLangObject(); + } + + @Override + public Symbol getSymbolicName() { + // This is assumed to be low-traffic + return SymbolsSupport.getNames().getOrCreate(TypeSymbols.typeToName(type)); + } + + @Override + public Symbol getSymbolicType() { + return type; + } + + @Override + public final boolean isAssignableFrom(InterpreterResolvedJavaType other) { + return clazz.isAssignableFrom(other.clazz); + } + + @Override + public final boolean hasSameDefiningClassLoader(InterpreterResolvedJavaType other) { + return this.clazz.getClassLoader() == other.clazz.getClassLoader(); + } + + @Override + public abstract InterpreterResolvedJavaMethod[] getDeclaredMethods(boolean forceLink); + + @Override + public final boolean isMagicAccessor() { + return false; + } + + @Override + public final boolean isConcrete() { + return ResolvedJavaType.super.isConcrete(); + } + // region Unimplemented methods @Override @@ -269,7 +313,7 @@ public final ResolvedJavaMethod[] getDeclaredConstructors() { } @Override - public final ResolvedJavaMethod[] getDeclaredMethods() { + public final InterpreterResolvedJavaMethod[] getDeclaredMethods() { return getDeclaredMethods(true); } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java index 6bda32174c19..31584ef04773 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedObjectType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -33,11 +33,15 @@ import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.espresso.classfile.ParserKlass; +import com.oracle.svm.espresso.classfile.descriptors.Name; +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.interpreter.metadata.serialization.VisibleForSerialization; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public final class InterpreterResolvedObjectType extends InterpreterResolvedJavaType { @@ -47,6 +51,7 @@ public final class InterpreterResolvedObjectType extends InterpreterResolvedJava private final InterpreterResolvedObjectType superclass; private final InterpreterResolvedObjectType[] interfaces; private InterpreterResolvedJavaMethod[] declaredMethods; + private int afterFieldsOffset; // Populated after analysis. private InterpreterConstantPool constantPool; @@ -71,11 +76,12 @@ public VTableHolder(InterpreterResolvedObjectType holder, InterpreterResolvedJav private VTableHolder vtableHolder = null; // Debugger side constructor, class is an opaque JavaConstant. - private InterpreterResolvedObjectType(String name, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces, + private InterpreterResolvedObjectType(Symbol type, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, + InterpreterResolvedObjectType[] interfaces, InterpreterConstantPool constantPool, JavaConstant clazzConstant, boolean isWordType, String sourceFileName) { - super(name, clazzConstant, isWordType); + super(type, clazzConstant, isWordType); this.modifiers = modifiers; this.componentType = componentType; this.superclass = superclass; @@ -85,11 +91,12 @@ private InterpreterResolvedObjectType(String name, int modifiers, InterpreterRes } // Interpreter side constructor. - private InterpreterResolvedObjectType(String name, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces, + private InterpreterResolvedObjectType(Symbol type, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, + InterpreterResolvedObjectType[] interfaces, InterpreterConstantPool constantPool, Class javaClass, boolean isWordType) { - super(name, javaClass, isWordType); + super(type, javaClass, isWordType); assert isWordType == WordBase.class.isAssignableFrom(javaClass); this.modifiers = modifiers; this.superclass = superclass; @@ -100,11 +107,12 @@ private InterpreterResolvedObjectType(String name, int modifiers, InterpreterRes } @Platforms(Platform.HOSTED_ONLY.class) - private InterpreterResolvedObjectType(ResolvedJavaType originalType, String name, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, + private InterpreterResolvedObjectType(ResolvedJavaType originalType, Symbol type, int modifiers, InterpreterResolvedJavaType componentType, + InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces, InterpreterConstantPool constantPool, Class javaClass, String sourceFileName) { - super(name, javaClass); + super(type, javaClass); this.originalType = originalType; this.modifiers = modifiers; this.componentType = componentType; @@ -125,15 +133,22 @@ public static InterpreterResolvedObjectType createAtBuildTime(ResolvedJavaType o InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces, InterpreterConstantPool constantPool, Class javaClass, String sourceFileName) { - return new InterpreterResolvedObjectType(originalType, name, modifiers, componentType, superclass, interfaces, constantPool, javaClass, sourceFileName); + Symbol type = CremaTypeAccess.jvmciNameToType(name); + return new InterpreterResolvedObjectType(originalType, type, modifiers, componentType, superclass, interfaces, constantPool, javaClass, sourceFileName); } @VisibleForSerialization public static InterpreterResolvedObjectType createForInterpreter(String name, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, InterpreterResolvedObjectType[] interfaces, InterpreterConstantPool constantPool, - Class javaClass, - boolean isWordType) { - return new InterpreterResolvedObjectType(name, modifiers, componentType, superclass, interfaces, constantPool, javaClass, isWordType); + Class javaClass, boolean isWordType) { + Symbol type = CremaTypeAccess.jvmciNameToType(name); + return new InterpreterResolvedObjectType(type, modifiers, componentType, superclass, interfaces, constantPool, javaClass, isWordType); + } + + @VisibleForSerialization + public static InterpreterResolvedObjectType create(ParserKlass parserKlass, int modifiers, InterpreterResolvedJavaType componentType, InterpreterResolvedObjectType superclass, + InterpreterResolvedObjectType[] interfaces, Class javaClass, boolean isWordType) { + return new InterpreterResolvedObjectType(parserKlass.getType(), modifiers, componentType, superclass, interfaces, null, javaClass, isWordType); } @VisibleForSerialization @@ -142,7 +157,9 @@ public static InterpreterResolvedObjectType createWithOpaqueClass(String name, i JavaConstant clazzConstant, boolean isWordType, String sourceFileName) { - return new InterpreterResolvedObjectType(name, modifiers, componentType, superclass, interfaces, constantPool, clazzConstant, isWordType, sourceFileName); + Symbol type = CremaTypeAccess.jvmciNameToType(name); + return new InterpreterResolvedObjectType(type, modifiers, componentType, superclass, interfaces, constantPool, clazzConstant, isWordType, + sourceFileName); } public void setConstantPool(InterpreterConstantPool constantPool) { @@ -150,11 +167,17 @@ public void setConstantPool(InterpreterConstantPool constantPool) { this.constantPool = MetadataUtil.requireNonNull(constantPool); } + @Override public InterpreterConstantPool getConstantPool() { assert !isArray(); return constantPool; } + @Override + public InterpreterResolvedJavaType resolveClassConstantInPool(int cpi) { + return null; + } + @Platforms(Platform.HOSTED_ONLY.class) public ResolvedJavaType getOriginalType() { return originalType; @@ -210,6 +233,10 @@ private static boolean isSubTypeOf(InterpreterResolvedObjectType superType, Inte return false; } + /** + * Returns the virtual dispatch table. For interfaces this returns the interface dispatch table + * prototype. + */ public InterpreterResolvedJavaMethod[] getVtable() { if (vtableHolder == null) { return null; @@ -221,6 +248,12 @@ public void setVtable(InterpreterResolvedJavaMethod[] vtable) { this.vtableHolder = new VTableHolder(this, vtable); } + @Override + public InterpreterResolvedJavaMethod lookupVTableEntry(int vtableIndex) { + assert getVtable() != null; + return getVtable()[vtableIndex]; + } + @Platforms(Platform.HOSTED_ONLY.class) public VTableHolder getVtableHolder() { assert !isArray(); @@ -228,7 +261,7 @@ public VTableHolder getVtableHolder() { } @Override - public ResolvedJavaMethod[] getDeclaredMethods(boolean link) { + public InterpreterResolvedJavaMethod[] getDeclaredMethods(boolean link) { if (link) { link(); } @@ -238,4 +271,57 @@ public ResolvedJavaMethod[] getDeclaredMethods(boolean link) { public void setDeclaredMethods(InterpreterResolvedJavaMethod[] declaredMethods) { this.declaredMethods = declaredMethods; } + + public void setAfterFieldsOffset(int afterFieldsOffset) { + this.afterFieldsOffset = afterFieldsOffset; + } + + public int getAfterFieldsOffset() { + return afterFieldsOffset; + } + + @Override + public String getJavaName() { + throw VMError.unimplemented("getJavaName"); + } + + @Override + public InterpreterResolvedJavaType findLeastCommonAncestor(InterpreterResolvedJavaType other) { + throw VMError.unimplemented("getJavaName"); + } + + @Override + public InterpreterResolvedJavaType getSuperClass() { + return this.superclass; + } + + @Override + public InterpreterResolvedJavaType getHostType() { + throw VMError.unimplemented("getJavaName"); + } + + @Override + public Symbol getSymbolicRuntimePackage() { + throw VMError.unimplemented("getSymbolicRuntimePackage"); + } + + @Override + public InterpreterResolvedJavaField lookupField(Symbol name, Symbol type) { + throw VMError.unimplemented("lookupField"); + } + + @Override + public InterpreterResolvedJavaMethod lookupMethod(Symbol name, Symbol signature) { + throw VMError.unimplemented("lookupMethod"); + } + + @Override + public InterpreterResolvedJavaMethod lookupInstanceMethod(Symbol name, Symbol signature) { + throw VMError.unimplemented("lookupInstanceMethod"); + } + + @Override + public InterpreterResolvedJavaMethod lookupInterfaceMethod(Symbol name, Symbol signature) { + throw VMError.unimplemented("lookupInterfaceMethod"); + } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java index ef781407a94e..3de08aeba8ee 100644 --- a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/InterpreterResolvedPrimitiveType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -26,29 +26,41 @@ import java.lang.reflect.Modifier; +import com.oracle.svm.core.hub.registry.SymbolsSupport; +import com.oracle.svm.espresso.classfile.ConstantPool; +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.ParserSymbols.ParserTypes; +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 jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public final class InterpreterResolvedPrimitiveType extends InterpreterResolvedJavaType { - private final JavaKind kind; - private InterpreterResolvedPrimitiveType(JavaKind kind) { - super(String.valueOf(kind.getTypeChar()), kind.toJavaClass()); + private InterpreterResolvedPrimitiveType(Symbol type, JavaKind kind) { + super(type, kind.toJavaClass()); assert kind.isPrimitive(); this.kind = kind; } - static final InterpreterResolvedPrimitiveType BOOLEAN = new InterpreterResolvedPrimitiveType(JavaKind.Boolean); - static final InterpreterResolvedPrimitiveType BYTE = new InterpreterResolvedPrimitiveType(JavaKind.Byte); - static final InterpreterResolvedPrimitiveType SHORT = new InterpreterResolvedPrimitiveType(JavaKind.Short); - static final InterpreterResolvedPrimitiveType CHAR = new InterpreterResolvedPrimitiveType(JavaKind.Char); - static final InterpreterResolvedPrimitiveType INT = new InterpreterResolvedPrimitiveType(JavaKind.Int); - static final InterpreterResolvedPrimitiveType FLOAT = new InterpreterResolvedPrimitiveType(JavaKind.Float); - static final InterpreterResolvedPrimitiveType LONG = new InterpreterResolvedPrimitiveType(JavaKind.Long); - static final InterpreterResolvedPrimitiveType DOUBLE = new InterpreterResolvedPrimitiveType(JavaKind.Double); - static final InterpreterResolvedPrimitiveType VOID = new InterpreterResolvedPrimitiveType(JavaKind.Void); + static { + ParserSymbols.ensureInitialized(); + } + + static final InterpreterResolvedPrimitiveType BOOLEAN = new InterpreterResolvedPrimitiveType(ParserTypes._boolean, JavaKind.Boolean); + static final InterpreterResolvedPrimitiveType BYTE = new InterpreterResolvedPrimitiveType(ParserTypes._byte, JavaKind.Byte); + static final InterpreterResolvedPrimitiveType SHORT = new InterpreterResolvedPrimitiveType(ParserTypes._short, JavaKind.Short); + static final InterpreterResolvedPrimitiveType CHAR = new InterpreterResolvedPrimitiveType(ParserTypes._char, JavaKind.Char); + static final InterpreterResolvedPrimitiveType INT = new InterpreterResolvedPrimitiveType(ParserTypes._int, JavaKind.Int); + static final InterpreterResolvedPrimitiveType FLOAT = new InterpreterResolvedPrimitiveType(ParserTypes._float, JavaKind.Float); + static final InterpreterResolvedPrimitiveType LONG = new InterpreterResolvedPrimitiveType(ParserTypes._long, JavaKind.Long); + static final InterpreterResolvedPrimitiveType DOUBLE = new InterpreterResolvedPrimitiveType(ParserTypes._double, JavaKind.Double); + static final InterpreterResolvedPrimitiveType VOID = new InterpreterResolvedPrimitiveType(ParserTypes._void, JavaKind.Void); public static InterpreterResolvedPrimitiveType fromKind(JavaKind kind) { // @formatter:off @@ -106,7 +118,70 @@ public boolean isAssignableFrom(ResolvedJavaType other) { } @Override - public ResolvedJavaMethod[] getDeclaredMethods(boolean link) { + public InterpreterResolvedJavaMethod[] getDeclaredMethods(boolean link) { return NO_METHODS; } + + @Override + public String getJavaName() { + return getJavaKind().getJavaName(); + } + + @Override + public InterpreterResolvedJavaType findLeastCommonAncestor(InterpreterResolvedJavaType other) { + if (this == other) { + return this; + } + return null; + } + + @Override + public InterpreterResolvedJavaType getSuperClass() { + return null; + } + + @Override + public InterpreterResolvedJavaType getHostType() { + return null; + } + + @Override + public Symbol getSymbolicRuntimePackage() { + return SymbolsSupport.getNames().getOrCreate(ByteSequence.EMPTY); + } + + @Override + public InterpreterResolvedJavaField lookupField(Symbol name, Symbol type) { + return null; + } + + @Override + public InterpreterResolvedJavaMethod lookupMethod(Symbol name, Symbol signature) { + return null; + } + + @Override + public InterpreterResolvedJavaMethod lookupInstanceMethod(Symbol name, Symbol signature) { + return null; + } + + @Override + public InterpreterResolvedJavaMethod lookupInterfaceMethod(Symbol name, Symbol signature) { + return null; + } + + @Override + public InterpreterResolvedJavaMethod lookupVTableEntry(int vtableIndex) { + return null; + } + + @Override + public ConstantPool getConstantPool() { + return null; + } + + @Override + public InterpreterResolvedJavaType resolveClassConstantInPool(int cpi) { + return null; + } } diff --git a/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/WithModifiers.java b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/WithModifiers.java new file mode 100644 index 000000000000..8c2524058132 --- /dev/null +++ b/substratevm/src/com.oracle.svm.interpreter.metadata/src/com/oracle/svm/interpreter/metadata/WithModifiers.java @@ -0,0 +1,113 @@ +/* + * 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. 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.svm.interpreter.metadata; + +/** + * Resolves conflict between jvmci and shared ModifiersProvider. + */ +public interface WithModifiers extends com.oracle.svm.espresso.shared.meta.ModifiersProvider, jdk.vm.ci.meta.ModifiersProvider { + @Override + int getModifiers(); + + @Override + default boolean isVarargs() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isVarargs(); + } + + @Override + default boolean isInterface() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isInterface(); + } + + @Override + default boolean isSynchronized() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isSynchronized(); + } + + @Override + default boolean isStatic() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isStatic(); + } + + @Override + default boolean isFinalFlagSet() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isFinalFlagSet(); + } + + @Override + default boolean isPublic() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isPublic(); + } + + @Override + default boolean isPackagePrivate() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isPackagePrivate(); + } + + @Override + default boolean isPrivate() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isPrivate(); + } + + @Override + default boolean isProtected() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isProtected(); + } + + @Override + default boolean isTransient() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isTransient(); + } + + @Override + default boolean isStrict() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isStrict(); + } + + @Override + default boolean isVolatile() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isVolatile(); + } + + @Override + default boolean isNative() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isNative(); + } + + @Override + default boolean isAbstract() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isAbstract(); + } + + @Override + default boolean isConcrete() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isConcrete(); + } + + @Override + default boolean isEnum() { + return com.oracle.svm.espresso.shared.meta.ModifiersProvider.super.isEnum(); + } +} diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeInterpreterUniverse.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeInterpreterUniverse.java index ea7ef86ae6a2..89a3053d6f7a 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeInterpreterUniverse.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/BuildTimeInterpreterUniverse.java @@ -916,17 +916,19 @@ public void mirrorSVMVTable(HostedType hostedType, Consumer rescanFieldI if (!(iType instanceof InterpreterResolvedObjectType objectType)) { return; } + HostedMethod[] hostedDispatchTable = hostedType.getInterpreterDispatchTable(); + VMError.guarantee(hostedDispatchTable != null, "Missing dispatch table for %s", hostedType); - if (hostedType.getVTable().length == 0) { - return; - } - - InterpreterResolvedJavaMethod[] iVTable = new InterpreterResolvedJavaMethod[hostedType.getVTable().length]; + InterpreterResolvedJavaMethod[] iVTable; + if (hostedDispatchTable.length == 0) { + iVTable = InterpreterResolvedJavaType.NO_METHODS; + } else { + iVTable = new InterpreterResolvedJavaMethod[hostedDispatchTable.length]; - for (int i = 0; i < iVTable.length; i++) { - iVTable[i] = getMethod(hostedType.getVTable()[i].getWrapped()); + for (int i = 0; i < iVTable.length; i++) { + iVTable[i] = getMethod(hostedDispatchTable[i].getWrapped()); + } } - objectType.setVtable(iVTable); rescanFieldInHeap.accept(objectType); } diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java index c9a15fc99046..3f7280cbdf13 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/CremaFeature.java @@ -44,8 +44,10 @@ import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.meta.HostedInstanceClass; import com.oracle.svm.hosted.meta.HostedType; import com.oracle.svm.hosted.meta.HostedUniverse; +import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaType; import com.oracle.svm.interpreter.metadata.InterpreterResolvedObjectType; import com.oracle.svm.util.ReflectionUtil; @@ -102,7 +104,7 @@ public void afterAnalysis(AfterAnalysisAccess access) { if (!analysisType.isReachable()) { continue; } - assert btiUniverse.getOrCreateType(analysisType) != null : "type is reachable but not part of interpreter universe: " + analysisType; + assert btiUniverse.getType(analysisType) != null : "type is reachable but not part of interpreter universe: " + analysisType; } } } @@ -119,6 +121,25 @@ public void beforeCompilation(BeforeCompilationAccess access) { } } + @Override + public void afterCompilation(AfterCompilationAccess access) { + FeatureImpl.AfterCompilationAccessImpl accessImpl = (FeatureImpl.AfterCompilationAccessImpl) access; + BuildTimeInterpreterUniverse iUniverse = BuildTimeInterpreterUniverse.singleton(); + for (HostedType type : accessImpl.getUniverse().getTypes()) { + if (type.isPrimitive() || type.isArray() || type.isInterface()) { + continue; + } + InterpreterResolvedJavaType iType = iUniverse.getType(type.getWrapped()); + if (iType == null) { + assert !type.getWrapped().isReachable() : "No interpreter type for " + type; + continue; + } + InterpreterResolvedObjectType objectType = (InterpreterResolvedObjectType) iType; + HostedInstanceClass instanceClass = (HostedInstanceClass) type; + objectType.setAfterFieldsOffset(instanceClass.getAfterFieldsOffset()); + } + } + @Override public void afterAbstractImageCreation(AfterAbstractImageCreationAccess access) { FeatureImpl.AfterAbstractImageCreationAccessImpl accessImpl = ((FeatureImpl.AfterAbstractImageCreationAccessImpl) access); 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 de45bb97d089..6c676fea68b1 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 @@ -24,28 +24,48 @@ */ package com.oracle.svm.interpreter; +import static com.oracle.svm.interpreter.InterpreterStubSection.getCremaStubForVTableIndex; + +import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.svm.core.graal.meta.KnownOffsets; import com.oracle.svm.core.hub.CremaSupport; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.espresso.classfile.ParserKlass; +import com.oracle.svm.espresso.classfile.ParserMethod; +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.shared.vtable.MethodTableException; +import com.oracle.svm.espresso.shared.vtable.PartialMethod; +import com.oracle.svm.espresso.shared.vtable.PartialType; +import com.oracle.svm.espresso.shared.vtable.Tables; +import com.oracle.svm.espresso.shared.vtable.VTable; import com.oracle.svm.hosted.substitute.DeletedElementException; +import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaField; import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaMethod; import com.oracle.svm.interpreter.metadata.InterpreterResolvedJavaType; import com.oracle.svm.interpreter.metadata.InterpreterResolvedObjectType; +import jdk.graal.compiler.word.Word; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; public class CremaSupportImpl implements CremaSupport { - @Platforms(Platform.HOSTED_ONLY.class) @Override public ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType type) { @@ -56,7 +76,7 @@ public ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType t /* query type from universe, maybe already exists (due to method creation) */ InterpreterResolvedJavaType interpreterType = btiUniverse.getOrCreateType(analysisType); - ResolvedJavaMethod[] declaredMethods = interpreterType.getDeclaredMethods(); + ResolvedJavaMethod[] declaredMethods = interpreterType.getDeclaredMethods(false); assert declaredMethods == null || declaredMethods == InterpreterResolvedJavaType.NO_METHODS : "should only be set once"; if (analysisType.isPrimitive()) { @@ -87,4 +107,321 @@ public ResolvedJavaType createInterpreterType(DynamicHub hub, ResolvedJavaType t return interpreterType; } + + @Override + public void fillDynamicHubInfo(DynamicHub hub, CremaDispatchTable dispatchTable, List> transitiveSuperInterfaces, int[] interfaceIndicies) { + CremaDispatchTableImpl table = (CremaDispatchTableImpl) dispatchTable; + + assert hub.getSuperHub() == DynamicHub.fromClass(table.superType()); + InterpreterResolvedObjectType superType = (InterpreterResolvedObjectType) hub.getSuperHub().getInterpreterType(); + InterpreterResolvedObjectType[] interfaces = new InterpreterResolvedObjectType[hub.getInterfaces().length]; + for (int i = 0; i < interfaces.length; i++) { + interfaces[i] = (InterpreterResolvedObjectType) hub.getInterfaces()[i].getInterpreterType(); + } + + InterpreterResolvedJavaType componentType = null; + DynamicHub componentHub = hub.getComponentHub(); + if (componentHub != null) { + componentType = (InterpreterResolvedJavaType) componentHub.getInterpreterType(); + } + InterpreterResolvedObjectType thisType = InterpreterResolvedObjectType.create(table.partialType.parserKlass, hub.getModifiers(), componentType, superType, interfaces, DynamicHub.toClass(hub), + false); + + // GR-60109 + // thisType.setConstantPool(...); + + table.registerClass(thisType); + + thisType.setDeclaredMethods(table.declaredMethods()); + + List completeTable = table.cremaVTable(transitiveSuperInterfaces); + thisType.setVtable(completeTable.toArray(new InterpreterResolvedJavaMethod[0])); + + long vTableBaseOffset = KnownOffsets.singleton().getVTableBaseOffset(); + long vTableEntrySize = KnownOffsets.singleton().getVTableEntrySize(); + int i = 0; + for (InterpreterResolvedJavaMethod method : completeTable) { + long offset = vTableBaseOffset + i * vTableEntrySize; + WordBase entry; + if (method.hasNativeEntryPoint()) { + entry = method.getNativeEntryPoint(); + } else { + entry = getCremaStubForVTableIndex(i); + } + Word.objectToUntrackedPointer(hub).writeWord(Math.toIntExact(offset), entry); + i++; + } + + hub.setInterpreterType(thisType); + } + + @Override + public CremaDispatchTable getDispatchTable(ParserKlass parsed, Class superClass, List> transitiveSuperInterfaces) { + CremaPartialType partialType = new CremaPartialType(parsed, superClass, transitiveSuperInterfaces); + try { + if (Modifier.isInterface(parsed.getFlags())) { + return new CremaInterfaceDispatchTableImpl(partialType); + } else { + Tables tables = VTable.create(partialType, + false, + false, + true); + return new CremaInstanceDispatchTableImpl(tables, partialType); + } + } catch (MethodTableException e) { + throw new IncompatibleClassChangeError(e.getMessage()); + } + } + + static final class CremaPartialType implements PartialType { + private final Class superClass; + private final ParserKlass parserKlass; + private final List declared; + private final List parentTable; + private final EconomicMap> interfacesData = EconomicMap.create(Equivalence.IDENTITY); + + private InterpreterResolvedObjectType thisJavaType; + + @SuppressWarnings("this-escape") + CremaPartialType(ParserKlass parsed, Class superClass, List> superInterfaces) { + this.superClass = superClass; + this.parserKlass = parsed; + parentTable = computeParentTable(superClass); + + for (Class intf : superInterfaces) { + DynamicHub intfHub = DynamicHub.fromClass(intf); + InterpreterResolvedObjectType interpreterType = (InterpreterResolvedObjectType) intfHub.getInterpreterType(); + // "vtable" contains the interface table prototype for interfaces + interfacesData.put(interpreterType, Arrays.asList(interpreterType.getVtable())); + } + + declared = new ArrayList<>(); + for (ParserMethod m : parsed.getMethods()) { + declared.add(new CremaPartialMethod(this, m)); + } + + } + + private static List computeParentTable(Class superClass) { + DynamicHub superHub = DynamicHub.fromClass(superClass); + InterpreterResolvedObjectType superType = (InterpreterResolvedObjectType) superHub.getInterpreterType(); + InterpreterResolvedJavaMethod[] superVTableMirror = superType.getVtable(); + // Computes the size of the parent's vtable, without the trailing itables. + long vTableEntrySize = KnownOffsets.singleton().getVTableEntrySize(); + long minOffset = superVTableMirror.length * vTableEntrySize; + int[] typeSlots = superHub.getOpenTypeWorldTypeCheckSlots(); + for (int i = superHub.getNumClassTypes(); i < typeSlots.length; i += 2) { + minOffset = Math.min(minOffset, typeSlots[i + 1]); + } + int superTableLen = Math.toIntExact(minOffset / vTableEntrySize); + InterpreterResolvedJavaMethod[] superTable = Arrays.copyOf(superVTableMirror, superTableLen); + return Arrays.asList(superTable); + } + + @Override + public List getParentTable() { + return parentTable; + } + + @Override + public EconomicMap> getInterfacesData() { + return interfacesData; + } + + @Override + public List getDeclaredMethodsList() { + return declared; + } + + @Override + public boolean sameRuntimePackage(InterpreterResolvedJavaType otherType) { + // GR-62339 runtime packages + return false; + } + + @Override + public Symbol getSymbolicName() { + return parserKlass.getName(); + } + + public InterpreterResolvedObjectType getThisJavaType() { + return thisJavaType; + } + + @Override + public String toString() { + return "CremaPartialType<" + getSymbolicName() + ">"; + } + } + + static final class CremaPartialMethod implements PartialMethod { + private final CremaPartialType partialType; + private final ParserMethod m; + private int vtableIndex = -1; + private int itableIndex = -1; + + InterpreterResolvedJavaMethod resolved; + + CremaPartialMethod(CremaPartialType partialType, ParserMethod m) { + this.partialType = partialType; + this.m = m; + } + + @Override + public CremaPartialMethod withVTableIndex(int index) { + assert vtableIndex == -1; + this.vtableIndex = index; + return this; + } + + public CremaPartialMethod withITableIndex(int index) { + assert itableIndex == -1; + this.itableIndex = index; + return this; + } + + @Override + public boolean isConstructor() { + return m.getName() == ParserSymbols.ParserNames._init_; + } + + @Override + public boolean isClassInitializer() { + return m.getName() == ParserSymbols.ParserNames._clinit_; + } + + @Override + public int getModifiers() { + return m.getFlags(); + } + + @Override + public Symbol getSymbolicName() { + return m.getName(); + } + + @Override + public Symbol getSymbolicSignature() { + return m.getSignature(); + } + + @Override + public InterpreterResolvedJavaMethod asMethodAccess() { + if (resolved != null) { + return resolved; + } + int dispatchIndex = InterpreterResolvedJavaMethod.VTBL_NO_ENTRY; + if (vtableIndex != -1) { + assert itableIndex == -1; + dispatchIndex = vtableIndex; + } else if (itableIndex != -1) { + dispatchIndex = itableIndex; + } + resolved = InterpreterResolvedJavaMethod.create(partialType.getThisJavaType(), m, dispatchIndex); + return resolved; + } + } + + private abstract static class CremaDispatchTableImpl implements CremaDispatchTable { + protected final CremaPartialType partialType; + + CremaDispatchTableImpl(CremaPartialType partialType) { + this.partialType = partialType; + } + + public void registerClass(InterpreterResolvedObjectType thisType) { + partialType.thisJavaType = thisType; + } + + public Class superType() { + return partialType.superClass; + } + + public InterpreterResolvedJavaMethod[] declaredMethods() { + InterpreterResolvedJavaMethod[] result = new InterpreterResolvedJavaMethod[partialType.getDeclaredMethodsList().size()]; + int i = 0; + for (var m : partialType.getDeclaredMethodsList()) { + result[i] = m.asMethodAccess(); + i++; + } + return result; + } + + protected static List toSimpleTable(List> table) { + List result = new ArrayList<>(); + for (var pm : table) { + result.add(pm.asMethodAccess()); + } + return result; + } + + public abstract List cremaVTable(List> intfList); + } + + private static final class CremaInterfaceDispatchTableImpl extends CremaDispatchTableImpl { + + CremaInterfaceDispatchTableImpl(CremaPartialType partialType) { + super(partialType); + } + + @Override + public List cremaVTable(List> intfList) { + List itable = new ArrayList<>(); + for (CremaPartialMethod method : partialType.getDeclaredMethodsList()) { + if (VTable.isVirtualEntry(method)) { + itable.add(method.withITableIndex(itable.size()).asMethodAccess()); + } + } + return itable; + } + + @Override + public int vtableLength() { + return 0; + } + + @Override + public int itableLength(Class iface) { + return 0; + } + } + + private static final class CremaInstanceDispatchTableImpl extends CremaDispatchTableImpl { + private final Tables table; + + CremaInstanceDispatchTableImpl(Tables table, CremaPartialType partialType) { + super(partialType); + this.table = table; + } + + @Override + public List cremaVTable(List> intfList) { + List vtable = toSimpleTable(table.getVtable()); + List result = new ArrayList<>(vtable); + for (Class intf : intfList) { + List itable = toSimpleTable(getItableFor(intf)); + result.addAll(itable); + } + return result; + } + + @Override + public int vtableLength() { + return table.getVtable().size(); + } + + @Override + public int itableLength(Class iface) { + return getItableFor(iface).size(); + } + + private List> getItableFor(Class iface) { + return table.getItables().get((InterpreterResolvedJavaType) DynamicHub.fromClass(iface).getInterpreterType()); + } + } + + @Override + public int getAfterFieldsOffset(DynamicHub hub) { + return ((InterpreterResolvedObjectType) hub.getInterpreterType()).getAfterFieldsOffset(); + } } diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java index 632fa838b8c0..91dd863b4446 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerFeature.java @@ -65,7 +65,6 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.svm.core.BuildArtifacts; import com.oracle.svm.core.FunctionPointerHolder; -import com.oracle.svm.core.InvalidMethodPointerHandler; import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.RuntimeAssertionsSupport; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; @@ -440,9 +439,6 @@ public void beforeCompilation(BeforeCompilationAccess access) { iUniverse.mirrorSVMVTable(hostedType, objectType -> accessImpl.getHeapScanner().rescanField(objectType, vtableHolderField)); } - HostedMethod methodNotCompiledHandler = hMetaAccess.lookupJavaMethod(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD); - InterpreterMethodPointerHolder.setMethodNotCompiledHandler(new MethodPointer(methodNotCompiledHandler)); - // Allow methods that call System.arraycopy to be interpreted. try { HostedMethod arraycopy = hMetaAccess.lookupJavaMethod( diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerSupport.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerSupport.java index ec28340f13af..b2418e8ad55f 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerSupport.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/DebuggerSupport.java @@ -24,6 +24,25 @@ */ package com.oracle.svm.interpreter; +import static com.oracle.svm.interpreter.InterpreterUtil.traceInterpreter; + +import java.io.IOException; +import java.lang.reflect.Executable; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Set; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.MapCursor; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.ProcessProperties; +import org.graalvm.word.Pointer; + import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.FunctionPointerHolder; @@ -39,28 +58,11 @@ import com.oracle.svm.interpreter.metadata.MetadataUtil; import com.oracle.svm.interpreter.metadata.serialization.SerializationContext; import com.oracle.svm.interpreter.metadata.serialization.Serializers; + import jdk.graal.compiler.api.replacements.Fold; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; -import org.graalvm.collections.EconomicMap; -import org.graalvm.collections.MapCursor; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.ProcessProperties; -import org.graalvm.word.Pointer; - -import java.io.IOException; -import java.lang.reflect.Executable; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Set; - -import static com.oracle.svm.interpreter.InterpreterUtil.traceInterpreter; public class DebuggerSupport { public static final String IMAGE_INTERP_HASH_SYMBOL_NAME = "__svm_interp_hash"; diff --git a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterFeature.java b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterFeature.java index 88bce9e3515a..3cfd0b029509 100644 --- a/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterFeature.java +++ b/substratevm/src/com.oracle.svm.interpreter/src/com/oracle/svm/interpreter/InterpreterFeature.java @@ -44,6 +44,7 @@ import org.graalvm.word.WordBase; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.core.InvalidMethodPointerHandler; import com.oracle.svm.core.ParsingReason; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.feature.InternalFeature; @@ -226,6 +227,9 @@ public void beforeCompilation(BeforeCompilationAccess access) { /* required so that it can hold a relocatable pointer */ accessImpl.registerAsImmutable(InterpreterMethodPointerHolder.singleton()); accessImpl.registerAsImmutable(InterpreterSupport.singleton()); + + HostedMethod methodNotCompiledHandler = accessImpl.getMetaAccess().lookupJavaMethod(InvalidMethodPointerHandler.METHOD_POINTER_NOT_COMPILED_HANDLER_METHOD); + InterpreterMethodPointerHolder.setMethodNotCompiledHandler(new MethodPointer(methodNotCompiledHandler)); } @Override From ac705befe56cadf63fa9554a11e58f27ed5bd41f Mon Sep 17 00:00:00 2001 From: Gilles Duboscq Date: Thu, 3 Jul 2025 17:31:25 +0200 Subject: [PATCH 6/6] Ensure Crema doesn't load Hybrid types --- .../espresso/classfile/ClassfileParser.java | 5 +- .../com/oracle/svm/core/hub/DynamicHub.java | 5 +- .../AbstractRuntimeClassRegistry.java | 21 +++++++ .../svm/core/hub/registry/SVMSymbols.java | 58 +++++++++++++++++++ .../svm/core/hub/registry/SymbolsSupport.java | 4 +- 5 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java diff --git a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java index 7ef3b4eae892..423a5ff67a5f 100644 --- a/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java +++ b/espresso-shared/src/com.oracle.truffle.espresso.classfile/src/com/oracle/truffle/espresso/classfile/ClassfileParser.java @@ -1126,7 +1126,10 @@ private static Attribute[] spawnAttributesArray(int attributeCount) { return attributeCount == 0 ? Attribute.EMPTY_ARRAY : new Attribute[attributeCount]; } - private static int parseAnnotation(ClassfileStream subStream) { + /** + * Parse one annotation in an annotation attribute and return the annotation type index. + */ + public static int parseAnnotation(ClassfileStream subStream) { int typeIndex = subStream.readU2(); int numElementValuePairs = subStream.readU2(); for (int k = 0; k < numElementValuePairs; k++) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 0716ffe8da8b..7410fcbe91e3 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -486,7 +486,10 @@ public static DynamicHub allocate(String name, DynamicHub superHub, Object inter } else { // instance class assert !"java.lang.Class".equals(name); - // @Hybrid is ignored + /* + * @Hybrid types are not supported. The absence of the annotation is assumed to be + * checked by callers. See AbstractRuntimeClassRegistry.checkNotHybrid. + */ if (Modifier.isAbstract(modifiers)) { layoutEncoding = LayoutEncoding.forAbstract(); } else { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java index 0f666abd7fba..a9fb5b13e967 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/AbstractRuntimeClassRegistry.java @@ -47,6 +47,7 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.RuntimeClassLoading; import com.oracle.svm.core.hub.RuntimeClassLoading.ClassDefinitionInfo; +import com.oracle.svm.core.hub.registry.SVMSymbols.SVMTypes; import com.oracle.svm.core.jdk.Target_java_lang_ClassLoader; import com.oracle.svm.core.util.VMError; import com.oracle.svm.espresso.classfile.ClassfileParser; @@ -56,6 +57,7 @@ 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.InnerClassesAttribute; import com.oracle.svm.espresso.classfile.attributes.NestHostAttribute; import com.oracle.svm.espresso.classfile.attributes.PermittedSubclassesAttribute; @@ -408,6 +410,8 @@ private Class createClass(ParserKlass parsed, ClassDefinitionInfo info, Symbo // GR-62339 Module module = getClassLoader().getUnnamedModule(); + checkNotHybrid(parsed); + DynamicHub hub = DynamicHub.allocate(externalName, superHub, interfacesEncoding, null, sourceFile, modifiers, flags, getClassLoader(), nestHost, simpleBinaryName, module, enclosingClass, classSignature, typeID, numClassTypes, typeIDDepth, numInterfacesTypes, openTypeWorldTypeCheckSlots, dispatchTableLength, afterFieldsOffset, isValueBased); @@ -417,6 +421,23 @@ sourceFile, modifiers, flags, getClassLoader(), nestHost, simpleBinaryName, modu return DynamicHub.toClass(hub); } + private static void checkNotHybrid(ParserKlass parsed) { + Attribute attribute = parsed.getAttribute(ParserNames.RuntimeVisibleAnnotations); + if (attribute == null) { + return; + } + ClassfileStream stream = new ClassfileStream(attribute.getData(), null); + int count = stream.readU2(); + for (int j = 0; j < count; j++) { + int typeIndex = ClassfileParser.parseAnnotation(stream); + Symbol annotType = parsed.getConstantPool().utf8At(typeIndex, "annotation type"); + if (SVMTypes.com_oracle_svm_core_hub_Hybrid.equals(annotType)) { + throw new ClassFormatError("Cannot load @Hybrid classes at runtime"); + } + } + + } + private static boolean declaresDefaultMethods(ParserKlass parsed) { for (ParserMethod method : parsed.getMethods()) { int flags = method.getFlags(); 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 new file mode 100644 index 000000000000..016b9953fc11 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SVMSymbols.java @@ -0,0 +1,58 @@ +/* + * 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. 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.svm.core.hub.registry; + +import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols; +import com.oracle.svm.espresso.classfile.descriptors.StaticSymbols; +import com.oracle.svm.espresso.classfile.descriptors.Symbol; +import com.oracle.svm.espresso.classfile.descriptors.Type; + +public final class SVMSymbols { + // Pre-allocate enough slots to avoid resizing the underlying map. + // But not too much, since these maps will be persisted in the image heap (Native Image). + public static final StaticSymbols SYMBOLS = new StaticSymbols(ParserSymbols.SYMBOLS, 1 << 8); + + private SVMSymbols() { + } + + static { + SVMTypes.ensureInitialized(); + } + + public static void ensureInitialized() { + /* nop */ + } + + public static final class SVMTypes { + public static final Symbol com_oracle_svm_core_hub_Hybrid = SYMBOLS.putType("Lcom/oracle/svm/core/hub/Hybrid;"); + + private SVMTypes() { + } + + public static void ensureInitialized() { + /* nop */ + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java index 3acaeb1b9b33..ae77508ad958 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/registry/SymbolsSupport.java @@ -30,7 +30,6 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.espresso.classfile.descriptors.NameSymbols; -import com.oracle.svm.espresso.classfile.descriptors.ParserSymbols; import com.oracle.svm.espresso.classfile.descriptors.SignatureSymbols; import com.oracle.svm.espresso.classfile.descriptors.Symbols; import com.oracle.svm.espresso.classfile.descriptors.TypeSymbols; @@ -49,9 +48,8 @@ public final class SymbolsSupport { @Platforms(Platform.HOSTED_ONLY.class) public SymbolsSupport() { - ParserSymbols.ensureInitialized(); int initialSymbolTableCapacity = 4 * 1024; - Symbols symbols = Symbols.fromExisting(ParserSymbols.SYMBOLS.freeze(), initialSymbolTableCapacity, 0); + Symbols symbols = Symbols.fromExisting(SVMSymbols.SYMBOLS.freeze(), initialSymbolTableCapacity, 0); // let this resize when first used at runtime utf8 = new Utf8Symbols(symbols); names = new NameSymbols(symbols);