diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConcurrentReachabilityHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConcurrentReachabilityHandler.java deleted file mode 100644 index 79488344a753..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConcurrentReachabilityHandler.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.hosted; - -import java.lang.reflect.Executable; -import java.lang.reflect.Field; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import com.oracle.graal.pointsto.meta.AnalysisElement; -import com.oracle.graal.pointsto.meta.AnalysisElement.ElementNotification; -import com.oracle.graal.pointsto.meta.AnalysisElement.MethodOverrideReachableNotification; -import com.oracle.graal.pointsto.meta.AnalysisElement.SubtypeReachableNotification; -import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; -import com.oracle.graal.pointsto.meta.AnalysisMethod; -import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.core.feature.InternalFeature; -import com.oracle.svm.core.util.UserError; -import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; - -public class ConcurrentReachabilityHandler extends ReachabilityHandler implements InternalFeature { - - private final Map, ElementNotification> reachabilityNotifications = new ConcurrentHashMap<>(); - - @Override - public void registerMethodOverrideReachabilityHandler(BeforeAnalysisAccessImpl access, BiConsumer callback, Executable baseMethod) { - AnalysisMetaAccess metaAccess = access.getMetaAccess(); - AnalysisMethod baseAnalysisMethod = metaAccess.lookupJavaMethod(baseMethod); - - MethodOverrideReachableNotification notification = new MethodOverrideReachableNotification(callback); - baseAnalysisMethod.registerOverrideReachabilityNotification(notification); - - /* - * Notify for already reachable overrides. When a new override becomes reachable all - * installed reachability callbacks in the supertypes declaring the method are triggered. - */ - for (AnalysisMethod override : access.reachableMethodOverrides(baseAnalysisMethod)) { - notification.notifyCallback(metaAccess.getUniverse(), override); - } - } - - @Override - public void registerSubtypeReachabilityHandler(BeforeAnalysisAccessImpl access, BiConsumer> callback, Class baseClass) { - AnalysisMetaAccess metaAccess = access.getMetaAccess(); - AnalysisType baseType = metaAccess.lookupJavaType(baseClass); - - SubtypeReachableNotification notification = new SubtypeReachableNotification(callback); - baseType.registerSubtypeReachabilityNotification(notification); - - /* - * Notify for already reachable subtypes. When a new type becomes reachable all installed - * reachability callbacks in the supertypes are triggered. - */ - for (AnalysisType subtype : access.reachableSubtypes(baseType)) { - notification.notifyCallback(metaAccess.getUniverse(), subtype); - } - } - - @Override - public void registerReachabilityHandler(BeforeAnalysisAccessImpl access, Consumer callback, Object[] triggers) { - registerConcurrentReachabilityHandler(access, callback, triggers); - } - - private void registerConcurrentReachabilityHandler(BeforeAnalysisAccessImpl access, Consumer callback, Object[] triggers) { - AnalysisMetaAccess metaAccess = access.getMetaAccess(); - - /* - * All callback->notification pairs are tracked by the reachabilityNotifications map to - * prevent registering the same callback multiple times. The notifications are also tracked - * by each AnalysisElement, i.e., each trigger, and are removed as soon as they are - * notified. - */ - ElementNotification notification = reachabilityNotifications.computeIfAbsent(callback, ElementNotification::new); - - if (notification.isNotified()) { - /* Already notified from an earlier registration, nothing to do. */ - return; - } - - for (Object trigger : triggers) { - AnalysisElement analysisElement; - if (trigger instanceof Class) { - analysisElement = metaAccess.lookupJavaType((Class) trigger); - } else if (trigger instanceof Field) { - analysisElement = metaAccess.lookupJavaField((Field) trigger); - } else if (trigger instanceof Executable) { - analysisElement = metaAccess.lookupJavaMethod((Executable) trigger); - } else { - throw UserError.abort("'registerReachabilityHandler' called with an element that is not a Class, Field, or Executable: %s", trigger.getClass().getTypeName()); - } - - analysisElement.registerReachabilityNotification(notification); - if (analysisElement.isTriggered()) { - /* - * Element already triggered, just notify the callback. At this point we could just - * notify the callback and bail out, but, for debugging, it may be useful to execute - * the notification for each trigger. Note that although the notification can be - * shared between multiple triggers the notification mechanism ensures that the - * callback itself is only executed once. - */ - analysisElement.notifyReachabilityCallback(access.getUniverse(), notification); - } - } - } -} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java index 9d268b206d05..ed381b2e755f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/FeatureImpl.java @@ -39,6 +39,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; @@ -59,6 +60,10 @@ import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; +import com.oracle.graal.pointsto.meta.AnalysisElement; +import com.oracle.graal.pointsto.meta.AnalysisElement.ElementNotification; +import com.oracle.graal.pointsto.meta.AnalysisElement.MethodOverrideReachableNotification; +import com.oracle.graal.pointsto.meta.AnalysisElement.SubtypeReachableNotification; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -358,14 +363,13 @@ public SVMHost getHostVM() { public static class BeforeAnalysisAccessImpl extends AnalysisAccessBase implements Feature.BeforeAnalysisAccess { private final NativeLibraries nativeLibraries; - private final ReachabilityHandler reachabilityHandler; private final ClassForNameSupport classForNameSupport; + private final Map, ElementNotification> reachabilityNotifications = new ConcurrentHashMap<>(); public BeforeAnalysisAccessImpl(FeatureHandler featureHandler, ImageClassLoader imageClassLoader, Inflation bb, NativeLibraries nativeLibraries, DebugContext debugContext) { super(featureHandler, imageClassLoader, bb, debugContext); this.nativeLibraries = nativeLibraries; - this.reachabilityHandler = new ConcurrentReachabilityHandler(); this.classForNameSupport = ClassForNameSupport.currentLayer(); } @@ -469,22 +473,80 @@ public void registerHierarchyForReflectiveInstantiation(Class c) { @Override public void registerReachabilityHandler(Consumer callback, Object... elements) { - reachabilityHandler.registerReachabilityHandler(this, callback, elements); + /* + * All callback->notification pairs are tracked by the reachabilityNotifications map to + * prevent registering the same callback multiple times. The notifications are also + * tracked by each AnalysisElement, i.e., each trigger, and are removed as soon as they + * are notified. + */ + ElementNotification notification = reachabilityNotifications.computeIfAbsent(callback, ElementNotification::new); + + if (notification.isNotified()) { + /* Already notified from an earlier registration, nothing to do. */ + return; + } + + for (Object trigger : elements) { + AnalysisElement analysisElement = switch (trigger) { + case Class clazz -> getMetaAccess().lookupJavaType(clazz); + case Field field -> getMetaAccess().lookupJavaField(field); + case Executable executable -> getMetaAccess().lookupJavaMethod(executable); + default -> throw UserError.abort("'registerReachabilityHandler' called with an element that is not a Class, Field, or Executable: %s", + trigger.getClass().getTypeName()); + }; + + analysisElement.registerReachabilityNotification(notification); + if (analysisElement.isTriggered()) { + /* + * Element already triggered, just notify the callback. At this point we could + * just notify the callback and bail out, but, for debugging, it may be useful + * to execute the notification for each trigger. Note that although the + * notification can be shared between multiple triggers the notification + * mechanism ensures that the callback itself is only executed once. + */ + analysisElement.notifyReachabilityCallback(getUniverse(), notification); + } + } } @Override public void registerMethodOverrideReachabilityHandler(BiConsumer callback, Executable baseMethod) { - reachabilityHandler.registerMethodOverrideReachabilityHandler(this, callback, baseMethod); + AnalysisMethod baseAnalysisMethod = getMetaAccess().lookupJavaMethod(baseMethod); + MethodOverrideReachableNotification notification = new MethodOverrideReachableNotification(callback); + baseAnalysisMethod.registerOverrideReachabilityNotification(notification); + + /* + * Notify for already reachable overrides. When a new override becomes reachable all + * installed reachability callbacks in the supertypes declaring the method are + * triggered. + */ + for (AnalysisMethod override : reachableMethodOverrides(baseAnalysisMethod)) { + notification.notifyCallback(getUniverse(), override); + } } @Override public void registerSubtypeReachabilityHandler(BiConsumer> callback, Class baseClass) { - reachabilityHandler.registerSubtypeReachabilityHandler(this, callback, baseClass); + AnalysisType baseType = getMetaAccess().lookupJavaType(baseClass); + SubtypeReachableNotification notification = new SubtypeReachableNotification(callback); + baseType.registerSubtypeReachabilityNotification(notification); + + /* + * Notify for already reachable subtypes. When a new type becomes reachable all + * installed reachability callbacks in the supertypes are triggered. + */ + for (AnalysisType subtype : reachableSubtypes(baseType)) { + notification.notifyCallback(getUniverse(), subtype); + } } @Override public void registerClassInitializerReachabilityHandler(Consumer callback, Class clazz) { - reachabilityHandler.registerClassInitializerReachabilityHandler(this, callback, clazz); + /* + * In our current static analysis implementations, there is no difference between the + * reachability of a class and the reachability of its class initializer. + */ + registerReachabilityHandler(callback, clazz); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandler.java deleted file mode 100644 index 8127d8af590f..000000000000 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ReachabilityHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.hosted; - -import java.lang.reflect.Executable; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; - -import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; - -public abstract class ReachabilityHandler { - - public abstract void registerMethodOverrideReachabilityHandler(BeforeAnalysisAccessImpl access, BiConsumer callback, Executable baseMethod); - - public abstract void registerSubtypeReachabilityHandler(BeforeAnalysisAccessImpl access, BiConsumer> callback, Class baseClass); - - public final void registerClassInitializerReachabilityHandler(BeforeAnalysisAccessImpl access, Consumer callback, Class clazz) { - /* - * In our current static analysis implementations, there is no difference between the - * reachability of a class and the reachability of its class initializer. - */ - registerReachabilityHandler(access, callback, new Object[]{clazz}); - } - - public abstract void registerReachabilityHandler(BeforeAnalysisAccessImpl access, Consumer callback, Object[] triggers); -}