From 3fd43b04267e2fc3e13d1692dbd01502209935dc Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Mon, 2 Oct 2023 11:54:51 +0200 Subject: [PATCH 1/4] svm: add @BasedOnJDKClass annotation --- .../oracle/svm/core/util/BasedOnJDKClass.java | 71 +++++++++++++++++++ .../svm/core/util/BasedOnJDKClasses.java | 44 ++++++++++++ .../AnnotationSubstitutionProcessor.java | 2 +- 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java new file mode 100644 index 000000000000..f07a0981bf0e --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023, 2023, 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.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.function.Function; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.annotate.TargetClass; + +/** + * Documents that the class is based on a JDK class without {@linkplain TargetClass substituting} + * it. + */ +@Repeatable(BasedOnJDKClasses.class) +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.TYPE}) +@Platforms(Platform.HOSTED_ONLY.class) +public @interface BasedOnJDKClass { + + /** + * @see TargetClass#value() + */ + Class value() default BasedOnJDKClass.class; + + /** + * @see TargetClass#className() + */ + String className() default ""; + + /** + * @see TargetClass#classNameProvider() + */ + Class> classNameProvider() default BasedOnJDKClass.NoClassNameProvider.class; + + /** + * @see TargetClass#innerClass() + */ + String[] innerClass() default {}; + + interface NoClassNameProvider extends Function { + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java new file mode 100644 index 000000000000..a792afa55469 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, 2023, 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.util; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +/** + * Support for making {@link BasedOnJDKClass} {@linkplain java.lang.annotation.Repeatable + * repeatable}. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = {ElementType.TYPE}) +@Platforms(Platform.HOSTED_ONLY.class) +public @interface BasedOnJDKClasses { + BasedOnJDKClass[] value(); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 12a54326b5b7..9c4ad092f9fe 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -1105,7 +1105,7 @@ protected Class findTargetClass(Class annotatedBaseClass, TargetClass targ return holder; } - private static Class findInnerClass(Class outerClass, String innerClassSimpleName) { + protected static Class findInnerClass(Class outerClass, String innerClassSimpleName) { for (Class innerClass : outerClass.getDeclaredClasses()) { // Checkstyle: allow Class.getSimpleName String simpleName = innerClass.getSimpleName(); From 7547daa6f7db065f7a004d7937f61a7d4d641ecc Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Tue, 3 Oct 2023 12:09:07 +0200 Subject: [PATCH 2/4] svm: annotate JavaMonitor with @BasedOnJDKClass --- .../src/com/oracle/svm/core/monitor/JavaMonitor.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java index 9bbd4a564846..482a211194ef 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/monitor/JavaMonitor.java @@ -30,6 +30,8 @@ import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.NOT_FREQUENT_PROBABILITY; import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability; +import java.util.concurrent.locks.ReentrantLock; + import org.graalvm.nativeimage.IsolateThread; import com.oracle.svm.core.Uninterruptible; @@ -37,6 +39,7 @@ import com.oracle.svm.core.jfr.SubstrateJVM; import com.oracle.svm.core.jfr.events.JavaMonitorEnterEvent; import com.oracle.svm.core.thread.JavaThreads; +import com.oracle.svm.core.util.BasedOnJDKClass; import com.oracle.svm.core.util.VMError; import jdk.internal.misc.Unsafe; @@ -55,6 +58,8 @@ * to store the number of lock acquisitions, enabling various optimizations. * */ +@BasedOnJDKClass(ReentrantLock.class) +@BasedOnJDKClass(value = ReentrantLock.class, innerClass = "Sync") public class JavaMonitor extends JavaMonitorQueuedSynchronizer { protected long latestJfrTid; From 00a5757ad2109ae08c2945bf49edabd7f2074be5 Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Mon, 9 Oct 2023 09:20:48 +0200 Subject: [PATCH 3/4] svm: move BasedOnJDKClasses to BasedOnJDKClass.List --- .../oracle/svm/core/util/BasedOnJDKClass.java | 12 ++++- .../svm/core/util/BasedOnJDKClasses.java | 44 ------------------- 2 files changed, 11 insertions(+), 45 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java index f07a0981bf0e..b3989f3ac584 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClass.java @@ -40,7 +40,7 @@ * Documents that the class is based on a JDK class without {@linkplain TargetClass substituting} * it. */ -@Repeatable(BasedOnJDKClasses.class) +@Repeatable(BasedOnJDKClass.List.class) @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE}) @Platforms(Platform.HOSTED_ONLY.class) @@ -68,4 +68,14 @@ interface NoClassNameProvider extends Function { } + + /** + * Support for making {@link BasedOnJDKClass} {@linkplain Repeatable repeatable}. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(value = {ElementType.TYPE}) + @Platforms(Platform.HOSTED_ONLY.class) + @interface List { + BasedOnJDKClass[] value(); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java deleted file mode 100644 index a792afa55469..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/BasedOnJDKClasses.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023, 2023, 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.util; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - -/** - * Support for making {@link BasedOnJDKClass} {@linkplain java.lang.annotation.Repeatable - * repeatable}. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(value = {ElementType.TYPE}) -@Platforms(Platform.HOSTED_ONLY.class) -public @interface BasedOnJDKClasses { - BasedOnJDKClass[] value(); -} From a285ce510b2d11f094638b879e23d2e4eee8a0ef Mon Sep 17 00:00:00 2001 From: Josef Eisl Date: Mon, 9 Oct 2023 10:35:49 +0200 Subject: [PATCH 4/4] svm: decouple findTargetClass from TargetClass --- .../AnnotationSubstitutionProcessor.java | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java index 9c4ad092f9fe..b577937e70ba 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/AnnotationSubstitutionProcessor.java @@ -43,6 +43,7 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BooleanSupplier; +import java.util.function.Function; import java.util.function.Predicate; import org.graalvm.nativeimage.AnnotationAccess; @@ -1043,24 +1044,30 @@ Class findTargetClass(Class annotatedBaseClass, TargetClass target) { } protected Class findTargetClass(Class annotatedBaseClass, TargetClass target, boolean checkOnlyWith) { + return findTargetClass(TargetClass.class, TargetClass.NoClassNameProvider.class, + annotatedBaseClass, target, target.value(), target.className(), target.classNameProvider(), target.innerClass(), checkOnlyWith ? target.onlyWith() : null); + } + + protected Class findTargetClass(Class targetClass, Class noClassNameProviderClass, + Class annotatedBaseClass, T target, Class value, String targetClassName, Class> classNameProvider, String[] innerClasses, Class[] onlyWith) { + String className; - if (target.value() != TargetClass.class) { - guarantee(target.className().isEmpty(), "Both class and class name specified for substitution"); - guarantee(target.classNameProvider() == TargetClass.NoClassNameProvider.class, "Both class and classNameProvider specified for substitution"); - className = target.value().getName(); - } else if (target.classNameProvider() != TargetClass.NoClassNameProvider.class) { + if (value != targetClass) { + guarantee(targetClassName.isEmpty(), "Both class and class name specified for substitution"); + guarantee(classNameProvider == noClassNameProviderClass, "Both class and classNameProvider specified for substitution"); + className = value.getName(); + } else if (classNameProvider != noClassNameProviderClass) { try { - className = ReflectionUtil.newInstance(target.classNameProvider()).apply(target); + className = ReflectionUtil.newInstance(classNameProvider).apply(target); } catch (ReflectionUtilError ex) { - throw UserError.abort(ex.getCause(), "Cannot instantiate classNameProvider: %s. The class must have a parameterless constructor.", target.classNameProvider().getTypeName()); + throw UserError.abort(ex.getCause(), "Cannot instantiate classNameProvider: %s. The class must have a parameterless constructor.", classNameProvider.getTypeName()); } } else { - guarantee(!target.className().isEmpty(), "Neither class, className, nor classNameProvider specified for substitution"); - className = target.className(); + guarantee(!targetClassName.isEmpty(), "Neither class, className, nor classNameProvider specified for substitution"); + className = targetClassName; } - - if (checkOnlyWith) { - for (Class onlyWithClass : target.onlyWith()) { + if (onlyWith != null) { + for (Class onlyWithClass : onlyWith) { Object onlyWithProvider; try { onlyWithProvider = ReflectionUtil.newInstance(onlyWithClass); @@ -1091,8 +1098,8 @@ protected Class findTargetClass(Class annotatedBaseClass, TargetClass targ throw UserError.abort("Substitution target for %s is not loaded. Use field `onlyWith` in the `TargetClass` annotation to make substitution only active when needed.", annotatedBaseClass.getName()); } - if (target.innerClass().length > 0) { - for (String innerClass : target.innerClass()) { + if (innerClasses.length > 0) { + for (String innerClass : innerClasses) { Class prevHolder = holder; holder = findInnerClass(prevHolder, innerClass); if (holder == null) {