Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -232,11 +232,24 @@ interface BeforeAnalysisAccess extends FeatureAccess {
void registerReachabilityHandler(Consumer<DuringAnalysisAccess> callback, Object... elements);

/**
* Registers a callback that is invoked once {@link Feature#duringAnalysis during analysis}
* for each time a method that overrides the specified {param baseMethod} is determined to
* be reachable at run time. In addition the handler will also get invoked once when the
* {param baseMethod} itself becomes reachable. The specific method that becomes reachable
* is passed to the handler as the second parameter.
* Registers a callback that is invoked once during analysis for each time a method that
* overrides the specified {param baseMethod} is determined to be reachable at run time. In
* addition, the handler will also get invoked once when the {param baseMethod} itself
* becomes reachable. The specific method that becomes reachable is passed to the handler as
* the second parameter.
* <p/>
* A method is considered reachable at run time if it can be executed, as determined via
* static analysis.
* <p/>
* Therefore, if a method can be statically bound (usually, that means it is final or
* private or static, but not abstract, or the declaring class is final), or it is a
* constructors, and it is the target of a reachable invoke, or it is inlined, then it is
* considered run time reachable.
* <p/>
* A virtual methods is considered run time reachable if its declaring-class or any of its
* subtypes is instantiated and the method is the target of a reachable invoke, or it is
* inlined. Even if the declaring type itself is not marked as instantiated the method can
* still be reachable via special invokes, e.g., `super` calls.
*
* @since 19.3
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* 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.graal.pointsto.meta;

import java.lang.reflect.Executable;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess;

import com.oracle.graal.pointsto.PointsToAnalysis;

public abstract class AnalysisElement {

/**
* Contains reachability handlers that are notified when the element is marked as reachable.
* Each handler is notified only once, and then it is removed from the set.
*/
private final Set<ElementReachableNotification> elementReachableNotifications = ConcurrentHashMap.newKeySet();

public void registerReachabilityNotification(ElementReachableNotification notification) {
elementReachableNotifications.add(notification);
}

public void notifyReachabilityCallback(AnalysisUniverse universe, ElementReachableNotification notification) {
notification.notifyCallback(universe, this);
elementReachableNotifications.remove(notification);
}

protected void notifyReachabilityCallbacks(AnalysisUniverse universe) {
elementReachableNotifications.forEach(c -> c.notifyCallback(universe, this));
elementReachableNotifications.removeIf(ElementReachableNotification::isNotified);
}

public abstract boolean isReachable();

protected abstract void onReachable();

/** Return true if reachability handlers should be executed for this element. */
public boolean isTriggered() {
return isReachable();
}

public static final class ElementReachableNotification {

private final Consumer<DuringAnalysisAccess> callback;
private final AtomicBoolean notified = new AtomicBoolean();

public ElementReachableNotification(Consumer<DuringAnalysisAccess> callback) {
this.callback = callback;
}

public boolean isNotified() {
return notified.get();
}

/**
* Notify the callback exactly once. Note that this callback can be shared by multiple
* triggers, the one that triggers it is passed into triggeredElement for debugging.
*/
private void notifyCallback(AnalysisUniverse universe, AnalysisElement triggeredElement) {
assert triggeredElement.isTriggered();
if (!notified.getAndSet(true)) {
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess()));
}
}
}

public static final class SubtypeReachableNotification {
private final BiConsumer<DuringAnalysisAccess, Class<?>> callback;
private final Set<AnalysisType> seenSubtypes = ConcurrentHashMap.newKeySet();

public SubtypeReachableNotification(BiConsumer<DuringAnalysisAccess, Class<?>> callback) {
this.callback = callback;
}

/** Notify the callback exactly once for each reachable subtype. */
public void notifyCallback(AnalysisUniverse universe, AnalysisType reachableSubtype) {
assert reachableSubtype.isReachable();
if (seenSubtypes.add(reachableSubtype)) {
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess(), reachableSubtype.getJavaClass()));
}
}
}

public static final class MethodOverrideReachableNotification {
private final BiConsumer<DuringAnalysisAccess, Executable> callback;
private final Set<AnalysisMethod> seenOverride = ConcurrentHashMap.newKeySet();

public MethodOverrideReachableNotification(BiConsumer<DuringAnalysisAccess, Executable> callback) {
this.callback = callback;
}

/** Notify the callback exactly once for each reachable method override. */
public void notifyCallback(AnalysisUniverse universe, AnalysisMethod reachableOverride) {
assert reachableOverride.isReachable();
if (seenOverride.add(reachableOverride)) {
execute(universe, () -> callback.accept(universe.getConcurrentAnalysisAccess(), reachableOverride.getJavaMethod()));
}
}
}

private static void execute(AnalysisUniverse universe, Runnable task) {
/*
* Post the tasks to the analysis executor. This ensures that even for elements registered
* as reachable early, before the analysis is started, the reachability callbacks are run
* during the analysis.
*/
((PointsToAnalysis) universe.getBigbang()).postTask(task);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
import org.graalvm.util.GuardedAnnotationAccess;

import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.api.HostVM;
import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.flow.ContextInsensitiveFieldTypeFlow;
import com.oracle.graal.pointsto.flow.FieldTypeFlow;
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
Expand All @@ -52,7 +52,7 @@
import jdk.vm.ci.meta.ResolvedJavaField;
import jdk.vm.ci.meta.ResolvedJavaType;

public abstract class AnalysisField implements ResolvedJavaField, OriginalFieldProvider {
public abstract class AnalysisField extends AnalysisElement implements ResolvedJavaField, OriginalFieldProvider {

@SuppressWarnings("rawtypes")//
private static final AtomicReferenceFieldUpdater<AnalysisField, Object> OBSERVERS_UPDATER = //
Expand Down Expand Up @@ -242,6 +242,7 @@ public boolean registerAsAccessed() {
boolean firstAttempt = AtomicUtils.atomicMark(isAccessed);
notifyUpdateAccessInfo();
if (firstAttempt) {
onReachable();
getUniverse().onFieldAccessed(this);
getUniverse().getHeapScanner().onFieldRead(this);
}
Expand All @@ -255,6 +256,7 @@ public boolean registerAsRead(MethodTypeFlow method) {
readBy.put(method, Boolean.TRUE);
}
if (firstAttempt) {
onReachable();
getUniverse().onFieldAccessed(this);
getUniverse().getHeapScanner().onFieldRead(this);
}
Expand All @@ -273,15 +275,19 @@ public boolean registerAsWritten(MethodTypeFlow method) {
if (writtenBy != null && method != null) {
writtenBy.put(method, Boolean.TRUE);
}
if (firstAttempt && (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object)) {
getUniverse().onFieldAccessed(this);
if (firstAttempt) {
onReachable();
if (Modifier.isVolatile(getModifiers()) || getStorageKind() == JavaKind.Object) {
getUniverse().onFieldAccessed(this);
}
}
return firstAttempt;
}

public void markFolded() {
if (AtomicUtils.atomicMark(isFolded)) {
getDeclaringClass().registerAsReachable();
onReachable();
}
}

Expand Down Expand Up @@ -385,10 +391,16 @@ public boolean isFolded() {
return isFolded.get();
}

@Override
public boolean isReachable() {
return isAccessed.get() || isRead.get() || isWritten.get() || isFolded.get();
}

@Override
public void onReachable() {
notifyReachabilityCallbacks(declaringClass.getUniverse());
}

public void setCanBeNull(boolean canBeNull) {
this.canBeNull = canBeNull;
notifyUpdateAccessInfo();
Expand Down
Loading