diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java index 2592e9bd6086..c92e506525dd 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/util/FrequencyEncoder.java @@ -89,17 +89,6 @@ public void addObject(T object) { entry.frequency++; } - /** - * Returns whether the given object has been previously added to the array. - */ - public boolean contains(T object) { - if (object == null && containsNull) { - return true; - } - Entry entry = map.get(object); - return entry != null; - } - /** * Returns the index of an object in the array. The object must have been * {@link #addObject(Object) added} before. diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java index e3a9b2193c33..c1df478a92b6 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeReflectionSupport.java @@ -51,5 +51,5 @@ public interface RuntimeReflectionSupport extends ReflectionRegistry { * Returns the methods that shadow a superclass method registered for reflection, to be excluded * from reflection queries. */ - Set getHiddenMethods(); + Set getHidingMethods(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java index 173879d1b759..631d6136fd7f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/OriginalMethodProvider.java @@ -40,44 +40,22 @@ static Executable getJavaMethod(SnippetReflectionProvider reflectionProvider, Re return ((OriginalMethodProvider) method).getJavaMethod(); } try { - return getJavaMethodInternal(reflectionProvider, method); + ResolvedJavaMethod.Parameter[] parameters = method.getParameters(); + Class[] parameterTypes = new Class[parameters.length]; + ResolvedJavaType declaringClassType = method.getDeclaringClass(); + for (int i = 0; i < parameterTypes.length; i++) { + parameterTypes[i] = OriginalClassProvider.getJavaClass(reflectionProvider, parameters[i].getType().resolve(declaringClassType)); + } + Class declaringClass = OriginalClassProvider.getJavaClass(reflectionProvider, declaringClassType); + if (method.isConstructor()) { + return declaringClass.getDeclaredConstructor(parameterTypes); + } else { + return declaringClass.getDeclaredMethod(method.getName(), parameterTypes); + } } catch (NoSuchMethodException e) { throw AnalysisError.shouldNotReachHere(); } } - static boolean hasJavaMethod(SnippetReflectionProvider reflectionProvider, ResolvedJavaMethod method) { - if (method instanceof OriginalMethodProvider) { - return ((OriginalMethodProvider) method).hasJavaMethod(); - } - try { - getJavaMethodInternal(reflectionProvider, method); - return true; - } catch (NoSuchMethodException | LinkageError | RuntimeException e) { - /* - * These exceptions may happen anytime during the lookup, so we can't simply use the - * result of getJavaMethodInternal. - */ - return false; - } - } - - static Executable getJavaMethodInternal(SnippetReflectionProvider reflectionProvider, ResolvedJavaMethod method) throws NoSuchMethodException { - ResolvedJavaMethod.Parameter[] parameters = method.getParameters(); - Class[] parameterTypes = new Class[parameters.length]; - ResolvedJavaType declaringClassType = method.getDeclaringClass(); - for (int i = 0; i < parameterTypes.length; i++) { - parameterTypes[i] = OriginalClassProvider.getJavaClass(reflectionProvider, parameters[i].getType().resolve(declaringClassType)); - } - Class declaringClass = OriginalClassProvider.getJavaClass(reflectionProvider, declaringClassType); - if (method.isConstructor()) { - return declaringClass.getDeclaredConstructor(parameterTypes); - } else { - return declaringClass.getDeclaredMethod(method.getName(), parameterTypes); - } - } - Executable getJavaMethod(); - - boolean hasJavaMethod(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 12e8443811f0..545a4731fb23 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -554,11 +554,6 @@ public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(universe.getOriginalSnippetReflection(), wrapped); } - @Override - public boolean hasJavaMethod() { - return OriginalMethodProvider.hasJavaMethod(universe.getOriginalSnippetReflection(), wrapped); - } - /** * Unique, per method, context insensitive invoke. The context insensitive invoke uses the * receiver type of the method, i.e., its declaring class. Therefore this invoke will link with diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 38f2756a292b..cf380199738b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -612,25 +612,20 @@ private static boolean collectMethodImplementations(AnalysisMethod method, Analy * method. The method cannot be marked as invoked. */ if (holderOrSubtypeInstantiated || method.isIntrinsicMethod()) { + AnalysisMethod aResolved; try { - AnalysisMethod aResolved = holder.resolveConcreteMethod(method, null); - if (aResolved != null) { - /* - * aResolved == null means that the method in the base class was called, but - * never with this holder. - */ - if (includeInlinedMethods ? aResolved.isReachable() : aResolved.isImplementationInvoked()) { - implementations.add(aResolved); - } - } + aResolved = holder.resolveConcreteMethod(method, null); } catch (UnsupportedFeatureException e) { + /* An unsupported overriding method is not reachable. */ + aResolved = null; + } + if (aResolved != null) { /* - * Failing the lookup for subclass implementations is acceptable when the method is - * never called. This happens because an AnalysisMethod object can be created during - * any lookup for a method, including when it is not reachable. + * aResolved == null means that the method in the base class was called, but never + * with this holder. */ - if (method.isReachable()) { - throw e; + if (includeInlinedMethods ? aResolved.isReachable() : aResolved.isImplementationInvoked()) { + implementations.add(aResolved); } } } diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java index 16ddb241dbd3..86e016b27993 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/OmitPreviousConfigTests.java @@ -310,7 +310,7 @@ void doTest() { Assert.assertNotNull("Generated configuration type " + name + " does not exist. Has the test code changed?", configurationType); for (Map.Entry methodEntry : methodsThatMustExist.entrySet()) { - ConfigurationMemberDeclaration kind = ConfigurationType.TestBackdoor.getMethodInfoIfPresent(configurationType, methodEntry.getKey()).getMemberKind(); + ConfigurationMemberDeclaration kind = ConfigurationType.TestBackdoor.getMethodInfoIfPresent(configurationType, methodEntry.getKey()).getDeclaration(); Assert.assertNotNull("Method " + methodEntry.getKey() + " unexpectedly NOT found in the new configuration.", kind); Assert.assertEquals("Method " + methodEntry.getKey() + " contains a different kind than expected in the new configuration.", kind, methodEntry.getValue()); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMemberInfo.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMemberInfo.java index 9e53fca94891..fb1e676155ff 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMemberInfo.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationMemberInfo.java @@ -35,20 +35,20 @@ public final class ConfigurationMemberInfo { } } - private final ConfigurationMemberDeclaration memberKind; - private final ConfigurationMemberAccessibility accessKind; + private final ConfigurationMemberDeclaration declaration; + private final ConfigurationMemberAccessibility accessibility; - private ConfigurationMemberInfo(ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { - this.memberKind = memberKind; - this.accessKind = accessKind; + private ConfigurationMemberInfo(ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { + this.declaration = declaration; + this.accessibility = accessibility; } - public ConfigurationMemberDeclaration getMemberKind() { - return memberKind; + public ConfigurationMemberDeclaration getDeclaration() { + return declaration; } - public ConfigurationMemberAccessibility getAccessKind() { - return accessKind; + public ConfigurationMemberAccessibility getAccessibility() { + return accessibility; } public static ConfigurationMemberInfo get(ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { @@ -103,8 +103,19 @@ public boolean includes(ConfigurationMemberDeclaration other) { } public enum ConfigurationMemberAccessibility { + /** + * The member is not accessed reflectively. + */ NONE, + + /** + * The member is queried reflectively but never invoked (only for methods and constructors). + */ QUERIED, + + /** + * The member is fully accessed reflectively. + */ ACCESSED; public ConfigurationMemberAccessibility combine(ConfigurationMemberAccessibility other) { @@ -121,14 +132,14 @@ public boolean includes(ConfigurationMemberAccessibility other) { } public ConfigurationMemberInfo intersect(ConfigurationMemberInfo other) { - return get(memberKind.intersect(other.memberKind), accessKind.combine(other.accessKind)); + return get(declaration.intersect(other.declaration), accessibility.combine(other.accessibility)); } public ConfigurationMemberInfo union(ConfigurationMemberInfo other) { - return get(memberKind.union(other.memberKind), accessKind.combine(other.accessKind)); + return get(declaration.union(other.declaration), accessibility.combine(other.accessibility)); } public boolean includes(ConfigurationMemberInfo other) { - return memberKind.includes(other.memberKind) && accessKind.includes(other.accessKind); + return declaration.includes(other.declaration) && accessibility.includes(other.accessibility); } } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java index 10d0186441fb..960010051069 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationType.java @@ -234,10 +234,10 @@ public String getQualifiedJavaName() { return qualifiedJavaName; } - public synchronized void addField(String name, ConfigurationMemberDeclaration memberKind, boolean finalButWritable) { + public synchronized void addField(String name, ConfigurationMemberDeclaration declaration, boolean finalButWritable) { if (!finalButWritable) { - if ((memberKind.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredFields) || - (memberKind.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicFields)) { + if ((declaration.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredFields) || + (declaration.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicFields)) { fields = maybeRemove(fields, map -> { FieldInfo fieldInfo = map.get(name); if (fieldInfo != null && !fieldInfo.isFinalButWritable()) { @@ -251,30 +251,30 @@ public synchronized void addField(String name, ConfigurationMemberDeclaration me fields = new HashMap<>(); } fields.compute(name, (k, v) -> (v != null) - ? FieldInfo.get(v.getKind().intersect(memberKind), v.isFinalButWritable() || finalButWritable) - : FieldInfo.get(memberKind, finalButWritable)); + ? FieldInfo.get(v.getKind().intersect(declaration), v.isFinalButWritable() || finalButWritable) + : FieldInfo.get(declaration, finalButWritable)); } - public void addMethodsWithName(String name, ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { - addMethod(name, null, memberKind, accessKind); + public void addMethodsWithName(String name, ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { + addMethod(name, null, declaration, accessibility); } - public void addMethod(String name, String internalSignature, ConfigurationMemberDeclaration memberKind) { - addMethod(name, internalSignature, memberKind, ConfigurationMemberAccessibility.ACCESSED); + public void addMethod(String name, String internalSignature, ConfigurationMemberDeclaration declaration) { + addMethod(name, internalSignature, declaration, ConfigurationMemberAccessibility.ACCESSED); } - public synchronized void addMethod(String name, String internalSignature, ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { - ConfigurationMemberInfo kind = ConfigurationMemberInfo.get(memberKind, accessKind); + public synchronized void addMethod(String name, String internalSignature, ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { + ConfigurationMemberInfo kind = ConfigurationMemberInfo.get(declaration, accessibility); boolean matchesAllSignatures = (internalSignature == null); - if (ConfigurationMethod.isConstructorName(name) ? hasAllConstructors(memberKind, accessKind) : hasAllMethods(memberKind, accessKind)) { + if (ConfigurationMethod.isConstructorName(name) ? hasAllConstructors(declaration, accessibility) : hasAllMethods(declaration, accessibility)) { if (!matchesAllSignatures) { - if (accessKind == ConfigurationMemberAccessibility.ACCESSED) { + if (accessibility == ConfigurationMemberAccessibility.ACCESSED) { methods = maybeRemove(methods, map -> map.remove(new ConfigurationMethod(name, internalSignature))); - } else if (accessKind == ConfigurationMemberAccessibility.QUERIED) { + } else if (accessibility == ConfigurationMemberAccessibility.QUERIED) { methods = maybeRemove(methods, map -> { ConfigurationMethod method = new ConfigurationMethod(name, internalSignature); /* Querying all methods should not remove individually accessed methods. */ - if (map.containsKey(method) && map.get(method).getAccessKind() == ConfigurationMemberAccessibility.QUERIED) { + if (map.containsKey(method) && map.get(method).getAccessibility() == ConfigurationMemberAccessibility.QUERIED) { map.remove(method); } }); @@ -296,14 +296,14 @@ public synchronized void addMethod(String name, String internalSignature, Config assert methods.containsKey(method); } - private boolean hasAllConstructors(ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { - return (memberKind.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredConstructorsAccess.includes(accessKind)) || - (memberKind.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicConstructorsAccess.includes(accessKind)); + private boolean hasAllConstructors(ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { + return (declaration.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredConstructorsAccess.includes(accessibility)) || + (declaration.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicConstructorsAccess.includes(accessibility)); } - private boolean hasAllMethods(ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind) { - return (memberKind.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredMethodsAccess.includes(accessKind)) || - (memberKind.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicMethodsAccess.includes(accessKind)); + private boolean hasAllMethods(ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility) { + return (declaration.includes(ConfigurationMemberDeclaration.DECLARED) && allDeclaredMethodsAccess.includes(accessibility)) || + (declaration.includes(ConfigurationMemberDeclaration.PUBLIC) && allPublicMethodsAccess.includes(accessibility)); } public synchronized void setAllDeclaredClasses() { @@ -324,31 +324,31 @@ public synchronized void setAllPublicFields() { removeFields(ConfigurationMemberDeclaration.PUBLIC); } - public synchronized void setAllDeclaredMethods(ConfigurationMemberAccessibility accessKind) { - if (!allDeclaredMethodsAccess.includes(accessKind)) { - allDeclaredMethodsAccess = accessKind; - removeMethods(ConfigurationMemberDeclaration.DECLARED, accessKind, false); + public synchronized void setAllDeclaredMethods(ConfigurationMemberAccessibility accessibility) { + if (!allDeclaredMethodsAccess.includes(accessibility)) { + allDeclaredMethodsAccess = accessibility; + removeMethods(ConfigurationMemberDeclaration.DECLARED, accessibility, false); } } - public synchronized void setAllPublicMethods(ConfigurationMemberAccessibility accessKind) { - if (!allPublicMethodsAccess.includes(accessKind)) { - allPublicMethodsAccess = accessKind; - removeMethods(ConfigurationMemberDeclaration.PUBLIC, accessKind, false); + public synchronized void setAllPublicMethods(ConfigurationMemberAccessibility accessibility) { + if (!allPublicMethodsAccess.includes(accessibility)) { + allPublicMethodsAccess = accessibility; + removeMethods(ConfigurationMemberDeclaration.PUBLIC, accessibility, false); } } - public synchronized void setAllDeclaredConstructors(ConfigurationMemberAccessibility accessKind) { - if (!allDeclaredConstructorsAccess.includes(accessKind)) { - allDeclaredConstructorsAccess = accessKind; - removeMethods(ConfigurationMemberDeclaration.DECLARED, accessKind, true); + public synchronized void setAllDeclaredConstructors(ConfigurationMemberAccessibility accessibility) { + if (!allDeclaredConstructorsAccess.includes(accessibility)) { + allDeclaredConstructorsAccess = accessibility; + removeMethods(ConfigurationMemberDeclaration.DECLARED, accessibility, true); } } - public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibility accessKind) { - if (!allPublicConstructorsAccess.includes(accessKind)) { - allPublicConstructorsAccess = accessKind; - removeMethods(ConfigurationMemberDeclaration.PUBLIC, accessKind, true); + public synchronized void setAllPublicConstructors(ConfigurationMemberAccessibility accessibility) { + if (!allPublicConstructorsAccess.includes(accessibility)) { + allPublicConstructorsAccess = accessibility; + removeMethods(ConfigurationMemberDeclaration.PUBLIC, accessibility, true); } } @@ -375,7 +375,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException { JsonPrinter.printCollection(writer, fields.entrySet(), Map.Entry.comparingByKey(), ConfigurationType::printField); } if (methods != null) { - Set accessedMethods = getMethodsForAccessKind(ConfigurationMemberAccessibility.ACCESSED); + Set accessedMethods = getMethodsByAccessibility(ConfigurationMemberAccessibility.ACCESSED); if (!accessedMethods.isEmpty()) { writer.append(',').newline().quote("methods").append(':'); JsonPrinter.printCollection(writer, @@ -383,7 +383,7 @@ public synchronized void printJson(JsonWriter writer) throws IOException { Comparator.comparing(ConfigurationMethod::getName).thenComparing(Comparator.nullsFirst(Comparator.comparing(ConfigurationMethod::getInternalSignature))), JsonPrintable::printJson); } - Set queriedMethods = getMethodsForAccessKind(ConfigurationMemberAccessibility.QUERIED); + Set queriedMethods = getMethodsByAccessibility(ConfigurationMemberAccessibility.QUERIED); if (!queriedMethods.isEmpty()) { writer.append(',').newline().quote("queriedMethods").append(':'); JsonPrinter.printCollection(writer, @@ -396,8 +396,8 @@ public synchronized void printJson(JsonWriter writer) throws IOException { writer.append('}').unindent().newline(); } - private Set getMethodsForAccessKind(ConfigurationMemberAccessibility accessKind) { - return methods.entrySet().stream().filter(e -> e.getValue().getAccessKind() == accessKind).map(Map.Entry::getKey).collect(Collectors.toSet()); + private Set getMethodsByAccessibility(ConfigurationMemberAccessibility accessibility) { + return methods.entrySet().stream().filter(e -> e.getValue().getAccessibility() == accessibility).map(Map.Entry::getKey).collect(Collectors.toSet()); } private static void printField(Map.Entry entry, JsonWriter w) throws IOException { @@ -414,12 +414,12 @@ private static void optionallyPrintJsonBoolean(JsonWriter writer, boolean predic } } - private void removeFields(ConfigurationMemberDeclaration memberKind) { - fields = maybeRemove(fields, map -> map.values().removeIf(v -> memberKind.includes(v.getKind()))); + private void removeFields(ConfigurationMemberDeclaration declaration) { + fields = maybeRemove(fields, map -> map.values().removeIf(v -> declaration.includes(v.getKind()))); } - private void removeMethods(ConfigurationMemberDeclaration memberKind, ConfigurationMemberAccessibility accessKind, boolean constructors) { - ConfigurationMemberInfo kind = ConfigurationMemberInfo.get(memberKind, accessKind); + private void removeMethods(ConfigurationMemberDeclaration declaration, ConfigurationMemberAccessibility accessibility, boolean constructors) { + ConfigurationMemberInfo kind = ConfigurationMemberInfo.get(declaration, accessibility); methods = maybeRemove(methods, map -> map.entrySet().removeIf(entry -> entry.getKey().isConstructor() == constructors && kind.includes(entry.getValue()))); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java index 7138da1ce204..1f0ba777fcef 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/JniProcessor.java @@ -85,7 +85,7 @@ void processEntry(Map entry) { } String declaringClass = (String) entry.get("declaring_class"); String declaringClassOrClazz = (declaringClass != null) ? declaringClass : clazz; - ConfigurationMemberDeclaration memberKind = (declaringClass != null) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; + ConfigurationMemberDeclaration declaration = (declaringClass != null) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; TypeConfiguration config = configuration; switch (function) { case "GetStaticMethodID": @@ -94,7 +94,7 @@ void processEntry(Map entry) { String name = (String) args.get(0); String signature = (String) args.get(1); if (!advisor.shouldIgnoreJniMethodLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, memberKind); + config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); } @@ -105,7 +105,7 @@ void processEntry(Map entry) { case "GetStaticFieldID": { expectSize(args, 2); String name = (String) args.get(0); - config.getOrCreateType(condition, declaringClassOrClazz).addField(name, memberKind, false); + config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); if (!declaringClassOrClazz.equals(clazz)) { config.getOrCreateType(condition, clazz); } @@ -116,7 +116,7 @@ void processEntry(Map entry) { String name = ConfigurationMethod.CONSTRUCTOR_NAME; String signature = "(Ljava/lang/String;)V"; if (!advisor.shouldIgnoreJniMethodLookup(lazyValue(clazz), lazyValue(name), lazyValue(signature), callerClassLazyValue)) { - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, memberKind); + config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); assert declaringClassOrClazz.equals(clazz) : "Constructor can only be accessed via declaring class"; } break; @@ -126,7 +126,7 @@ void processEntry(Map entry) { case "FromReflectedField": { expectSize(args, 1); String name = (String) args.get(0); - config.getOrCreateType(condition, declaringClassOrClazz).addField(name, memberKind, false); + config.getOrCreateType(condition, declaringClassOrClazz).addField(name, declaration, false); break; } case "ToReflectedMethod": @@ -135,7 +135,7 @@ void processEntry(Map entry) { expectSize(args, 2); String name = (String) args.get(0); String signature = (String) args.get(1); - config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, memberKind); + config.getOrCreateType(condition, declaringClassOrClazz).addMethod(name, signature, declaration); break; } case "NewObjectArray": { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 413f76edaf9c..e4eb9ffaf46a 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -118,8 +118,8 @@ public void processEntry(Map entry) { if (advisor.shouldIgnore(lazyValue(clazz), lazyValue(callerClass))) { return; } - ConfigurationMemberDeclaration memberKind = ConfigurationMemberDeclaration.PUBLIC; - ConfigurationMemberAccessibility accessKind = ConfigurationMemberAccessibility.QUERIED; + ConfigurationMemberDeclaration declaration = ConfigurationMemberDeclaration.PUBLIC; + ConfigurationMemberAccessibility accessibility = ConfigurationMemberAccessibility.QUERIED; String clazzOrDeclaringClass = entry.containsKey("declaring_class") ? (String) entry.get("declaring_class") : clazz; switch (function) { case "getDeclaredFields": { @@ -132,23 +132,23 @@ public void processEntry(Map entry) { } case "getDeclaredMethods": { - configuration.getOrCreateType(condition, clazz).setAllDeclaredMethods(accessKind); + configuration.getOrCreateType(condition, clazz).setAllDeclaredMethods(accessibility); break; } case "asInterfaceInstance": - accessKind = ConfigurationMemberAccessibility.ACCESSED; + accessibility = ConfigurationMemberAccessibility.ACCESSED; // fallthrough case "getMethods": { - configuration.getOrCreateType(condition, clazz).setAllPublicMethods(accessKind); + configuration.getOrCreateType(condition, clazz).setAllPublicMethods(accessibility); break; } case "getDeclaredConstructors": { - configuration.getOrCreateType(condition, clazz).setAllDeclaredConstructors(accessKind); + configuration.getOrCreateType(condition, clazz).setAllDeclaredConstructors(accessibility); break; } case "getConstructors": { - configuration.getOrCreateType(condition, clazz).setAllPublicConstructors(accessKind); + configuration.getOrCreateType(condition, clazz).setAllPublicConstructors(accessibility); break; } @@ -165,10 +165,10 @@ public void processEntry(Map entry) { case "findFieldHandle": case "unreflectField": case "getDeclaredField": - memberKind = "findFieldHandle".equals(function) ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED; + declaration = "findFieldHandle".equals(function) ? ConfigurationMemberDeclaration.PRESENT : ConfigurationMemberDeclaration.DECLARED; // fall through case "getField": { - configuration.getOrCreateType(condition, clazzOrDeclaringClass).addField(singleElement(args), memberKind, false); + configuration.getOrCreateType(condition, clazzOrDeclaringClass).addField(singleElement(args), declaration, false); if (!clazzOrDeclaringClass.equals(clazz)) { configuration.getOrCreateType(condition, clazz); } @@ -178,17 +178,17 @@ public void processEntry(Map entry) { case "getDeclaredMethod": case "findMethodHandle": case "invokeMethod": - memberKind = "getDeclaredMethod".equals(function) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; + declaration = "getDeclaredMethod".equals(function) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; // fall through case "getMethod": { - accessKind = (function.equals("invokeMethod") || function.equals("findMethodHandle")) ? ConfigurationMemberAccessibility.ACCESSED : ConfigurationMemberAccessibility.QUERIED; + accessibility = (function.equals("invokeMethod") || function.equals("findMethodHandle")) ? ConfigurationMemberAccessibility.ACCESSED : ConfigurationMemberAccessibility.QUERIED; expectSize(args, 2); String name = (String) args.get(0); List parameterTypes = (List) args.get(1); if (parameterTypes == null) { // tolerated and equivalent to no parameter types parameterTypes = Collections.emptyList(); } - configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(name, SignatureUtil.toInternalSignature(parameterTypes), memberKind, accessKind); + configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(name, SignatureUtil.toInternalSignature(parameterTypes), declaration, accessibility); if (!clazzOrDeclaringClass.equals(clazz)) { configuration.getOrCreateType(condition, clazz); } @@ -198,17 +198,18 @@ public void processEntry(Map entry) { case "getDeclaredConstructor": case "findConstructorHandle": case "invokeConstructor": - memberKind = "getDeclaredConstructor".equals(function) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; + declaration = "getDeclaredConstructor".equals(function) ? ConfigurationMemberDeclaration.DECLARED : ConfigurationMemberDeclaration.PRESENT; // fall through case "getConstructor": { - accessKind = (function.equals("invokeConstructor") || function.equals("findConstructorHandle")) ? ConfigurationMemberAccessibility.ACCESSED : ConfigurationMemberAccessibility.QUERIED; + accessibility = (function.equals("invokeConstructor") || function.equals("findConstructorHandle")) ? ConfigurationMemberAccessibility.ACCESSED + : ConfigurationMemberAccessibility.QUERIED; List parameterTypes = singleElement(args); if (parameterTypes == null) { // tolerated and equivalent to no parameter types parameterTypes = Collections.emptyList(); } String signature = SignatureUtil.toInternalSignature(parameterTypes); assert clazz.equals(clazzOrDeclaringClass) : "Constructor can only be accessed via declaring class"; - configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, signature, memberKind, accessKind); + configuration.getOrCreateType(condition, clazzOrDeclaringClass).addMethod(ConfigurationMethod.CONSTRUCTOR_NAME, signature, declaration, accessibility); break; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 24d08478557f..5405cef41dc4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -629,7 +629,7 @@ protected void onValueUpdate(EconomicMap, Object> values, String ol }; @APIOption(name = "configure-reflection-metadata")// - @Option(help = "Limit method reflection metadata to configuration entries instead of including it for all reachable methods")// + @Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert)// public static final HostedOptionKey ConfigureReflectionMetadata = new HostedOptionKey<>(true); @Option(help = "Include a list of methods included in the image for runtime inspection.", type = OptionType.Expert)// diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java index f51e893a0c41..5cbe41e8aeba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoDecoder.java @@ -28,27 +28,15 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; -import java.lang.reflect.Executable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.graalvm.collections.Pair; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.core.common.util.TypeConversion; -import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; -import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AlwaysInline; import com.oracle.svm.core.annotate.Uninterruptible; -import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.heap.ReferenceMapIndex; -import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.jdk.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.option.HostedOptionKey; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; import com.oracle.svm.core.util.ByteArrayReader; import com.oracle.svm.core.util.Counter; import com.oracle.svm.core.util.NonmovableByteArrayReader; @@ -505,211 +493,6 @@ private static long advanceOffset(long entryOffset, int entryFlags) { static CodeInfoDecoderCounters counters() { return ImageSingletons.lookup(CodeInfoDecoderCounters.class); } - - public static class MethodDescriptor { - private final String name; - private final Class[] parameterTypes; - - public MethodDescriptor(String name, Class[] parameterTypes) { - this.name = name; - this.parameterTypes = parameterTypes; - } - - public String getName() { - return name; - } - - public Class[] getParameterTypes() { - return parameterTypes; - } - } - - public static Pair getMethodMetadata(DynamicHub declaringType) { - return getMethodMetadata(declaringType.getTypeID(), true); - } - - public static Executable[] getAllMethodMetadata() { - List allMethods = new ArrayList<>(); - for (int i = 0; i < ImageSingletons.lookup(MethodMetadataEncoding.class).getIndexEncoding().length / Integer.BYTES; ++i) { - allMethods.addAll(Arrays.asList(getMethodMetadata(i, false).getLeft())); - } - return allMethods.toArray(new Executable[0]); - } - - /** - * The metadata for methods in the image is split into two arrays: one for the index and the - * other for data. The index contains an array of integers pointing to offsets in the data, and - * indexed by type ID. The data array contains arrays of method metadata, ordered by type ID, - * such that all methods declared by a class are stored consecutively. The data for a method is - * stored in the following format: - * - *
-     * {
-     *     int methodNameIndex;        // index in frameInfoSourceMethodNames ("" for constructors)
-     *     int modifiers;
-     *     int paramCount;
-     *     {
-     *         int paramTypeIndex;     // index in frameInfoSourceClasses
-     *     } paramTypes[paramCount];
-     *     int returnTypeIndex;        // index in frameInfoSourceClasses (void for constructors)
-     *     int exceptionTypeCount;
-     *     {
-     *         int exceptionTypeIndex; // index in frameInfoSourceClasses
-     *     } exceptionTypes[exceptionTypeCount];
-     *     // Annotation encodings (see {@link CodeInfoEncoder})
-     *     int annotationsLength;
-     *     byte[] annotationsEncoding[annotationsLength];
-     *     int parameterAnnotationsLength;
-     *     byte[] parameterAnnotationsEncoding[parameterAnnotationsLength];
-     * }
-     * 
- * - * This method returns two arrays. The first one contains the desired method data, the second - * one contains the names and parameter types of methods shadowing methods declared in - * superclasses which therefore should not be returned by a call to getMethod(). This second - * array is only computed for reflection queries, not for method dumping. - */ - public static Pair getMethodMetadata(int typeID, boolean complete) { - CodeInfo info = CodeInfoTable.getImageCodeInfo(); - MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); - byte[] index = encoding.getIndexEncoding(); - UnsafeArrayTypeReader indexReader = UnsafeArrayTypeReader.create(index, Integer.BYTES * typeID, ByteArrayReader.supportsUnalignedMemoryAccess()); - int offset = indexReader.getS4(); - if (offset == MethodMetadataEncoder.NO_METHOD_METADATA) { - return Pair.create(new Executable[0], new MethodDescriptor[0]); - } - byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); - UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, offset, ByteArrayReader.supportsUnalignedMemoryAccess()); - - Executable[] methods; - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - int methodCount = dataReader.getUVInt(); - methods = new Executable[methodCount]; - for (int i = 0; i < methodCount; ++i) { - int classIndex = dataReader.getSVInt(); - Class declaringClass = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); - - int nameIndex = dataReader.getSVInt(); - /* Interning the string to ensure JDK8 method search succeeds */ - String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex).intern(); - - int modifiers = dataReader.getUVInt(); - - int paramTypeCount = dataReader.getUVInt(); - Class[] paramTypes = new Class[paramTypeCount]; - for (int j = 0; j < paramTypeCount; ++j) { - int paramTypeIndex = dataReader.getSVInt(); - paramTypes[j] = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), paramTypeIndex); - } - - int returnTypeIndex = dataReader.getSVInt(); - Class returnType = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), returnTypeIndex); - - int exceptionCount = dataReader.getUVInt(); - Class[] exceptionTypes = new Class[exceptionCount]; - for (int j = 0; j < exceptionCount; ++j) { - int exceptionTypeIndex = dataReader.getSVInt(); - exceptionTypes[j] = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), exceptionTypeIndex); - } - - int signatureIndex = dataReader.getSVInt(); - String signature = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), signatureIndex); - - int annotationsLength = dataReader.getUVInt(); - byte[] annotations = new byte[annotationsLength]; - for (int j = 0; j < annotationsLength; ++j) { - annotations[j] = (byte) dataReader.getS1(); - } - - int parameterAnnotationsLength = dataReader.getUVInt(); - byte[] parameterAnnotations = new byte[parameterAnnotationsLength]; - for (int j = 0; j < parameterAnnotationsLength; ++j) { - parameterAnnotations[j] = (byte) dataReader.getS1(); - } - - int typeAnnotationsLength = dataReader.getUVInt(); - byte[] typeAnnotations = new byte[typeAnnotationsLength]; - for (int j = 0; j < typeAnnotationsLength; ++j) { - typeAnnotations[j] = (byte) dataReader.getS1(); - } - - boolean reflectParameterDataPresent = dataReader.getU1() == 1; - String[] reflectParameterNames = null; - int[] reflectParameterModifiers = null; - if (reflectParameterDataPresent) { - int reflectParameterCount = dataReader.getUVInt(); - reflectParameterNames = new String[reflectParameterCount]; - reflectParameterModifiers = new int[reflectParameterCount]; - for (int j = 0; j < reflectParameterCount; ++j) { - int reflectParameterNameIndex = dataReader.getSVInt(); - reflectParameterNames[j] = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), reflectParameterNameIndex); - reflectParameterModifiers[j] = dataReader.getS4(); - } - } - - if (name.equals("")) { - assert returnType == void.class; - methods[i] = ImageSingletons.lookup(RuntimeReflectionConstructors.class).newConstructor(declaringClass, paramTypes, exceptionTypes, modifiers, signature, - annotations, parameterAnnotations, typeAnnotations, reflectParameterNames, reflectParameterModifiers); - } else { - methods[i] = ImageSingletons.lookup(RuntimeReflectionConstructors.class).newMethod(declaringClass, name, paramTypes, returnType, exceptionTypes, modifiers, signature, - annotations, parameterAnnotations, null, typeAnnotations, reflectParameterNames, reflectParameterModifiers); - } - } - int hiddenMethodCount = dataReader.getUVInt(); - MethodDescriptor[] hiddenMethods = new MethodDescriptor[hiddenMethodCount]; - for (int i = 0; i < hiddenMethodCount; ++i) { - int nameIndex = dataReader.getSVInt(); - /* Interning the string to ensure JDK8 method search succeeds */ - String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex).intern(); - - int paramCount = dataReader.getUVInt(); - Class[] paramTypes = new Class[paramCount]; - for (int j = 0; j < paramCount; ++j) { - int paramTypeIndex = dataReader.getSVInt(); - paramTypes[j] = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), paramTypeIndex); - } - - hiddenMethods[i] = new MethodDescriptor(name, paramTypes); - } - if (complete) { - return Pair.create(methods, hiddenMethods); - } - } - if (SubstrateOptions.IncludeMethodData.getValue() && !complete) { - int methodCount = dataReader.getUVInt(); - methods = new Executable[methodCount]; - for (int i = 0; i < methodCount; ++i) { - int classIndex = dataReader.getSVInt(); - Class declaringClass = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); - - int nameIndex = dataReader.getSVInt(); - /* Interning the string to ensure JDK8 method search succeeds */ - String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex).intern(); - - int paramCount = dataReader.getUVInt(); - Class[] paramTypes = new Class[paramCount]; - for (int j = 0; j < paramCount; ++j) { - int paramTypeIndex = dataReader.getSVInt(); - paramTypes[j] = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), paramTypeIndex); - } - - if (name.equals("")) { - methods[i] = ImageSingletons.lookup(RuntimeReflectionConstructors.class).newConstructor(declaringClass, paramTypes, null, 0, null, - null, null, null, null, null); - } else { - methods[i] = ImageSingletons.lookup(RuntimeReflectionConstructors.class).newMethod(declaringClass, name, paramTypes, null, null, 0, null, - null, null, null, null, null, null); - } - } - return Pair.create(methods, new MethodDescriptor[0]); - } - return Pair.create(new Executable[0], new MethodDescriptor[0]); - } - - public static Target_jdk_internal_reflect_ConstantPool getMetadataPseudoConstantPool() { - return new Target_jdk_internal_reflect_ConstantPool(); - } } class CodeInfoDecoderCounters { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index c149333e8442..7b7d14a99eef 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -26,9 +26,6 @@ import static com.oracle.svm.core.util.VMError.shouldNotReachHere; -// Checkstyle: stop -import java.lang.reflect.Executable; -// Checkstyle: resume import java.util.BitSet; import java.util.TreeMap; @@ -39,15 +36,12 @@ import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; import com.oracle.svm.core.CalleeSavedRegisters; import com.oracle.svm.core.ReservedRegisters; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.NonmovableArray; import com.oracle.svm.core.c.NonmovableArrays; @@ -108,11 +102,11 @@ public static class Counters { public static final class Encoders { final FrequencyEncoder objectConstants; - final FrequencyEncoder> sourceClasses; - final FrequencyEncoder sourceMethodNames; + public final FrequencyEncoder> sourceClasses; + public final FrequencyEncoder sourceMethodNames; final FrequencyEncoder names; - private Encoders() { + public Encoders() { this.objectConstants = FrequencyEncoder.createEqualityEncoder(); this.sourceClasses = FrequencyEncoder.createEqualityEncoder(); this.sourceMethodNames = FrequencyEncoder.createEqualityEncoder(); @@ -160,20 +154,14 @@ static class IPData { private final Encoders encoders; private final FrameInfoEncoder frameInfoEncoder; - @Platforms(Platform.HOSTED_ONLY.class)// - private MethodMetadataEncoder methodMetadataEncoder; - private NonmovableArray codeInfoIndex; private NonmovableArray codeInfoEncodings; private NonmovableArray referenceMapEncoding; - public CodeInfoEncoder(FrameInfoEncoder.Customization frameInfoCustomization) { + public CodeInfoEncoder(FrameInfoEncoder.Customization frameInfoCustomization, Encoders encoders) { this.entries = new TreeMap<>(); - this.encoders = new Encoders(); + this.encoders = encoders; this.frameInfoEncoder = new FrameInfoEncoder(frameInfoCustomization, encoders); - if (SubstrateUtil.HOSTED) { - this.methodMetadataEncoder = new MethodMetadataEncoder(encoders); - } } public static int getEntryOffset(Infopoint infopoint) { @@ -230,18 +218,6 @@ public void addMethod(SharedMethod method, CompilationResult compilation, int co ImageSingletons.lookup(Counters.class).codeSize.add(compilation.getTargetCodeSize()); } - public void prepareMetadataForClass(Class clazz) { - methodMetadataEncoder.prepareMetadataForClass(clazz); - } - - public void prepareMetadataForMethod(SharedMethod method, Executable reflectMethod, boolean complete) { - methodMetadataEncoder.prepareMetadataForMethod(method, reflectMethod, complete); - } - - public void prepareHiddenMethodMetadata(SharedType type, String name, Class[] parameterTypes) { - methodMetadataEncoder.prepareHiddenMethodMetadata(type, name, parameterTypes); - } - private IPData makeEntry(long ip) { IPData result = entries.get(ip); if (result == null) { @@ -256,9 +232,6 @@ public void encodeAllAndInstall(CodeInfo target, ReferenceAdjuster adjuster) { encoders.encodeAllAndInstall(target, adjuster); encodeReferenceMaps(); frameInfoEncoder.encodeAllAndInstall(target); - if (SubstrateUtil.HOSTED) { - methodMetadataEncoder.encodeAllAndInstall(); - } encodeIPData(); install(target); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index ba39aa63e0e5..0dd70333fd63 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -278,7 +278,6 @@ public void duringSetup(DuringSetupAccess access) { ImageSingletons.add(RuntimeCodeInfoHistory.class, new RuntimeCodeInfoHistory()); ImageSingletons.add(RuntimeCodeCache.class, new RuntimeCodeCache()); ImageSingletons.add(RuntimeCodeInfoMemory.class, new RuntimeCodeInfoMemory()); - ImageSingletons.add(MethodMetadataEncoding.class, new MethodMetadataEncoding()); } @Override 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 8afb64453225..ecb172e5694d 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 @@ -48,14 +48,11 @@ import java.security.CodeSource; import java.security.ProtectionDomain; import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.StringJoiner; -import org.graalvm.collections.Pair; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; @@ -78,15 +75,12 @@ import com.oracle.svm.core.annotate.UnknownObjectField; import com.oracle.svm.core.classinitialization.ClassInitializationInfo; import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode; -import com.oracle.svm.core.code.CodeInfoDecoder; -import com.oracle.svm.core.code.CodeInfoDecoder.MethodDescriptor; import com.oracle.svm.core.jdk.JDK11OrLater; import com.oracle.svm.core.jdk.JDK17OrLater; import com.oracle.svm.core.jdk.JDK8OrEarlier; import com.oracle.svm.core.jdk.Package_jdk_internal_reflect; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.Target_java_lang_Module; -import com.oracle.svm.core.jdk.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.jdk.Target_jdk_internal_reflect_Reflection; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.util.LazyFinalReference; @@ -1068,7 +1062,7 @@ private static boolean z(Object[] array) { // for better readability above */ final Executable enclosingMethodOrConstructor; - private ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor[] declaredConstructors, + ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] publicUnhiddenFields, Method[] declaredMethods, Method[] publicMethods, Constructor[] declaredConstructors, Constructor[] publicConstructors, Constructor nullaryConstructor, Field[] declaredPublicFields, Method[] declaredPublicMethods, Class[] declaredClasses, Class[] publicClasses, Executable enclosingMethodOrConstructor, Object[] recordComponents) { @@ -1093,18 +1087,13 @@ private ReflectionData(Field[] declaredFields, Field[] publicFields, Field[] pub static final class Target_java_lang_Class_MethodArray { } - private ReflectionData rd = ReflectionData.EMPTY; + ReflectionData rd = ReflectionData.EMPTY; @Platforms(Platform.HOSTED_ONLY.class) public void setReflectionData(ReflectionData rd) { this.rd = rd; } - @Platforms(Platform.HOSTED_ONLY.class) - public Executable[] hostedGetDeclaredMethods() { - return rd.declaredMethods; - } - @KeepOriginal private native Field[] getFields(); @@ -1203,81 +1192,6 @@ private Method[] privateGetDeclaredMethods(boolean publicOnly) { return publicOnly ? companion.get().getCompleteReflectionData().declaredPublicMethods : companion.get().getCompleteReflectionData().declaredMethods; } - ReflectionData loadReflectionMetadata() { - Pair data = CodeInfoDecoder.getMethodMetadata(this); - Executable[] methodData = data.getLeft(); - MethodDescriptor[] hiddenMethodData = data.getRight(); - - List newDeclaredMethods = new ArrayList<>(Arrays.asList(rd.declaredMethods)); - List newPublicMethods = new ArrayList<>(Arrays.asList(rd.publicMethods)); - List> newDeclaredConstructors = new ArrayList<>(Arrays.asList(rd.declaredConstructors)); - List> newPublicConstructors = new ArrayList<>(Arrays.asList(rd.publicConstructors)); - List newDeclaredPublicMethods = new ArrayList<>(Arrays.asList(rd.declaredPublicMethods)); - - outer: for (Executable method : methodData) { - if (method instanceof Constructor) { - Constructor c = (Constructor) method; - for (Constructor c2 : rd.declaredConstructors) { - if (Arrays.equals(c.getParameterTypes(), c2.getParameterTypes())) { - continue outer; - } - } - newDeclaredConstructors.add(c); - if (Modifier.isPublic(c.getModifiers())) { - newPublicConstructors.add(c); - } - } else { - Method m = (Method) method; - for (Method m2 : rd.declaredMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - continue outer; - } - } - newDeclaredMethods.add(m); - if (Modifier.isPublic(m.getModifiers())) { - newPublicMethods.add(m); - newDeclaredPublicMethods.add(m); - } - } - } - - /* Recursively add public superclass methods to the public methods list */ - if (superHub != null) { - addInheritedPublicMethods(newPublicMethods, superHub, hiddenMethodData); - } - for (DynamicHub superintfc : getInterfaces()) { - addInheritedPublicMethods(newPublicMethods, superintfc, hiddenMethodData); - } - - return new ReflectionData(rd.declaredFields, rd.publicFields, rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]), newPublicMethods.toArray(new Method[0]), - newDeclaredConstructors.toArray(new Constructor[0]), newPublicConstructors.toArray(new Constructor[0]), rd.nullaryConstructor, rd.declaredPublicFields, - newDeclaredPublicMethods.toArray(new Method[0]), rd.declaredClasses, rd.publicClasses, rd.enclosingMethodOrConstructor, rd.recordComponents); - } - - private void addInheritedPublicMethods(List newPublicMethods, DynamicHub parentHub, MethodDescriptor[] hiddenMethods) { - outer: for (Method m : parentHub.companion.get().getCompleteReflectionData().publicMethods) { - if (!isInterface() && parentHub.isInterface() && Modifier.isStatic(m.getModifiers())) { - continue; - } - for (Method m2 : newPublicMethods) { - if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { - if (m.getDeclaringClass() != m2.getDeclaringClass() && m2.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) { - /* Need to store the more specific method */ - newPublicMethods.remove(m2); - newPublicMethods.add(m); - } - continue outer; - } - } - for (MethodDescriptor hiddenMethod : hiddenMethods) { - if (m.getName().equals(hiddenMethod.getName()) && Arrays.equals(m.getParameterTypes(), hiddenMethod.getParameterTypes())) { - continue outer; - } - } - newPublicMethods.add(m); - } - } - @Substitute @TargetElement(name = "privateGetPublicFields", onlyWith = JDK8OrEarlier.class) private Field[] privateGetPublicFieldsJDK8OrEarlier(@SuppressWarnings("unused") Set> traversedInterfaces) { @@ -1291,7 +1205,7 @@ private Field[] privateGetPublicFieldsJDK11OrLater() { } @Substitute - private Method[] privateGetPublicMethods() { + Method[] privateGetPublicMethods() { return companion.get().getCompleteReflectionData().publicMethods; } @@ -1756,6 +1670,10 @@ public static Target_jdk_internal_reflect_ReflectionFactory getReflectionFactory } } +@TargetClass(classNameProvider = Package_jdk_internal_reflect.class, className = "ConstantPool") +final class Target_jdk_internal_reflect_ConstantPool { +} + @TargetClass(className = "java.lang.reflect.RecordComponent", onlyWith = JDK17OrLater.class) final class Target_java_lang_reflect_RecordComponent { } 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 e5ef466c8ea8..7ed1e196a7fa 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 @@ -24,11 +24,25 @@ */ package com.oracle.svm.core.hub; +// Checkstyle: allow reflection + import static com.oracle.svm.core.hub.DynamicHub.NO_CLASS_LOADER; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.security.ProtectionDomain; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.graalvm.collections.Pair; +import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.hub.DynamicHub.ReflectionData; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor; import com.oracle.svm.core.util.VMError; /** An optional, non-immutable companion to a {@link DynamicHub} instance. */ @@ -80,8 +94,80 @@ public void setProtectionDomain(ProtectionDomain domain) { public ReflectionData getCompleteReflectionData() { if (completeReflectionData == null) { - completeReflectionData = hub.loadReflectionMetadata(); + List newDeclaredMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredMethods)); + List newPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.publicMethods)); + List> newDeclaredConstructors = new ArrayList<>(Arrays.asList(hub.rd.declaredConstructors)); + List> newPublicConstructors = new ArrayList<>(Arrays.asList(hub.rd.publicConstructors)); + List newDeclaredPublicMethods = new ArrayList<>(Arrays.asList(hub.rd.declaredPublicMethods)); + + Pair queriedAndHidingMethods = ImageSingletons.lookup(MethodMetadataDecoder.class).getQueriedAndHidingMethods(hub); + Executable[] queriedMethods = queriedAndHidingMethods.getLeft(); + MethodDescriptor[] hidingMethods = queriedAndHidingMethods.getRight(); + newMethods: for (Executable method : queriedMethods) { + if (method instanceof Constructor) { + Constructor c = (Constructor) method; + for (Constructor c2 : hub.rd.declaredConstructors) { + if (Arrays.equals(c.getParameterTypes(), c2.getParameterTypes())) { + continue newMethods; + } + } + newDeclaredConstructors.add(c); + if (Modifier.isPublic(c.getModifiers())) { + newPublicConstructors.add(c); + } + } else { + Method m = (Method) method; + for (Method m2 : hub.rd.declaredMethods) { + if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { + continue newMethods; + } + } + newDeclaredMethods.add(m); + if (Modifier.isPublic(m.getModifiers())) { + newPublicMethods.add(m); + newDeclaredPublicMethods.add(m); + } + } + } + + /* Recursively add public superclass methods to the public methods list */ + DynamicHub superHub = hub.getSuperHub(); + if (superHub != null) { + addInheritedPublicMethods(newPublicMethods, superHub.privateGetPublicMethods(), hidingMethods, true); + } + for (DynamicHub interfaceHub : hub.getInterfaces()) { + addInheritedPublicMethods(newPublicMethods, interfaceHub.privateGetPublicMethods(), hidingMethods, hub.isInterface()); + } + + completeReflectionData = new ReflectionData(hub.rd.declaredFields, hub.rd.publicFields, hub.rd.publicUnhiddenFields, newDeclaredMethods.toArray(new Method[0]), + newPublicMethods.toArray(new Method[0]), + newDeclaredConstructors.toArray(new Constructor[0]), newPublicConstructors.toArray(new Constructor[0]), hub.rd.nullaryConstructor, hub.rd.declaredPublicFields, + newDeclaredPublicMethods.toArray(new Method[0]), hub.rd.declaredClasses, hub.rd.publicClasses, hub.rd.enclosingMethodOrConstructor, hub.rd.recordComponents); } return completeReflectionData; } + + private static void addInheritedPublicMethods(List newPublicMethods, Method[] parentMethods, MethodDescriptor[] hidingMethods, boolean includeStaticMethods) { + parentMethods: for (Method m : parentMethods) { + if (!includeStaticMethods && Modifier.isStatic(m.getModifiers())) { + continue; + } + for (Method m2 : newPublicMethods) { + if (m.getName().equals(m2.getName()) && Arrays.equals(m.getParameterTypes(), m2.getParameterTypes())) { + if (m.getDeclaringClass() != m2.getDeclaringClass() && m2.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) { + /* Need to store the more specific method */ + newPublicMethods.remove(m2); + newPublicMethods.add(m); + } + continue parentMethods; + } + } + for (MethodDescriptor hidingMethod : hidingMethods) { + if (m.getName().equals(hidingMethod.getName()) && Arrays.equals(m.getParameterTypes(), hidingMethod.getParameterTypes())) { + continue parentMethods; + } + } + newPublicMethods.add(m); + } + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java new file mode 100644 index 000000000000..4393e14505dc --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/MethodMetadataDecoder.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021, 2021, 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.reflect; + +// Checkstyle: allow reflection + +import java.lang.reflect.Executable; + +import org.graalvm.collections.Pair; + +import com.oracle.svm.core.hub.DynamicHub; + +public interface MethodMetadataDecoder { + Pair getQueriedAndHidingMethods(DynamicHub declaringType); + + MethodDescriptor[] getAllReachableMethods(); + + class MethodDescriptor { + private final Class declaringClass; + private final String name; + private final Class[] parameterTypes; + + public MethodDescriptor(Class declaringClass, String name, Class[] parameterTypes) { + this.declaringClass = declaringClass; + this.name = name; + this.parameterTypes = parameterTypes; + } + + public Class getDeclaringClass() { + return declaringClass; + } + + public String getName() { + return name; + } + + public Class[] getParameterTypes() { + return parameterTypes; + } + } + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/RuntimeReflectionConstructors.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/RuntimeReflectionConstructors.java deleted file mode 100644 index b3e309cf1ea1..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/RuntimeReflectionConstructors.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2012, 2020, 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.reflect; - -// Checkstyle: stop -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -// Checkstyle: resume - -import org.graalvm.compiler.api.replacements.Fold; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; - -public interface RuntimeReflectionConstructors { - Method newMethod(Class declaringClass, - String name, - Class[] parameterTypes, - Class returnType, - Class[] checkedExceptions, - int modifiers, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] annotationDefault, - byte[] typeAnnotations, - String[] reflectParameterNames, - int[] reflectParameterModifiers); - - Constructor newConstructor(Class declaringClass, - Class[] parameterTypes, - Class[] checkedExceptions, - int modifiers, - String signature, - byte[] annotations, - byte[] parameterAnnotations, - byte[] typeAnnotations, - String[] reflectParameterNames, - int[] reflectParameterModifiers); - - @Fold - static boolean hasQueriedMethods() { - return !ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods().isEmpty(); - } -} diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java index 19a77c99f852..209f552127e7 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/RuntimeCodeInstaller.java @@ -245,7 +245,7 @@ private void patchDirectObjectConstants(ObjectConstantsHolder objectConstants, C } private void createCodeChunkInfos(CodeInfo runtimeMethodInfo, ReferenceAdjuster adjuster) { - CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(new RuntimeFrameInfoCustomization()); + CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(new RuntimeFrameInfoCustomization(), new CodeInfoEncoder.Encoders()); codeInfoEncoder.addMethod(method, compilation, 0); codeInfoEncoder.encodeAllAndInstall(runtimeMethodInfo, adjuster); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java index 440189475480..81baa14f6f26 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationTypeFeature.java @@ -58,7 +58,6 @@ public void afterRegistration(AfterRegistrationAccess access) { } @Override - @SuppressWarnings("unchecked") public void duringAnalysis(DuringAnalysisAccess access) { DuringAnalysisAccessImpl accessImpl = (DuringAnalysisAccessImpl) access; AnalysisUniverse universe = accessImpl.getUniverse(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java index 3a1691dc10fa..bc1594d278dd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java @@ -246,9 +246,4 @@ public SpeculationLog getSpeculationLog() { public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); } - - @Override - public boolean hasJavaMethod() { - return OriginalMethodProvider.hasJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java index c7a552202c17..67d8dbeeb866 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageCodeCache.java @@ -53,7 +53,6 @@ import org.graalvm.word.WordFactory; import com.oracle.graal.pointsto.BigBang; -import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.objectfile.ObjectFile; import com.oracle.svm.core.SubstrateOptions; @@ -83,7 +82,6 @@ import com.oracle.svm.hosted.image.NativeImage.NativeTextSectionImpl; import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedType; -import com.oracle.svm.hosted.substitute.SubstitutionReflectivityFilter; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.site.Call; @@ -94,6 +92,7 @@ import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.VMConstant; @@ -218,47 +217,36 @@ public int getAlignedConstantsSize() { public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord codeSize) { // Build run-time metadata. HostedFrameInfoCustomization frameInfoCustomization = new HostedFrameInfoCustomization(); - CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(frameInfoCustomization); + CodeInfoEncoder.Encoders encoders = new CodeInfoEncoder.Encoders(); + CodeInfoEncoder codeInfoEncoder = new CodeInfoEncoder(frameInfoCustomization, encoders); for (Entry entry : compilations.entrySet()) { final HostedMethod method = entry.getKey(); final CompilationResult compilation = entry.getValue(); codeInfoEncoder.addMethod(method, compilation, method.getCodeAddressOffset()); } - for (HostedType type : imageHeap.getUniverse().getTypes()) { - if (type.getWrapped().isReachable()) { - codeInfoEncoder.prepareMetadataForClass(type.getJavaClass()); - } - } - + MethodMetadataEncoder methodMetadataEncoder = ImageSingletons.lookup(MethodMetadataEncoderFactory.class).create(encoders); if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { for (Executable queriedMethod : ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods()) { - if (SubstitutionReflectivityFilter.shouldExclude(queriedMethod, (AnalysisMetaAccess) imageHeap.getMetaAccess().getWrapped(), imageHeap.getAnalysisUniverse())) { - continue; - } - HostedMethod method = imageHeap.getMetaAccess().optionalLookupJavaMethod(queriedMethod); - if (method != null) { - codeInfoEncoder.prepareMetadataForMethod(method, queriedMethod, true); - } + HostedMethod method = imageHeap.getMetaAccess().lookupJavaMethod(queriedMethod); + methodMetadataEncoder.addReflectionMethodMetadata(imageHeap.getMetaAccess(), method, queriedMethod); } - for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHiddenMethods()) { - AnalysisMethod hiddenMethod = (AnalysisMethod) method; - JavaType[] parameterTypes = hiddenMethod.getSignature().toParameterTypes(null); - Class[] parameterClasses = new Class[parameterTypes.length]; - for (int i = 0; i < parameterTypes.length; ++i) { - parameterClasses[i] = imageHeap.getUniverse().lookup(parameterTypes[i]).getHub().getHostedJavaClass(); + for (Object method : ImageSingletons.lookup(RuntimeReflectionSupport.class).getHidingMethods()) { + AnalysisMethod hidingMethod = (AnalysisMethod) method; + HostedType declaringType = imageHeap.getUniverse().lookup(hidingMethod.getDeclaringClass()); + String name = hidingMethod.getName(); + JavaType[] analysisParameterTypes = hidingMethod.getSignature().toParameterTypes(null); + HostedType[] parameterTypes = new HostedType[analysisParameterTypes.length]; + for (int i = 0; i < analysisParameterTypes.length; ++i) { + parameterTypes[i] = imageHeap.getUniverse().lookup(analysisParameterTypes[i]); } - codeInfoEncoder.prepareHiddenMethodMetadata(imageHeap.getUniverse().lookup(hiddenMethod.getDeclaringClass()), hiddenMethod.getName(), parameterClasses); + methodMetadataEncoder.addHidingMethodMetadata(declaringType, name, parameterTypes); } } if (SubstrateOptions.IncludeMethodData.getValue()) { for (HostedMethod method : imageHeap.getUniverse().getMethods()) { - if (method.getWrapped().isReachable() && method.hasJavaMethod()) { - Executable reflectMethod = method.getJavaMethod(); - if (SubstitutionReflectivityFilter.shouldExclude(reflectMethod, (AnalysisMetaAccess) imageHeap.getMetaAccess().getWrapped(), imageHeap.getAnalysisUniverse())) { - continue; - } - codeInfoEncoder.prepareMetadataForMethod(method, method.getJavaMethod(), false); + if (method.getWrapped().isReachable() && !method.getWrapped().isIntrinsicMethod()) { + methodMetadataEncoder.addReachableMethodMetadata(method); } } } @@ -270,6 +258,7 @@ public void buildRuntimeMetadata(CFunctionPointer firstMethod, UnsignedWord code HostedImageCodeInfo imageCodeInfo = CodeInfoTable.getImageCodeCache().getHostedImageCodeInfo(); codeInfoEncoder.encodeAllAndInstall(imageCodeInfo, new InstantReferenceAdjuster()); + methodMetadataEncoder.encodeAllAndInstall(); imageCodeInfo.setCodeStart(firstMethod); imageCodeInfo.setCodeSize(codeSize); imageCodeInfo.setDataOffset(codeSize); @@ -558,4 +547,18 @@ protected boolean isDeoptEntry(ResolvedJavaMethod method, Infopoint infopoint) { return false; } } + + public interface MethodMetadataEncoder { + void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod sharedMethod, Executable reflectMethod); + + void addHidingMethodMetadata(HostedType declType, String name, HostedType[] paramTypes); + + void addReachableMethodMetadata(HostedMethod method); + + void encodeAllAndInstall(); + } + + public interface MethodMetadataEncoderFactory { + MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 13d035dabe57..0546c2ab4d33 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -500,9 +500,4 @@ public int compareTo(HostedMethod other) { public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(getDeclaringClass().universe.getSnippetReflection(), wrapped); } - - @Override - public boolean hasJavaMethod() { - return OriginalMethodProvider.hasJavaMethod(getDeclaringClass().universe.getSnippetReflection(), wrapped); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java index 873cb69b7c4f..524faf946f30 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotatedMethod.java @@ -284,9 +284,4 @@ public SpeculationLog getSpeculationLog() { public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); } - - @Override - public boolean hasJavaMethod() { - return OriginalMethodProvider.hasJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java index d66751657378..96551f86e441 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionMethod.java @@ -296,9 +296,4 @@ public SpeculationLog getSpeculationLog() { public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); } - - @Override - public boolean hasJavaMethod() { - return OriginalMethodProvider.hasJavaMethod(GraalAccess.getOriginalSnippetReflection(), original); - } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java index b28d716e111a..f81ce25afc16 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/SubstitutionReflectivityFilter.java @@ -27,8 +27,6 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; -import org.graalvm.compiler.api.replacements.Fold; - import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -64,7 +62,7 @@ public static boolean shouldExclude(Executable method, AnalysisMetaAccess metaAc AnalysisMethod aMethod = metaAccess.lookupJavaMethod(method); if (!universe.hostVM().platformSupported(universe, aMethod)) { return true; - } else if (aMethod.isAnnotationPresent(Delete.class) || aMethod.isAnnotationPresent(Fold.class)) { + } else if (aMethod.isAnnotationPresent(Delete.class)) { return true; // accesses would fail at runtime } else if (aMethod.isSynthetic() && aMethod.getDeclaringClass().isAnnotationPresent(TargetClass.class)) { /* diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java new file mode 100644 index 000000000000..d1a294bb2783 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadata.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021, 2021, 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.reflect.hosted; + +// Checkstyle: allow reflection + +import java.lang.annotation.Annotation; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; +import com.oracle.svm.reflect.target.MethodMetadataEncoding; + +import sun.reflect.annotation.TypeAnnotation; + +@AutomaticFeature +class MethodMetadataFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(MethodMetadataEncoderFactory.class, new MethodMetadataEncoderImpl.Factory()); + ImageSingletons.add(MethodMetadataDecoder.class, new MethodMetadataDecoderImpl()); + ImageSingletons.add(MethodMetadataEncoding.class, new MethodMetadataEncoding()); + } +} + +public class MethodMetadata { + final HostedType declaringType; + final String name; + final HostedType[] parameterTypes; + + MethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { + this.declaringType = declaringType; + this.name = name; + this.parameterTypes = parameterTypes; + } + + static class ReflectionMethodMetadata extends MethodMetadata { + final int modifiers; + final HostedType returnType; + final HostedType[] exceptionTypes; + final String signature; + final Annotation[] annotations; + final Annotation[][] parameterAnnotations; + final TypeAnnotation[] typeAnnotations; + final boolean hasRealParameterData; + final MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters; + + ReflectionMethodMetadata(HostedType declaringClass, String name, HostedType[] parameterTypes, int modifiers, HostedType returnType, HostedType[] exceptionTypes, String signature, + Annotation[] annotations, Annotation[][] parameterAnnotations, TypeAnnotation[] typeAnnotations, boolean hasRealParameterData, + MethodMetadataDecoderImpl.ReflectParameterDescriptor[] reflectParameters) { + super(declaringClass, name, parameterTypes); + this.modifiers = modifiers; + this.returnType = returnType; + this.exceptionTypes = exceptionTypes; + this.signature = signature; + this.annotations = annotations; + this.parameterAnnotations = parameterAnnotations; + this.typeAnnotations = typeAnnotations; + this.hasRealParameterData = hasRealParameterData; + this.reflectParameters = reflectParameters; + } + } + +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java similarity index 50% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoder.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java index 24cd0f1a7790..80d526fce5d4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/MethodMetadataEncoderImpl.java @@ -22,12 +22,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.code; +package com.oracle.svm.reflect.hosted; // Checkstyle: allow reflection -import static com.oracle.svm.core.util.VMError.shouldNotReachHere; - import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; @@ -36,287 +34,348 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.function.Consumer; -import org.graalvm.collections.Pair; import org.graalvm.compiler.core.common.util.TypeConversion; import org.graalvm.compiler.core.common.util.UnsafeArrayTypeWriter; +import org.graalvm.compiler.debug.GraalError; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.util.GuardedAnnotationAccess; import com.oracle.svm.core.SubstrateOptions; -import com.oracle.svm.core.code.CodeInfoDecoder.MethodDescriptor; +import com.oracle.svm.core.code.CodeInfoEncoder; import com.oracle.svm.core.hub.DynamicHubSupport; -import com.oracle.svm.core.meta.SharedMethod; -import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.util.ByteArrayReader; +import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoder; +import com.oracle.svm.hosted.image.NativeImageCodeCache.MethodMetadataEncoderFactory; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.reflect.hosted.MethodMetadata.ReflectionMethodMetadata; +import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl; +import com.oracle.svm.reflect.target.MethodMetadataDecoderImpl.ReflectParameterDescriptor; +import com.oracle.svm.reflect.target.MethodMetadataEncoding; +import com.oracle.svm.reflect.target.Target_jdk_internal_reflect_ConstantPool; +import com.oracle.svm.reflect.target.Target_sun_reflect_annotation_AnnotationParser; import com.oracle.svm.util.ReflectionUtil; -import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.MetaAccessProvider; import sun.invoke.util.Wrapper; import sun.reflect.annotation.AnnotationType; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; -@Platforms(Platform.HOSTED_ONLY.class) -public class MethodMetadataEncoder { - public static final int NO_METHOD_METADATA = -1; +/** + * The method metadata encoding puts data in the image for three distinct types of methods. + *
    + *
  1. Methods that are queried for reflection, but never accessed: in that case, the encoding + * includes everything required to recreate an {@link Executable} object at runtime.
  2. + *
  3. Methods that hide a method registered for reflection, but are not registered themselves: only + * basic method information is stored for those methods (declaring class, name and parameter types). + * They are used to ensure that the hidden superclass method is not incorrectly returned by a + * reflection query on the subclass where it is hidden.
  4. + *
  5. Methods that are included in the image: all reachable methods have their basic information + * included to enable introspecting the produced executable.
  6. + *
+ * + * Emitting the metadata happens in two phases. In the first phase, the string and class encoders + * are filled with the necessary values (in the {@code add*MethodMetadata} functions). In a second + * phase, the values are encoded as byte arrays and stored in {@link MethodMetadataEncoding} (see + * {@link #encodeAllAndInstall()}). + */ +public class MethodMetadataEncoderImpl implements MethodMetadataEncoder { - private CodeInfoEncoder.Encoders encoders; - private TreeSet sortedTypes; - private Map>> queriedMethodData; - private Map> reachableMethodData; - private Map> hiddenMethodData; + static class Factory implements MethodMetadataEncoderFactory { + @Override + public MethodMetadataEncoder create(CodeInfoEncoder.Encoders encoders) { + return new MethodMetadataEncoderImpl(encoders); + } + } + + private final CodeInfoEncoder.Encoders encoders; + private final TreeSet sortedTypes; + private Map> queriedMethodData; + private Map> reachableMethodData; + private Map> hidingMethodData; private byte[] methodDataEncoding; private byte[] methodDataIndexEncoding; - MethodMetadataEncoder(CodeInfoEncoder.Encoders encoders) { + public MethodMetadataEncoderImpl(CodeInfoEncoder.Encoders encoders) { this.encoders = encoders; this.sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { this.queriedMethodData = new HashMap<>(); - this.hiddenMethodData = new HashMap<>(); + this.hidingMethodData = new HashMap<>(); } if (SubstrateOptions.IncludeMethodData.getValue()) { this.reachableMethodData = new HashMap<>(); } } - void encodeAllAndInstall() { - encodeMethodMetadata(); - ImageSingletons.lookup(MethodMetadataEncoding.class).setMethodsEncoding(methodDataEncoding); - ImageSingletons.lookup(MethodMetadataEncoding.class).setIndexEncoding(methodDataIndexEncoding); + private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); + private static final Method hasRealParameterData = ReflectionUtil.lookupMethod(Executable.class, "hasRealParameterData"); + + @Override + public void addReflectionMethodMetadata(MetaAccessProvider metaAccess, HostedMethod hostedMethod, Executable reflectMethod) { + HostedType declaringType = hostedMethod.getDeclaringClass(); + String name = hostedMethod.isConstructor() ? "" : hostedMethod.getName(); + HostedType[] parameterTypes = getParameterTypes(hostedMethod); + /* Reflect method because substitution of Object.hashCode() is private */ + int modifiers = reflectMethod.getModifiers(); + HostedType returnType = (HostedType) hostedMethod.getSignature().getReturnType(null); + HostedType[] exceptionTypes = getExceptionTypes(metaAccess, reflectMethod); + String signature = getSignature(reflectMethod); + Annotation[] annotations = GuardedAnnotationAccess.getDeclaredAnnotations(hostedMethod); + Annotation[][] parameterAnnotations = reflectMethod.getParameterAnnotations(); + TypeAnnotation[] typeAnnotations; + boolean reflectParameterDataPresent; + try { + typeAnnotations = (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod); + reflectParameterDataPresent = (boolean) hasRealParameterData.invoke(reflectMethod); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } + ReflectParameterDescriptor[] reflectParameterDescriptors = reflectParameterDataPresent ? getReflectParameters(reflectMethod) : new ReflectParameterDescriptor[0]; + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); + } + encoders.sourceClasses.addObject(returnType.getJavaClass()); + for (HostedType exceptionType : exceptionTypes) { + encoders.sourceClasses.addObject(exceptionType.getJavaClass()); + } + encoders.sourceMethodNames.addObject(signature); + /* Register string and class values in annotations */ + registerAnnotationValues(annotations); + for (Annotation[] parameterAnnotation : parameterAnnotations) { + registerAnnotationValues(parameterAnnotation); + } + for (TypeAnnotation typeAnnotation : typeAnnotations) { + // Checkstyle: allow direct annotation access + registerAnnotationValues(typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access + } + for (ReflectParameterDescriptor parameter : reflectParameterDescriptors) { + encoders.sourceMethodNames.addObject(parameter.getName()); + } + + sortedTypes.add(declaringType); + queriedMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new ReflectionMethodMetadata(declaringType, name, parameterTypes, modifiers, returnType, exceptionTypes, signature, + annotations, parameterAnnotations, typeAnnotations, reflectParameterDataPresent, reflectParameterDescriptors)); + } + + private void registerAnnotationValues(Annotation... annotations) { + for (Annotation annotation : annotations) { + encoders.sourceClasses.addObject(annotation.annotationType()); + registerAnnotationValue(annotation.annotationType(), annotation); + } } @SuppressWarnings("unchecked") - public void prepareMetadataForClass(Class clazz) { - encoders.sourceClasses.addObject(clazz); - if (clazz.isAnnotation()) { - try { - for (String valueName : AnnotationType.getInstance((Class) clazz).members().keySet()) { - encoders.sourceMethodNames.addObject(valueName); + private void registerAnnotationValue(Class type, Object value) { + if (type.isAnnotation()) { + Annotation annotation = (Annotation) value; + AnnotationType annotationType = AnnotationType.getInstance((Class) type); + encoders.sourceClasses.addObject(type); + for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { + String valueName = entry.getKey(); + Class valueType = entry.getValue(); + encoders.sourceMethodNames.addObject(valueName); + Method getAnnotationValue = annotationType.members().get(valueName); + getAnnotationValue.setAccessible(true); + Object annotationValue; + try { + annotationValue = getAnnotationValue.invoke(annotation); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); } - } catch (LinkageError | RuntimeException t) { - // ignore + registerAnnotationValue(valueType, annotationValue); } + } else if (type.isArray()) { + Class componentType = type.getComponentType(); + if (!componentType.isPrimitive()) { + for (Object val : (Object[]) value) { + registerAnnotationValue(componentType, val); + } + } + } else if (type == Class.class) { + encoders.sourceClasses.addObject((Class) value); + } else if (type == String.class) { + encoders.sourceMethodNames.addObject((String) value); + } else if (type.isEnum()) { + encoders.sourceClasses.addObject(type); + encoders.sourceMethodNames.addObject(((Enum) value).name()); } } - private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); - - public void prepareMetadataForMethod(SharedMethod method, Executable reflectMethod, boolean complete) { - if (reflectMethod instanceof Constructor) { - encoders.sourceMethodNames.addObject(""); - } else { - encoders.sourceMethodNames.addObject(method.getName()); + @Override + public void addHidingMethodMetadata(HostedType declaringType, String name, HostedType[] parameterTypes) { + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(name); + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); } - if (complete) { - encoders.sourceMethodNames.addObject(getSignature(reflectMethod)); - for (Parameter parameter : reflectMethod.getParameters()) { - encoders.sourceMethodNames.addObject(parameter.getName()); - } + sortedTypes.add(declaringType); + hidingMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(new MethodMetadata(declaringType, name, parameterTypes)); + } - /* Register string values in annotations */ - registerStrings(GuardedAnnotationAccess.getDeclaredAnnotations(method)); - for (Annotation[] annotations : reflectMethod.getParameterAnnotations()) { - registerStrings(annotations); - } - try { - for (TypeAnnotation typeAnnotation : (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)) { - // Checkstyle: allow direct annotation access - registerStrings(typeAnnotation.getAnnotation()); - // Checkstyle: disallow direct annotation access - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw shouldNotReachHere(); - } + @Override + public void addReachableMethodMetadata(HostedMethod method) { + HostedType declaringType = method.getDeclaringClass(); + String name = method.getName(); + HostedType[] parameterTypes = getParameterTypes(method); + + /* Fill encoders with the necessary values. */ + encoders.sourceMethodNames.addObject(method.getName()); + for (HostedType parameterType : parameterTypes) { + encoders.sourceClasses.addObject(parameterType.getJavaClass()); } - SharedType declaringType = (SharedType) method.getDeclaringClass(); sortedTypes.add(declaringType); - if (complete) { - queriedMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(Pair.create(method, reflectMethod)); - } else { - reachableMethodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(method); + reachableMethodData.computeIfAbsent(declaringType, t -> { + /* The declaring class is encoded once for all methods */ + encoders.sourceClasses.addObject(declaringType.getJavaClass()); + return new HashSet<>(); + }).add(new MethodMetadata(declaringType, name, parameterTypes)); + } + + private static HostedType[] getParameterTypes(HostedMethod method) { + HostedType[] parameterTypes = new HostedType[method.getSignature().getParameterCount(false)]; + for (int i = 0; i < parameterTypes.length; ++i) { + parameterTypes[i] = (HostedType) method.getSignature().getParameterType(i, null); } + return parameterTypes; } - public void prepareHiddenMethodMetadata(SharedType type, String name, Class[] parameterTypes) { - encoders.sourceMethodNames.addObject(name); - sortedTypes.add(type); - hiddenMethodData.computeIfAbsent(type, t -> new HashSet<>()).add(new MethodDescriptor(name, parameterTypes)); + private static HostedType[] getExceptionTypes(MetaAccessProvider metaAccess, Executable reflectMethod) { + Class[] exceptionClasses = reflectMethod.getExceptionTypes(); + HostedType[] exceptionTypes = new HostedType[exceptionClasses.length]; + for (int i = 0; i < exceptionClasses.length; ++i) { + exceptionTypes[i] = (HostedType) metaAccess.lookupJavaType(exceptionClasses[i]); + } + return exceptionTypes; } - private static final Method hasRealParameterData = ReflectionUtil.lookupMethod(Executable.class, "hasRealParameterData"); + private static ReflectParameterDescriptor[] getReflectParameters(Executable reflectMethod) { + Parameter[] reflectParameters = reflectMethod.getParameters(); + ReflectParameterDescriptor[] reflectParameterDescriptors = new ReflectParameterDescriptor[reflectParameters.length]; + for (int i = 0; i < reflectParameters.length; ++i) { + reflectParameterDescriptors[i] = new ReflectParameterDescriptor(reflectParameters[i].getName(), reflectParameters[i].getModifiers()); + } + return reflectParameterDescriptors; + } + + @Override + public void encodeAllAndInstall() { + encodeMethodMetadata(); + ImageSingletons.lookup(MethodMetadataEncoding.class).setMethodsEncoding(methodDataEncoding); + ImageSingletons.lookup(MethodMetadataEncoding.class).setIndexEncoding(methodDataIndexEncoding); + } + /** + * See {@link MethodMetadataDecoderImpl} for the encoding format description. + */ private void encodeMethodMetadata() { UnsafeArrayTypeWriter dataEncodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); UnsafeArrayTypeWriter indexEncodingBuffer = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - long lastTypeID = -1; - for (SharedType declaringType : sortedTypes) { + + long nextTypeId = 0; + for (HostedType declaringType : sortedTypes) { long typeID = declaringType.getHub().getTypeID(); - assert typeID > lastTypeID; - lastTypeID++; - while (lastTypeID < typeID) { - indexEncodingBuffer.putS4(NO_METHOD_METADATA); - lastTypeID++; + assert typeID >= nextTypeId; + for (; nextTypeId < typeID; nextTypeId++) { + indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); } + assert nextTypeId == typeID; + long index = dataEncodingBuffer.getBytesWritten(); indexEncodingBuffer.putS4(index); - if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { - Set> completeMethods = queriedMethodData.getOrDefault(declaringType, Collections.emptySet()); - dataEncodingBuffer.putUV(completeMethods.size()); - for (Pair method : completeMethods) { - SharedMethod hostedMethod = method.getLeft(); - Executable reflectMethod = method.getRight(); - - Class declaringClass = getJavaClass((SharedType) hostedMethod.getDeclaringClass()); - final int classIndex = encoders.sourceClasses.getIndex(declaringClass); - dataEncodingBuffer.putSV(classIndex); - - String name = hostedMethod.isConstructor() ? "" : hostedMethod.getName(); - final int nameIndex = encoders.sourceMethodNames.getIndex(name); - dataEncodingBuffer.putSV(nameIndex); - - dataEncodingBuffer.putUV(reflectMethod.getModifiers()); - - /* Parameter types do not include the receiver */ - JavaType[] parameterTypes = hostedMethod.getSignature().toParameterTypes(null); - dataEncodingBuffer.putUV(parameterTypes.length); - for (JavaType parameterType : parameterTypes) { - Class parameterClass = getJavaClass((SharedType) parameterType); - final int paramClassIndex = encoders.sourceClasses.getIndex(parameterClass); - dataEncodingBuffer.putSV(paramClassIndex); - } - - Class returnType = void.class; - if (!hostedMethod.isConstructor()) { - returnType = getJavaClass((SharedType) hostedMethod.getSignature().getReturnType(null)); - } - final int returnTypeIndex = encoders.sourceClasses.getIndex(returnType); - dataEncodingBuffer.putSV(returnTypeIndex); - - /* - * Only include types that are in the image (i.e. that can actually be thrown) - */ - Class[] exceptionTypes = filterTypes(reflectMethod.getExceptionTypes()); - dataEncodingBuffer.putUV(exceptionTypes.length); - for (Class exceptionClazz : exceptionTypes) { - final int exceptionClassIndex = encoders.sourceClasses.getIndex(exceptionClazz); - dataEncodingBuffer.putSV(exceptionClassIndex); - } - - final int signatureIndex = encoders.sourceMethodNames.getIndex(getSignature(reflectMethod)); - dataEncodingBuffer.putSV(signatureIndex); - - try { - byte[] annotations = encodeAnnotations(GuardedAnnotationAccess.getDeclaredAnnotations(hostedMethod)); - dataEncodingBuffer.putUV(annotations.length); - for (byte b : annotations) { - dataEncodingBuffer.putS1(b); - } - - byte[] parameterAnnotations = encodeParameterAnnotations(reflectMethod.getParameterAnnotations()); - dataEncodingBuffer.putUV(parameterAnnotations.length); - for (byte b : parameterAnnotations) { - dataEncodingBuffer.putS1(b); - } - - byte[] typeAnnotations = encodeTypeAnnotations((TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)); - dataEncodingBuffer.putUV(typeAnnotations.length); - for (byte b : typeAnnotations) { - dataEncodingBuffer.putS1(b); - } - - boolean parameterDataPresent = (boolean) hasRealParameterData.invoke(reflectMethod); - dataEncodingBuffer.putU1(parameterDataPresent ? 1 : 0); - if (parameterDataPresent) { - Parameter[] parameters = reflectMethod.getParameters(); - dataEncodingBuffer.putUV(parameters.length); - for (Parameter parameter : parameters) { - final int parameterNameIndex = encoders.sourceMethodNames.getIndex(parameter.getName()); - dataEncodingBuffer.putSV(parameterNameIndex); - dataEncodingBuffer.putS4(parameter.getModifiers()); - } - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw shouldNotReachHere(); - } - } + nextTypeId++; - Set hiddenMethods = hiddenMethodData.getOrDefault(declaringType, Collections.emptySet()); - dataEncodingBuffer.putUV(hiddenMethods.size()); - for (MethodDescriptor hiddenMethod : hiddenMethods) { - String name = hiddenMethod.getName(); - final int nameIndex = encoders.sourceMethodNames.getIndex(name); - dataEncodingBuffer.putSV(nameIndex); - - /* Parameter types do not include the receiver */ - Class[] parameterTypes = hiddenMethod.getParameterTypes(); - dataEncodingBuffer.putUV(parameterTypes.length); - for (Class parameterType : parameterTypes) { - final int paramTypeIndex = encoders.sourceClasses.getIndex(parameterType); - dataEncodingBuffer.putSV(paramTypeIndex); - } - } + if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { + Set queriedMethods = queriedMethodData.getOrDefault(declaringType, Collections.emptySet()); + encodeArray(dataEncodingBuffer, queriedMethods.toArray(new ReflectionMethodMetadata[0]), method -> { + assert method.declaringType.equals(declaringType); + encodeReflectionMethod(dataEncodingBuffer, method); + }); + + Set hidingMethods = hidingMethodData.getOrDefault(declaringType, Collections.emptySet()); + encodeArray(dataEncodingBuffer, hidingMethods.toArray(new MethodMetadata[0]), hidingMethod -> encodeSimpleMethod(dataEncodingBuffer, hidingMethod)); } + if (SubstrateOptions.IncludeMethodData.getValue()) { - Set partialMethods = reachableMethodData.getOrDefault(declaringType, Collections.emptySet()); - dataEncodingBuffer.putUV(partialMethods.size()); - for (SharedMethod hostedMethod : partialMethods) { - Class declaringClass = getJavaClass((SharedType) hostedMethod.getDeclaringClass()); - final int classIndex = encoders.sourceClasses.getIndex(declaringClass); - dataEncodingBuffer.putSV(classIndex); - - String name = hostedMethod.isConstructor() ? "" : hostedMethod.getName(); - final int nameIndex = encoders.sourceMethodNames.getIndex(name); - dataEncodingBuffer.putSV(nameIndex); - - /* Parameter types do not include the receiver */ - JavaType[] parameterTypes = hostedMethod.getSignature().toParameterTypes(null); - dataEncodingBuffer.putUV(parameterTypes.length); - for (JavaType parameterType : parameterTypes) { - Class parameterClass = getJavaClass((SharedType) parameterType); - final int paramClassIndex = encoders.sourceClasses.getIndex(parameterClass); - dataEncodingBuffer.putSV(paramClassIndex); - } + Set reachableMethods = reachableMethodData.get(declaringType); + if (reachableMethods != null) { + encodeType(dataEncodingBuffer, declaringType); + encodeArray(dataEncodingBuffer, reachableMethods.toArray(new MethodMetadata[0]), reachableMethod -> encodeSimpleMethod(dataEncodingBuffer, reachableMethod)); + } else { + dataEncodingBuffer.putSV(MethodMetadataDecoderImpl.NO_METHOD_METADATA); } } } - while (lastTypeID < ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId()) { - indexEncodingBuffer.putS4(NO_METHOD_METADATA); - lastTypeID++; + for (; nextTypeId <= ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId(); nextTypeId++) { + indexEncodingBuffer.putS4(MethodMetadataDecoderImpl.NO_METHOD_METADATA); } + methodDataEncoding = new byte[TypeConversion.asS4(dataEncodingBuffer.getBytesWritten())]; dataEncodingBuffer.toArray(methodDataEncoding); methodDataIndexEncoding = new byte[TypeConversion.asS4(indexEncodingBuffer.getBytesWritten())]; indexEncodingBuffer.toArray(methodDataIndexEncoding); } - private static Class getJavaClass(SharedType sharedType) { - return sharedType.getHub().getHostedJavaClass(); + private void encodeReflectionMethod(UnsafeArrayTypeWriter buf, ReflectionMethodMetadata method) { + encodeSimpleMethod(buf, method); + buf.putUV(method.modifiers); + encodeType(buf, method.returnType); + encodeArray(buf, method.exceptionTypes, exceptionType -> encodeType(buf, exceptionType)); + encodeName(buf, method.signature); + encodeByteArray(buf, encodeAnnotations(method.annotations)); + encodeByteArray(buf, encodeParameterAnnotations(method.parameterAnnotations)); + encodeByteArray(buf, encodeTypeAnnotations(method.typeAnnotations)); + buf.putU1(method.hasRealParameterData ? 1 : 0); + if (method.hasRealParameterData) { + encodeArray(buf, method.reflectParameters, reflectParameter -> { + encodeName(buf, reflectParameter.getName()); + buf.putS4(reflectParameter.getModifiers()); + }); + } } - private Class[] filterTypes(Class[] types) { - List> filteredTypes = new ArrayList<>(); - for (Class type : types) { - if (encoders.sourceClasses.contains(type)) { - filteredTypes.add(type); - } + private void encodeSimpleMethod(UnsafeArrayTypeWriter buf, MethodMetadata method) { + encodeName(buf, method.name); + encodeArray(buf, method.parameterTypes, parameterType -> encodeType(buf, parameterType)); + } + + private void encodeType(UnsafeArrayTypeWriter buf, HostedType type) { + buf.putSV(encoders.sourceClasses.getIndex(type.getJavaClass())); + } + + private void encodeName(UnsafeArrayTypeWriter buf, String name) { + buf.putSV(encoders.sourceMethodNames.getIndex(name)); + } + + private static void encodeArray(UnsafeArrayTypeWriter buf, T[] array, Consumer elementEncoder) { + buf.putUV(array.length); + for (T elem : array) { + elementEncoder.accept(elem); + } + } + + private static void encodeByteArray(UnsafeArrayTypeWriter buf, byte[] array) { + buf.putUV(array.length); + for (byte b : array) { + buf.putS1(b); } - return filteredTypes.toArray(new Class[0]); } private static final Method getMethodSignature = ReflectionUtil.lookupMethod(Method.class, "getGenericSignature"); @@ -326,7 +385,7 @@ private static String getSignature(Executable method) { try { return (String) (method instanceof Method ? getMethodSignature.invoke(method) : getConstructorSignature.invoke(method)); } catch (IllegalAccessException | InvocationTargetException e) { - throw shouldNotReachHere(); + throw GraalError.shouldNotReachHere(); } } @@ -343,36 +402,31 @@ private static String getSignature(Executable method) { * * We use a modified version of the ConstantPool and AnnotationParser classes to decode the * data, since those are not used in their original functions at runtime. (see - * {@link com.oracle.svm.core.jdk.Target_jdk_internal_reflect_ConstantPool}) + * {@link Target_jdk_internal_reflect_ConstantPool} and + * {@link Target_sun_reflect_annotation_AnnotationParser}) */ - byte[] encodeAnnotations(Annotation[] annotations) throws InvocationTargetException, IllegalAccessException { + public byte[] encodeAnnotations(Annotation[] annotations) { UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - - Annotation[] filteredAnnotations = filterAnnotations(annotations); - buf.putU2(filteredAnnotations.length); - for (Annotation annotation : filteredAnnotations) { + buf.putU2(annotations.length); + for (Annotation annotation : annotations) { encodeAnnotation(buf, annotation); } - return buf.toArray(); } - byte[] encodeParameterAnnotations(Annotation[][] annotations) throws InvocationTargetException, IllegalAccessException { + private byte[] encodeParameterAnnotations(Annotation[][] annotations) { UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - buf.putU1(annotations.length); for (Annotation[] parameterAnnotations : annotations) { - Annotation[] filteredParameterAnnotations = filterAnnotations(parameterAnnotations); - buf.putU2(filteredParameterAnnotations.length); - for (Annotation parameterAnnotation : filteredParameterAnnotations) { + buf.putU2(parameterAnnotations.length); + for (Annotation parameterAnnotation : parameterAnnotations) { encodeAnnotation(buf, parameterAnnotation); } } - return buf.toArray(); } - void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) throws InvocationTargetException, IllegalAccessException { + private void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) { buf.putS4(encoders.sourceClasses.getIndex(annotation.annotationType())); AnnotationType type = AnnotationType.getInstance(annotation.annotationType()); buf.putU2(type.members().size()); @@ -380,11 +434,15 @@ void encodeAnnotation(UnsafeArrayTypeWriter buf, Annotation annotation) throws I String memberName = entry.getKey(); Method valueAccessor = entry.getValue(); buf.putS4(encoders.sourceMethodNames.getIndex(memberName)); - encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); + try { + encodeValue(buf, valueAccessor.invoke(annotation), type.memberTypes().get(memberName)); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } } } - void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) throws InvocationTargetException, IllegalAccessException { + private void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) { buf.putU1(tag(type)); if (type.isAnnotation()) { encodeAnnotation(buf, (Annotation) value); @@ -425,14 +483,14 @@ void encodeValue(UnsafeArrayTypeWriter buf, Object value, Class type) throws buf.putS8(Double.doubleToRawLongBits((double) value)); break; default: - throw shouldNotReachHere(); + throw GraalError.shouldNotReachHere(); } } else { - throw shouldNotReachHere(); + throw GraalError.shouldNotReachHere(); } } - void encodeArray(UnsafeArrayTypeWriter buf, Object value, Class componentType) throws InvocationTargetException, IllegalAccessException { + private void encodeArray(UnsafeArrayTypeWriter buf, Object value, Class componentType) { if (!componentType.isPrimitive()) { Object[] array = (Object[]) value; buf.putU2(array.length); @@ -490,7 +548,7 @@ void encodeArray(UnsafeArrayTypeWriter buf, Object value, Class componentType } } - byte tag(Class type) { + private static byte tag(Class type) { if (type.isAnnotation()) { return '@'; } else if (type.isEnum()) { @@ -506,90 +564,20 @@ byte tag(Class type) { } else if (Wrapper.isWrapperType(type)) { return (byte) Wrapper.forWrapperType(type).basicTypeChar(); } else { - throw shouldNotReachHere(); - } - } - - private Annotation[] filterAnnotations(Annotation[] annotations) { - List filteredAnnotations = new ArrayList<>(); - for (Annotation annotation : annotations) { - Class annotationClass = annotation.annotationType(); - if (supportedValue(annotationClass, annotation, null)) { - filteredAnnotations.add(annotation); - } - } - return filteredAnnotations.toArray(new Annotation[0]); - } - - private void registerStrings(Annotation... annotations) { - for (Annotation annotation : annotations) { - List stringValues = new ArrayList<>(); - if (supportedValue(annotation.annotationType(), annotation, stringValues)) { - for (String stringValue : stringValues) { - encoders.sourceMethodNames.addObject(stringValue); - } - } - } - } - - @SuppressWarnings("unchecked") - private boolean supportedValue(Class type, Object value, List stringValues) { - if (type.isAnnotation()) { - Annotation annotation = (Annotation) value; - if (!encoders.sourceClasses.contains(annotation.annotationType())) { - return false; - } - AnnotationType annotationType = AnnotationType.getInstance((Class) type); - for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { - String valueName = entry.getKey(); - Class valueType = entry.getValue(); - try { - Method getAnnotationValue = annotationType.members().get(valueName); - getAnnotationValue.setAccessible(true); - Object annotationValue = getAnnotationValue.invoke(annotation); - if (!supportedValue(valueType, annotationValue, stringValues)) { - return false; - } - } catch (IllegalAccessException | InvocationTargetException e) { - return false; - } - } - } else if (type.isArray()) { - boolean supported = true; - Class componentType = type.getComponentType(); - if (!componentType.isPrimitive()) { - for (Object val : (Object[]) value) { - supported &= supportedValue(componentType, val, stringValues); - } - } - return supported; - } else if (type == Class.class) { - return encoders.sourceClasses.contains((Class) value); - } else if (type == String.class) { - if (stringValues != null) { - stringValues.add((String) value); - } - } else if (type.isEnum()) { - if (stringValues != null) { - stringValues.add(((Enum) value).name()); - } - return encoders.sourceClasses.contains(type); + throw GraalError.shouldNotReachHere(); } - return true; } - byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) throws InvocationTargetException, IllegalAccessException { + private byte[] encodeTypeAnnotations(TypeAnnotation[] annotations) { UnsafeArrayTypeWriter buf = UnsafeArrayTypeWriter.create(ByteArrayReader.supportsUnalignedMemoryAccess()); - buf.putU2(annotations.length); for (TypeAnnotation typeAnnotation : annotations) { encodeTypeAnnotation(buf, typeAnnotation); } - return buf.toArray(); } - void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotation typeAnnotation) throws InvocationTargetException, IllegalAccessException { + private void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotation typeAnnotation) { encodeTargetInfo(buf, typeAnnotation.getTargetInfo()); encodeLocationInfo(buf, typeAnnotation.getLocationInfo()); // Checkstyle: allow direct annotation access @@ -608,7 +596,7 @@ void encodeTypeAnnotation(UnsafeArrayTypeWriter buf, TypeAnnotation typeAnnotati private static final byte METHOD_FORMAL_PARAMETER = 0x16; private static final byte THROWS = 0x17; - void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.TypeAnnotationTargetInfo targetInfo) { + private static void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.TypeAnnotationTargetInfo targetInfo) { switch (targetInfo.getTarget()) { case CLASS_TYPE_PARAMETER: buf.putU1(CLASS_TYPE_PARAMETER); @@ -659,14 +647,18 @@ void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.TypeAnnotationTa private static final Field locationInfoDepth = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "depth"); private static final Field locationInfoLocations = ReflectionUtil.lookupField(TypeAnnotation.LocationInfo.class, "locations"); - void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.LocationInfo locationInfo) throws IllegalAccessException { - int depth = (int) locationInfoDepth.get(locationInfo); - buf.putU1(depth); - TypeAnnotation.LocationInfo.Location[] locations = (TypeAnnotation.LocationInfo.Location[]) locationInfoLocations.get(locationInfo); - for (TypeAnnotation.LocationInfo.Location location : locations) { - buf.putS1(location.tag); - buf.putU1(location.index); + private static void encodeLocationInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.LocationInfo locationInfo) { + try { + int depth = (int) locationInfoDepth.get(locationInfo); + buf.putU1(depth); + TypeAnnotation.LocationInfo.Location[] locations; + locations = (TypeAnnotation.LocationInfo.Location[]) locationInfoLocations.get(locationInfo); + for (TypeAnnotation.LocationInfo.Location location : locations) { + buf.putS1(location.tag); + buf.putU1(location.index); + } + } catch (IllegalAccessException e) { + throw GraalError.shouldNotReachHere(); } - } } diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java index 57ef1e5af1c7..e7d522fd282e 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionDataBuilder.java @@ -76,6 +76,7 @@ import jdk.vm.ci.meta.JavaType; import jdk.vm.ci.meta.ResolvedJavaMethod; +import sun.reflect.annotation.AnnotationType; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; @@ -94,7 +95,7 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final Set reflectionMethods = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final Set reflectionFields = Collections.newSetFromMap(new ConcurrentHashMap<>()); private Set queriedMethods; - private Set hiddenMethods; + private Set hidingMethods; private final Set> processedClasses = new HashSet<>(); @@ -105,7 +106,7 @@ public ReflectionDataBuilder(FeatureAccessImpl access) { accessors = new ReflectionDataAccessors(access); if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { queriedMethods = ConcurrentHashMap.newKeySet(); - hiddenMethods = ConcurrentHashMap.newKeySet(); + hidingMethods = ConcurrentHashMap.newKeySet(); } } @@ -238,49 +239,61 @@ private void processReachableTypes(DuringAnalysisAccessImpl access) { } } + /** + * See {@link MethodMetadataEncoderImpl} for details. + */ protected void processMethodMetadata(DuringAnalysisAccessImpl access) { if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { Set newQueriedMethods = new HashSet<>(); - for (Executable method : queriedMethods) { - if (registerMetadataForReflection(access, null, method, true)) { - AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); - registerHiddenSubTypeMethods(analysisMethod, analysisMethod.getDeclaringClass()); - newQueriedMethods.add(method); + for (Executable reflectMethod : queriedMethods) { + if (!SubstitutionReflectivityFilter.shouldExclude(reflectMethod, access.getMetaAccess(), access.getUniverse())) { + AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(reflectMethod); + registerTypesForQueriedMethod(access, analysisMethod, reflectMethod); + registerHidingSubTypeMethods(analysisMethod, analysisMethod.getDeclaringClass()); + newQueriedMethods.add(reflectMethod); } } queriedMethods = newQueriedMethods; for (Executable method : reflectionMethods) { if (!SubstitutionReflectivityFilter.shouldExclude(method, access.getMetaAccess(), access.getUniverse())) { AnalysisMethod analysisMethod = access.getMetaAccess().lookupJavaMethod(method); - registerHiddenSubTypeMethods(analysisMethod, analysisMethod.getDeclaringClass()); + registerHidingSubTypeMethods(analysisMethod, analysisMethod.getDeclaringClass()); } } } if (SubstrateOptions.IncludeMethodData.getValue()) { for (AnalysisMethod method : access.getUniverse().getMethods()) { - /* - * Methods generated by Graal and class initialization methods have no reflection - * method. - */ - if (method.isReachable() && method.hasJavaMethod()) { - Executable reflectMethod = method.getJavaMethod(); - registerMetadataForReflection(access, method, reflectMethod, false); + if (method.isReachable() && !method.isIntrinsicMethod()) { + registerTypesForReachableMethod(access, method); } } } } - private final Map> seenHiddenMethods = new HashMap<>(); - - private void registerHiddenSubTypeMethods(AnalysisMethod method, AnalysisType type) { + private final Map> seenHidingMethods = new HashMap<>(); + private void registerHidingSubTypeMethods(AnalysisMethod method, AnalysisType type) { if (!type.equals(method.getDeclaringClass()) && type.isReachable()) { - if (!seenHiddenMethods.containsKey(method) || !seenHiddenMethods.get(method).contains(type)) { - seenHiddenMethods.computeIfAbsent(method, m -> new HashSet<>()).add(type); + if (!seenHidingMethods.containsKey(method) || !seenHidingMethods.get(method).contains(type)) { + seenHidingMethods.computeIfAbsent(method, m -> new HashSet<>()).add(type); try { + /* + * Using findMethod here which uses getDeclaredMethods internally, instead of + * resolveConcreteMethods which gives different results in at least two + * scenarios: + * + * 1) When resolving a static method, resolveConcreteMethods does not return a + * subclass method with the same signature, since they are actually fully + * distinct methods. However these methods need to be included in the hiding + * list because them showing up in a reflection query would be wrong. + * + * 2) When resolving an interface method from an abstract class, + * resolveConcreteMethods returns an undeclared method with the abstract + * subclass as declaring class, which is not the reflection API behavior. + */ AnalysisMethod subClassMethod = type.findMethod(method.getName(), method.getSignature()); if (subClassMethod != null) { - hiddenMethods.add(subClassMethod); + hidingMethods.add(subClassMethod); } } catch (UnsupportedFeatureException | LinkageError e) { /* @@ -292,71 +305,62 @@ private void registerHiddenSubTypeMethods(AnalysisMethod method, AnalysisType ty } for (AnalysisType subType : type.getSubTypes()) { if (!subType.equals(type)) { - registerHiddenSubTypeMethods(method, subType); + registerHidingSubTypeMethods(method, subType); } } } private static final Method parseAllTypeAnnotations = ReflectionUtil.lookupMethod(TypeAnnotationParser.class, "parseAllTypeAnnotations", AnnotatedElement.class); - private static boolean registerMetadataForReflection(DuringAnalysisAccessImpl access, AnalysisMethod method, Executable reflectMethod, boolean complete) { - if (SubstitutionReflectivityFilter.shouldExclude(reflectMethod, access.getMetaAccess(), access.getUniverse())) { - return false; + private static void registerTypesForQueriedMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod, Executable reflectMethod) { + makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); + + for (TypeVariable type : reflectMethod.getTypeParameters()) { + makeTypeReachable(access, type); + } + for (Type paramType : analysisMethod.getGenericParameterTypes()) { + makeTypeReachable(access, paramType); + } + if (!analysisMethod.isConstructor()) { + makeTypeReachable(access, ((Method) reflectMethod).getGenericReturnType()); + } + for (Type exceptionType : reflectMethod.getGenericExceptionTypes()) { + makeTypeReachable(access, exceptionType); } - AnalysisMethod analysisMethod = method == null ? access.getMetaAccess().lookupJavaMethod(reflectMethod) : method; /* - * Reflection signature parsing will try to instantiate classes via Class.forName(). + * Enable runtime instantiation of annotations */ - makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); - - if (complete) { - for (TypeVariable type : reflectMethod.getTypeParameters()) { - makeTypeReachable(access, type, true); - } - for (Type paramType : analysisMethod.getGenericParameterTypes()) { - makeTypeReachable(access, paramType, true); - } - if (!analysisMethod.isConstructor()) { - makeTypeReachable(access, ((Method) reflectMethod).getGenericReturnType(), true); - } - for (Type exceptionType : reflectMethod.getGenericExceptionTypes()) { - makeTypeReachable(access, exceptionType, true); - } - - /* - * Enable runtime parsing of annotations - */ - for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisMethod)) { - makeTypeReachable(access, annotation.annotationType(), false); - } - for (Annotation[] parameterAnnotations : reflectMethod.getParameterAnnotations()) { - for (Annotation parameterAnnotation : parameterAnnotations) { - makeTypeReachable(access, parameterAnnotation.annotationType(), false); - } - } - try { - for (TypeAnnotation typeAnnotation : (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)) { - // Checkstyle: allow direct annotation access - makeTypeReachable(access, typeAnnotation.getAnnotation().annotationType(), false); - // Checkstyle: disallow direct annotation access - } - } catch (IllegalAccessException | InvocationTargetException e) { - throw GraalError.shouldNotReachHere(); + for (Annotation annotation : GuardedAnnotationAccess.getDeclaredAnnotations(analysisMethod)) { + registerTypesForAnnotationValue(access, annotation.annotationType(), annotation); + } + for (Annotation[] parameterAnnotations : reflectMethod.getParameterAnnotations()) { + for (Annotation parameterAnnotation : parameterAnnotations) { + registerTypesForAnnotationValue(access, parameterAnnotation.annotationType(), parameterAnnotation); } - } else { - for (JavaType paramType : analysisMethod.toParameterTypes()) { - makeAnalysisTypeReachable(access, (AnalysisType) paramType); + } + try { + for (TypeAnnotation typeAnnotation : (TypeAnnotation[]) parseAllTypeAnnotations.invoke(null, reflectMethod)) { + // Checkstyle: allow direct annotation access + registerTypesForAnnotationValue(access, typeAnnotation.getAnnotation().annotationType(), typeAnnotation.getAnnotation()); + // Checkstyle: disallow direct annotation access } + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); } + } - return true; + private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl access, AnalysisMethod analysisMethod) { + makeAnalysisTypeReachable(access, analysisMethod.getDeclaringClass()); + for (JavaType paramType : analysisMethod.toParameterTypes()) { + makeAnalysisTypeReachable(access, (AnalysisType) paramType); + } } private static final Set seenTypes = new HashSet<>(); @SuppressWarnings("unchecked") - private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type, boolean needsClassForName) { + private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) { if (type == null || seenTypes.contains(type)) { return; } @@ -365,42 +369,73 @@ private static void makeTypeReachable(DuringAnalysisAccessImpl access, Type type Class clazz = (Class) type; makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(clazz)); - if (needsClassForName) { - if (ClassForNameSupport.forNameOrNull(clazz.getName(), null) == null) { - access.requireAnalysisIteration(); - } - ClassForNameSupport.registerClass(clazz); - } - - if (clazz.isAnnotation()) { - /* - * Parsing annotation data in reflection classes requires being able to instantiate - * all annotation types at runtime. - */ - ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) clazz); - ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(clazz); + /* + * Reflection signature parsing will try to instantiate classes via Class.forName(). + */ + if (ClassForNameSupport.forNameOrNull(clazz.getName(), null) == null) { + access.requireAnalysisIteration(); } + ClassForNameSupport.registerClass(clazz); } else if (type instanceof TypeVariable) { for (Type bound : ((TypeVariable) type).getBounds()) { - makeTypeReachable(access, bound, needsClassForName); + makeTypeReachable(access, bound); } } else if (type instanceof GenericArrayType) { - makeTypeReachable(access, ((GenericArrayType) type).getGenericComponentType(), needsClassForName); + makeTypeReachable(access, ((GenericArrayType) type).getGenericComponentType()); } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; for (Type actualType : parameterizedType.getActualTypeArguments()) { - makeTypeReachable(access, actualType, needsClassForName); + makeTypeReachable(access, actualType); } - makeTypeReachable(access, parameterizedType.getRawType(), needsClassForName); - makeTypeReachable(access, parameterizedType.getOwnerType(), needsClassForName); + makeTypeReachable(access, parameterizedType.getRawType()); + makeTypeReachable(access, parameterizedType.getOwnerType()); } else if (type instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type; for (Type lowerBound : wildcardType.getLowerBounds()) { - makeTypeReachable(access, lowerBound, needsClassForName); + makeTypeReachable(access, lowerBound); } for (Type upperBound : wildcardType.getUpperBounds()) { - makeTypeReachable(access, upperBound, needsClassForName); + makeTypeReachable(access, upperBound); + } + } + } + + @SuppressWarnings("unchecked") + private static void registerTypesForAnnotationValue(DuringAnalysisAccessImpl access, Class type, Object value) { + if (type.isAnnotation()) { + makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); + /* + * Parsing annotation data in reflection classes requires being able to instantiate all + * annotation types at runtime. + */ + ImageSingletons.lookup(AnnotationTypeSupport.class).createInstance((Class) type); + ImageSingletons.lookup(DynamicProxyRegistry.class).addProxyClass(type); + + Annotation annotation = (Annotation) value; + AnnotationType annotationType = AnnotationType.getInstance((Class) type); + for (Map.Entry> entry : annotationType.memberTypes().entrySet()) { + String valueName = entry.getKey(); + Class valueType = entry.getValue(); + try { + Method getAnnotationValue = annotationType.members().get(valueName); + getAnnotationValue.setAccessible(true); + Object annotationValue = getAnnotationValue.invoke(annotation); + registerTypesForAnnotationValue(access, valueType, annotationValue); + } catch (IllegalAccessException | InvocationTargetException e) { + throw GraalError.shouldNotReachHere(); + } + } + } else if (type.isArray()) { + Class componentType = type.getComponentType(); + if (!componentType.isPrimitive()) { + for (Object val : (Object[]) value) { + registerTypesForAnnotationValue(access, componentType, val); + } } + } else if (type == Class.class) { + makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType((Class) value)); + } else if (type.isEnum()) { + makeAnalysisTypeReachable(access, access.getMetaAccess().lookupJavaType(type)); } } @@ -662,8 +697,8 @@ public Set getQueriedOnlyMethods() { } @Override - public Set getHiddenMethods() { - return hiddenMethods != null ? Collections.unmodifiableSet(hiddenMethods) : Collections.emptySet(); + public Set getHidingMethods() { + return hidingMethods != null ? Collections.unmodifiableSet(hidingMethods) : Collections.emptySet(); } static final class ReflectionDataAccessors { diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java index 02684b29ba1e..7f28fd86ca3f 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java @@ -53,7 +53,6 @@ import com.oracle.svm.core.configure.ReflectionConfigurationParser; import com.oracle.svm.core.graal.GraalFeature; import com.oracle.svm.core.reflect.ReflectionAccessorHolder; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; import com.oracle.svm.core.reflect.SubstrateConstructorAccessor; import com.oracle.svm.core.reflect.SubstrateMethodAccessor; import com.oracle.svm.core.reflect.SubstrateReflectionAccessorFactory; @@ -68,7 +67,6 @@ import com.oracle.svm.hosted.meta.MethodPointer; import com.oracle.svm.hosted.snippets.ReflectionPlugins; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; -import com.oracle.svm.reflect.target.RuntimeReflectionConstructorsImpl; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; @@ -174,7 +172,6 @@ public void afterRegistration(AfterRegistrationAccess access) { reflectionData = new ReflectionDataBuilder((FeatureAccessImpl) access); ImageSingletons.add(RuntimeReflectionSupport.class, reflectionData); - ImageSingletons.add(RuntimeReflectionConstructors.class, new RuntimeReflectionConstructorsImpl()); } @Override diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java index a49324dbf209..d32ddb8c634b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/proxy/hosted/DynamicProxyFeature.java @@ -47,7 +47,6 @@ @AutomaticFeature public final class DynamicProxyFeature implements Feature { private int loadedConfigurations; - private DynamicProxySupport dynamicProxySupport; @Override public List> getRequiredFeatures() { @@ -59,7 +58,7 @@ public void duringSetup(DuringSetupAccess a) { DuringSetupAccessImpl access = (DuringSetupAccessImpl) a; ImageClassLoader imageClassLoader = access.getImageClassLoader(); - dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); + DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader()); ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport); ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue()); ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java new file mode 100644 index 000000000000..e642eaa5d9a2 --- /dev/null +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataDecoderImpl.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2021, 2021, 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.reflect.target; + +// Checkstyle: allow reflection + +import java.lang.reflect.Array; +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Supplier; + +import org.graalvm.collections.Pair; +import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.compiler.core.common.util.UnsafeArrayTypeReader; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.RuntimeReflectionSupport; + +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.c.NonmovableArrays; +import com.oracle.svm.core.code.CodeInfo; +import com.oracle.svm.core.code.CodeInfoAccess; +import com.oracle.svm.core.code.CodeInfoTable; +import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.reflect.MethodMetadataDecoder; +import com.oracle.svm.core.util.ByteArrayReader; + +/** + * The metadata for methods in the image is split into two arrays: one for the index and the other + * for data. The index contains an array of integers pointing to offsets in the data, and indexed by + * type ID. The data array contains arrays of method metadata, ordered by type ID, such that all + * methods declared by a class are stored consecutively, in the following format: + * + *
+ * {
+ *     int queriedMethodsCount;
+ *     ReflectMethodEncoding[] queriedMethods[queriedMethodsCount];
+ *     int hidingMethodsCount;
+ *     SimpleMethodEncoding[] hidingMethods[hidingMethodsCount];
+ *     int declaringTypeIndex;             // index in frameInfoSourceClasses
+ *     int reachableMethodsCount;
+ *     SimpleMethodEncoding[] reachableMethods[reachableMethodsCount];
+ * } TypeEncoding;
+ * 
+ * + * The declaring class is encoded before the reachable methods to avoid having to be decoded when + * getting the queried and hiding methods, in which case the declaring class is available as an + * argument and doesn't need to be retrieved from the encoding. + * + * The data for a queried method is stored in the following format: + * + *
+ * {
+ *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
+ *     int paramCount;
+ *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
+ *     int modifiers;
+ *     int returnTypeIndex;                // index in frameInfoSourceClasses (void for constructors)
+ *     int exceptionTypeCount;
+ *     int[] exceptionTypeIndices[exceptionTypeCount]; // index in frameInfoSourceClasses
+ *     int signatureIndex;                 // index in frameInfoSourceMethodNames
+ *     int annotationsEncodingLength;
+ *     byte[] annotationsEncoding[annotationsEncodingLength];
+ *     int parameterAnnotationsEncodingLength;
+ *     byte[] parameterAnnotationsEncoding[parameterAnnotationsEncodingLength];
+ *     int typeAnnotationsEncodingLength;
+ *     byte[] typeAnnotationsEncoding[typeAnnotationsEncodingLength];
+ *     boolean hasRealParameterData;
+ *     int reflectParameterCount;          // only if hasRealParameterData is true
+ *     {
+ *         int reflectParameterNameIndex;  // index in frameInfoSourceMethodNames
+ *         int reflectParameterModifiers;
+ *     } reflectParameters[reflectParameterCount];
+ * } ReflectMethodEncoding;
+ * 
+ * + * The data for a hiding or reachable method is stored as follows: + * + *
+ * {
+ *     int methodNameIndex;                // index in frameInfoSourceMethodNames ("" for constructors)
+ *     int paramCount;
+ *     int[] paramTypeIndices[paramCount]; // index in frameInfoSourceClasses
+ * } SimpleMethodEncoding;
+ * 
+ */ +public class MethodMetadataDecoderImpl implements MethodMetadataDecoder { + public static final int NO_METHOD_METADATA = -1; + + @Fold + static boolean hasQueriedMethods() { + return !ImageSingletons.lookup(RuntimeReflectionSupport.class).getQueriedOnlyMethods().isEmpty(); + } + + /** + * This method returns two arrays. The first one contains the desired method data, the second + * one contains the names and parameter types of methods hiding methods declared in superclasses + * which therefore should not be returned by a call to getMethods(). + */ + @Override + public Pair getQueriedAndHidingMethods(DynamicHub declaringType) { + int dataOffset = getOffset(declaringType.getTypeID()); + if (SubstrateOptions.ConfigureReflectionMetadata.getValue() && getOffset(declaringType.getTypeID()) != NO_METHOD_METADATA) { + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); + UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, dataOffset, ByteArrayReader.supportsUnalignedMemoryAccess()); + + Executable[] queriedMethods = decodeArray(dataReader, Executable.class, () -> decodeReflectionMethod(dataReader, codeInfo, DynamicHub.toClass(declaringType))); + MethodDescriptor[] hiddenMethods = decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, DynamicHub.toClass(declaringType))); + return Pair.create(queriedMethods, hiddenMethods); + } else { + return Pair.create(new Executable[0], new MethodDescriptor[0]); + } + } + + @Override + public MethodDescriptor[] getAllReachableMethods() { + if (!SubstrateOptions.IncludeMethodData.getValue()) { + return new MethodDescriptor[0]; + } + + CodeInfo codeInfo = CodeInfoTable.getImageCodeInfo(); + byte[] data = ImageSingletons.lookup(MethodMetadataEncoding.class).getMethodsEncoding(); + UnsafeArrayTypeReader dataReader = UnsafeArrayTypeReader.create(data, 0, ByteArrayReader.supportsUnalignedMemoryAccess()); + List allMethods = new ArrayList<>(); + for (int i = 0; i < ImageSingletons.lookup(MethodMetadataEncoding.class).getIndexEncoding().length / Integer.BYTES; ++i) { + int dataOffset = getOffset(i); + if (dataOffset != NO_METHOD_METADATA) { + dataReader.setByteIndex(dataOffset); + if (SubstrateOptions.ConfigureReflectionMetadata.getValue()) { + /* Skip the queried methods data */ + decodeArray(dataReader, Executable.class, () -> decodeReflectionMethod(dataReader, codeInfo, null)); + decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, null)); + } + Class declaringClass = decodeType(dataReader, codeInfo); + if (declaringClass != null) { + allMethods.addAll(Arrays.asList(decodeArray(dataReader, MethodDescriptor.class, () -> decodeSimpleMethod(dataReader, codeInfo, declaringClass)))); + } + } + } + return allMethods.toArray(new MethodDescriptor[0]); + } + + private static int getOffset(int typeID) { + MethodMetadataEncoding encoding = ImageSingletons.lookup(MethodMetadataEncoding.class); + byte[] index = encoding.getIndexEncoding(); + UnsafeArrayTypeReader indexReader = UnsafeArrayTypeReader.create(index, Integer.BYTES * typeID, ByteArrayReader.supportsUnalignedMemoryAccess()); + return indexReader.getS4(); + } + + private static Executable decodeReflectionMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + String name = decodeName(buf, info); + Class[] parameterTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + int modifiers = buf.getUVInt(); + Class returnType = decodeType(buf, info); + Class[] exceptionTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + String signature = decodeName(buf, info); + byte[] annotations = decodeByteArray(buf); + byte[] parameterAnnotations = decodeByteArray(buf); + byte[] typeAnnotations = decodeByteArray(buf); + boolean hasRealParameterData = buf.getU1() == 1; + ReflectParameterDescriptor[] reflectParameters = hasRealParameterData ? decodeArray(buf, ReflectParameterDescriptor.class, () -> decodeReflectParameter(buf, info)) : null; + + Target_java_lang_reflect_Executable executable; + if (name.equals("")) { + assert returnType == void.class; + Target_java_lang_reflect_Constructor constructor = new Target_java_lang_reflect_Constructor(); + constructor.constructor(declaringClass, parameterTypes, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations); + executable = SubstrateUtil.cast(constructor, Target_java_lang_reflect_Executable.class); + } else { + Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); + method.constructor(declaringClass, name, parameterTypes, returnType, exceptionTypes, modifiers, -1, signature, annotations, parameterAnnotations, null); + executable = SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class); + } + if (hasQueriedMethods()) { + executable.hasRealParameterData = hasRealParameterData; + if (hasRealParameterData) { + fillReflectParameters(executable, reflectParameters); + } + executable.typeAnnotations = typeAnnotations; + } + return SubstrateUtil.cast(executable, Executable.class); + } + + private static void fillReflectParameters(Target_java_lang_reflect_Executable executable, ReflectParameterDescriptor[] reflectParameters) { + executable.parameters = new Target_java_lang_reflect_Parameter[reflectParameters.length]; + for (int i = 0; i < reflectParameters.length; ++i) { + executable.parameters[i] = new Target_java_lang_reflect_Parameter(); + executable.parameters[i].constructor(reflectParameters[i].getName(), reflectParameters[i].getModifiers(), executable, i); + } + } + + private static MethodDescriptor decodeSimpleMethod(UnsafeArrayTypeReader buf, CodeInfo info, Class declaringClass) { + String name = decodeName(buf, info); + Class[] paramTypes = decodeArray(buf, Class.class, () -> decodeType(buf, info)); + return new MethodDescriptor(declaringClass, name, paramTypes); + } + + private static Class decodeType(UnsafeArrayTypeReader buf, CodeInfo info) { + int classIndex = buf.getSVInt(); + if (classIndex == NO_METHOD_METADATA) { + return null; + } + return NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceClasses(info), classIndex); + } + + private static String decodeName(UnsafeArrayTypeReader buf, CodeInfo info) { + int nameIndex = buf.getSVInt(); + String name = NonmovableArrays.getObject(CodeInfoAccess.getFrameInfoSourceMethodNames(info), nameIndex); + /* Interning the string to ensure JDK8 method search succeeds */ + return name == null ? null : name.intern(); + } + + @SuppressWarnings("unchecked") + private static T[] decodeArray(UnsafeArrayTypeReader buf, Class elementType, Supplier elementDecoder) { + int length = buf.getUVInt(); + T[] result = (T[]) Array.newInstance(elementType, length); + for (int i = 0; i < length; ++i) { + result[i] = elementDecoder.get(); + } + return result; + } + + private static byte[] decodeByteArray(UnsafeArrayTypeReader buf) { + int length = buf.getUVInt(); + byte[] result = new byte[length]; + for (int i = 0; i < length; ++i) { + result[i] = (byte) buf.getS1(); + } + return result; + } + + private static ReflectParameterDescriptor decodeReflectParameter(UnsafeArrayTypeReader buf, CodeInfo info) { + String name = decodeName(buf, info); + int modifiers = buf.getS4(); + return new ReflectParameterDescriptor(name, modifiers); + } + + public static class ReflectParameterDescriptor { + private final String name; + private final int modifiers; + + public ReflectParameterDescriptor(String name, int modifiers) { + this.name = name; + this.modifiers = modifiers; + } + + public String getName() { + return name; + } + + public int getModifiers() { + return modifiers; + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoding.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java similarity index 98% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoding.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java index db1a5963a7c8..e67b880f796a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/MethodMetadataEncoding.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/MethodMetadataEncoding.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.code; +package com.oracle.svm.reflect.target; // Checkstyle: allow reflection diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/RuntimeReflectionConstructorsImpl.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/RuntimeReflectionConstructorsImpl.java deleted file mode 100644 index b2fe6e518843..000000000000 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/RuntimeReflectionConstructorsImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2021, 2021, 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.reflect.target; - -// Checkstyle: stop -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; -// Checkstyle: resume - -import com.oracle.svm.core.SubstrateUtil; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; - -public class RuntimeReflectionConstructorsImpl implements RuntimeReflectionConstructors { - @Override - public Method newMethod(Class declaringClass, String name, Class[] parameterTypes, Class returnType, Class[] checkedExceptions, int modifiers, String signature, - byte[] annotations, byte[] parameterAnnotations, byte[] annotationDefault, byte[] typeAnnotations, String[] reflectParameterNames, int[] reflectParameterModifiers) { - Target_java_lang_reflect_Method method = new Target_java_lang_reflect_Method(); - method.constructor(declaringClass, name, parameterTypes, returnType, checkedExceptions, modifiers, -1, signature, annotations, parameterAnnotations, annotationDefault); - if (RuntimeReflectionConstructors.hasQueriedMethods()) { - fillReflectParameters(SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class), reflectParameterNames, reflectParameterModifiers); - SubstrateUtil.cast(method, Target_java_lang_reflect_Executable.class).typeAnnotations = typeAnnotations; - } - return SubstrateUtil.cast(method, Method.class); - } - - @Override - public Constructor newConstructor(Class declaringClass, Class[] parameterTypes, Class[] checkedExceptions, int modifiers, String signature, - byte[] annotations, byte[] parameterAnnotations, byte[] typeAnnotations, String[] reflectParameterNames, int[] reflectParameterModifiers) { - Target_java_lang_reflect_Constructor cons = new Target_java_lang_reflect_Constructor(); - cons.constructor(declaringClass, parameterTypes, checkedExceptions, modifiers, -1, signature, annotations, parameterAnnotations); - if (RuntimeReflectionConstructors.hasQueriedMethods()) { - fillReflectParameters(SubstrateUtil.cast(cons, Target_java_lang_reflect_Executable.class), reflectParameterNames, reflectParameterModifiers); - SubstrateUtil.cast(cons, Target_java_lang_reflect_Executable.class).typeAnnotations = typeAnnotations; - } - return SubstrateUtil.cast(cons, Constructor.class); - } - - private static void fillReflectParameters(Target_java_lang_reflect_Executable executable, String[] reflectParameterNames, int[] reflectParameterModifiers) { - if (reflectParameterNames != null && reflectParameterModifiers != null) { - executable.hasRealParameterData = true; - assert reflectParameterNames.length == reflectParameterModifiers.length; - executable.parameters = new Target_java_lang_reflect_Parameter[reflectParameterNames.length]; - for (int i = 0; i < reflectParameterNames.length; ++i) { - executable.parameters[i] = new Target_java_lang_reflect_Parameter(); - executable.parameters[i].constructor(reflectParameterNames[i], reflectParameterModifiers[i], executable, i); - } - } else { - executable.hasRealParameterData = false; - } - } -} diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java index 811f314efb23..184ab2635b54 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Constructor.java @@ -43,8 +43,6 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; -import com.oracle.svm.core.code.CodeInfoDecoder; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; import com.oracle.svm.core.util.VMError; import com.oracle.svm.reflect.hosted.ExecutableAccessorComputer; @@ -135,10 +133,10 @@ public AnnotatedType getAnnotatedReceiverType() { return null; } - if (RuntimeReflectionConstructors.hasQueriedMethods()) { + if (MethodMetadataDecoderImpl.hasQueriedMethods()) { // A Constructor for an inner class return Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(SubstrateUtil.cast(holder, Target_java_lang_reflect_Executable.class).typeAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), SubstrateUtil.cast(this, AnnotatedElement.class), thisDeclClass, enclosingClass, diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java index 2203121a0b5d..8d15ee62e641 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_Executable.java @@ -44,10 +44,8 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; -import com.oracle.svm.core.code.CodeInfoDecoder; import com.oracle.svm.core.jdk.JDK8OrEarlier; import com.oracle.svm.core.option.SubstrateOptionsParser; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.reflect.hosted.ReflectionObjectReplacer; @@ -121,7 +119,7 @@ private Target_java_lang_reflect_Parameter[] privateGetParameters() { Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); if (holder.parameters != null) { return holder.parameters; - } else if (RuntimeReflectionConstructors.hasQueriedMethods()) { + } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { assert !hasRealParameterData; holder.parameters = synthesizeAllParams(); return holder.parameters; @@ -133,7 +131,7 @@ private Target_java_lang_reflect_Parameter[] privateGetParameters() { Map, Annotation> declaredAnnotations() { Map, Annotation> declAnnos; if ((declAnnos = declaredAnnotations) == null) { - if (!RuntimeReflectionConstructors.hasQueriedMethods()) { + if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { throw VMError.shouldNotReachHere(); } // Checkstyle: stop @@ -142,7 +140,7 @@ Map, Annotation> declaredAnnotations() { Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); declAnnos = Target_sun_reflect_annotation_AnnotationParser.parseAnnotations( holder.getAnnotationBytes(), - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), holder.getDeclaringClass()); declaredAnnotations = declAnnos; } @@ -158,12 +156,12 @@ Map, Annotation> declaredAnnotations() { @Substitute @SuppressWarnings({"unused", "hiding", "static-method"}) Annotation[][] parseParameterAnnotations(byte[] parameterAnnotations) { - if (!RuntimeReflectionConstructors.hasQueriedMethods()) { + if (!MethodMetadataDecoderImpl.hasQueriedMethods()) { throw VMError.shouldNotReachHere(); } return Target_sun_reflect_annotation_AnnotationParser.parseParameterAnnotations( parameterAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), getDeclaringClass()); } @@ -176,9 +174,9 @@ public AnnotatedType getAnnotatedReceiverType() { if (Modifier.isStatic(this.getModifiers())) { return null; } - if (RuntimeReflectionConstructors.hasQueriedMethods()) { + if (MethodMetadataDecoderImpl.hasQueriedMethods()) { AnnotatedType annotatedRecvType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), SubstrateUtil.cast(this, AnnotatedElement.class), getDeclaringClass(), getDeclaringClass(), @@ -194,9 +192,9 @@ public AnnotatedType[] getAnnotatedParameterTypes() { Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); if (holder.annotatedParameterTypes != null) { return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedParameterTypes); - } else if (RuntimeReflectionConstructors.hasQueriedMethods()) { + } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { AnnotatedType[] annotatedParamTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), SubstrateUtil.cast(this, AnnotatedElement.class), getDeclaringClass(), getAllGenericParameterTypes(), @@ -212,9 +210,9 @@ public AnnotatedType getAnnotatedReturnType0(@SuppressWarnings("unused") Type re Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); if (holder.annotatedReturnType != null) { return (AnnotatedType) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedReturnType); - } else if (RuntimeReflectionConstructors.hasQueriedMethods()) { + } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { AnnotatedType annotatedRetType = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedType(typeAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), SubstrateUtil.cast(this, AnnotatedElement.class), getDeclaringClass(), returnType, @@ -230,9 +228,9 @@ public AnnotatedType[] getAnnotatedExceptionTypes() { Target_java_lang_reflect_Executable holder = ReflectionHelper.getHolder(this); if (holder.annotatedExceptionTypes != null) { return (AnnotatedType[]) AnnotatedTypeEncoder.decodeAnnotationTypes(holder.annotatedExceptionTypes); - } else if (RuntimeReflectionConstructors.hasQueriedMethods()) { + } else if (MethodMetadataDecoderImpl.hasQueriedMethods()) { AnnotatedType[] annotatedExcTypes = Target_sun_reflect_annotation_TypeAnnotationParser.buildAnnotatedTypes(typeAnnotations, - CodeInfoDecoder.getMetadataPseudoConstantPool(), + new Target_jdk_internal_reflect_ConstantPool(), SubstrateUtil.cast(this, AnnotatedElement.class), getDeclaringClass(), getGenericExceptionTypes(), diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java index 2305a1f8d375..f03f6666b6b2 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_java_lang_reflect_ReflectAccess.java @@ -29,7 +29,6 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.reflect.RuntimeReflectionConstructors; /** * These substitutions are needed to set the genericInfo field on Method, Field, Constructor. The @@ -72,7 +71,8 @@ public Target_java_lang_reflect_Constructor copyConstructor(Target_java_lang_ref class Util_java_lang_reflect_ReflectAccess { static void copyExecutable(Target_java_lang_reflect_Executable copy, Target_java_lang_reflect_Executable executable) { - if (RuntimeReflectionConstructors.hasQueriedMethods()) { + if (MethodMetadataDecoderImpl.hasQueriedMethods()) { + /* Isolated to avoid pulling the full signature parsing capabilities from the JDK. */ copy.parameters = executable.parameters; } copy.declaredAnnotations = executable.declaredAnnotations; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_reflect_ConstantPool.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java similarity index 90% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_reflect_ConstantPool.java rename to substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java index de75409e10b3..1d28047bdc64 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_reflect_ConstantPool.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_jdk_internal_reflect_ConstantPool.java @@ -22,14 +22,14 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.jdk; +package com.oracle.svm.reflect.target; + +// Checkstyle: allow reflection import static com.oracle.svm.core.util.VMError.unimplemented; -// Checkstyle: stop import java.lang.reflect.Field; import java.lang.reflect.Member; -// Checkstyle: resume import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Substitute; @@ -38,7 +38,17 @@ import com.oracle.svm.core.c.NonmovableArrays; import com.oracle.svm.core.code.CodeInfoAccess; import com.oracle.svm.core.code.CodeInfoTable; - +import com.oracle.svm.core.jdk.JDK11OrLater; +import com.oracle.svm.core.jdk.Package_jdk_internal_reflect; + +/** + * This class provides a "fake" constant pool to be used while parsing encoded annotation values in + * reflection methods. The annotation encoding used by the JDK encodes values by their constant pool + * indices, whereas the Native Image implementation stores offsets in the + * {@link com.oracle.svm.core.code.CodeInfoEncoder.Encoders} class and string caches. Since the + * runtime does not handle JDK constant pools, this substitution enables reusing much of the JDK + * decoding logic for free. + */ @SuppressWarnings({"unused", "static-method", "hiding"}) @TargetClass(classNameProvider = Package_jdk_internal_reflect.class, className = "ConstantPool") public final class Target_jdk_internal_reflect_ConstantPool { diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java index 8dd4d31bd7d0..ce03cc41600b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_AnnotationParser.java @@ -24,12 +24,12 @@ */ package com.oracle.svm.reflect.target; +// Checkstyle: allow reflection + import java.lang.annotation.Annotation; import java.lang.annotation.AnnotationFormatError; import java.lang.annotation.RetentionPolicy; -// Checkstyle: stop import java.lang.reflect.Method; -// Checkstyle: resume import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -41,20 +41,18 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.jdk.Target_jdk_internal_reflect_ConstantPool; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.reflect.hosted.MethodMetadataEncoderImpl; -// Checkstyle: stop import sun.reflect.annotation.AnnotationParser; import sun.reflect.annotation.AnnotationType; import sun.reflect.annotation.EnumConstantNotPresentExceptionProxy; import sun.reflect.annotation.ExceptionProxy; -// Checkstyle: resume /** * Substitutions in this class are required to adapt the JDK encoding for annotations to our - * modified version of it. See {@link com.oracle.svm.core.code.CodeInfoEncoder} for a description of - * the changes and the rationale behind them. + * modified version of it. See {@link MethodMetadataEncoderImpl#encodeAnnotations(Annotation[])} for + * a description of the changes and the rationale behind them. */ @TargetClass(AnnotationParser.class) @SuppressWarnings("unused") @@ -164,10 +162,6 @@ private static Annotation parseAnnotation2(ByteBuffer buf, skipAnnotation(buf, false); return null; } - if (type == null) { - skipAnnotation(buf, false); - return null; - } Map> memberTypes = type.memberTypes(); Map memberValues = new LinkedHashMap<>(type.memberDefaults()); diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java index d786dc9b21d9..a7917c830787 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/target/Target_sun_reflect_annotation_TypeAnnotationParser.java @@ -37,7 +37,6 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.config.ConfigurationValues; -import com.oracle.svm.core.jdk.Target_jdk_internal_reflect_ConstantPool; import sun.reflect.annotation.TypeAnnotation; import sun.reflect.annotation.TypeAnnotationParser; diff --git a/vm/mx.vm/mx_vm_benchmark.py b/vm/mx.vm/mx_vm_benchmark.py index 8756fcc9e073..8e4bedd206b1 100644 --- a/vm/mx.vm/mx_vm_benchmark.py +++ b/vm/mx.vm/mx_vm_benchmark.py @@ -163,7 +163,6 @@ def __init__(self, vm, bm_suite, args): self.base_image_build_args += ['-H:+PrintAnalysisStatistics', '-H:AnalysisStatisticsFile=' + self.analysis_report_path] self.base_image_build_args += ['-H:+PrintCallEdges'] self.base_image_build_args += ['-H:+CollectImageBuildStatistics', '-H:ImageBuildStatisticsFile=' + self.image_build_report_path] - self.base_image_build_args += ['-H:+ConfigureReflectionMetadata'] if vm.is_llvm: self.base_image_build_args += ['-H:CompilerBackend=llvm', '-H:Features=org.graalvm.home.HomeFinderFeature', '-H:DeadlockWatchdogInterval=0'] if vm.gc: