com.oracle.svm.hosted.code.RestrictHeapAccessCalleesImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of svm Show documentation
Show all versions of svm Show documentation
SubstrateVM image builder components
/*
* Copyright (c) 2017, 2017, 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.code;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.ImageSingletons;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.RestrictHeapAccess;
import com.oracle.svm.core.annotate.RestrictHeapAccess.Access;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.heap.RestrictHeapAccessCallees;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
import com.oracle.svm.hosted.meta.HostedMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod;
/**
* Construct a list of all the methods that are, or are called from, methods annotated with
* {@link RestrictHeapAccess} or {@link Uninterruptible}.
*/
public class RestrictHeapAccessCalleesImpl implements RestrictHeapAccessCallees {
/**
* A map from a callee to a caller on a path to an annotated caller. The keys are the set of
* callees that must not allocate. The values are for printing a call path for error messages.
*/
private Map calleeToCallerMap;
/** AssertionErrors are cut points, because their allocations are removed. */
private List assertionErrorConstructorList;
/** Initialize the set of callees only once. */
private boolean initialized;
/** Constructor for the singleton instance. */
public RestrictHeapAccessCalleesImpl() {
calleeToCallerMap = Collections.emptyMap();
this.assertionErrorConstructorList = Collections.emptyList();
initialized = false;
}
/** This gets called multiple times, but I only need one AnalysisMethod to be happy. */
public void setAssertionErrorConstructors(List resolvedConstructorList) {
if (assertionErrorConstructorList.isEmpty()) {
assertionErrorConstructorList = resolvedConstructorList;
}
}
public RestrictionInfo getRestrictionInfo(ResolvedJavaMethod method) {
return calleeToCallerMap.get(methodToKey(method));
}
@Override
public boolean mustNotAllocate(ResolvedJavaMethod method) {
RestrictionInfo info = getRestrictionInfo(method);
return info != null && (info.getAccess() == Access.NO_ALLOCATION || info.getAccess().isMoreRestrictiveThan(Access.NO_ALLOCATION));
}
/** Get the map from a callee to a caller. */
public Map getCallerMap() {
return calleeToCallerMap;
}
/**
* Aggregate a set of methods that are annotated with {@link RestrictHeapAccess} or with
* {@link Uninterruptible}, or methods that are called from those methods.
*/
public void aggregateMethods(Collection methods) {
assert !initialized : "RestrictHeapAccessCallees.aggregateMethods: Should only initialize once.";
final Map aggregation = new HashMap<>();
final MethodAggregator visitor = new MethodAggregator(aggregation, assertionErrorConstructorList);
final AnalysisMethodCalleeWalker walker = new AnalysisMethodCalleeWalker();
for (AnalysisMethod method : methods) {
final RestrictHeapAccess annotation = method.getAnnotation(RestrictHeapAccess.class);
if ((annotation != null && annotation.access() != Access.UNRESTRICTED) || method.isAnnotationPresent(Uninterruptible.class)) {
for (AnalysisMethod calleeImpl : method.getImplementations()) {
walker.walkMethod(calleeImpl, visitor);
}
}
}
calleeToCallerMap = Collections.unmodifiableMap(aggregation);
initialized = true;
}
/**
* During analysis, the ResolvedJavaMethod parameter will be an AnalysisMethod, but during
* compilation it will be a HostedMethod. Since I am using the AnalysisMethod as the key to the
* map, I get an AnalysisMethod to use as the key.
*/
private static AnalysisMethod methodToKey(ResolvedJavaMethod method) {
final AnalysisMethod result;
if (method instanceof AnalysisMethod) {
result = (AnalysisMethod) method;
} else if (method instanceof HostedMethod) {
result = ((HostedMethod) method).getWrapped();
} else {
throw VMError.shouldNotReachHere("RestrictHeapAccessCallees.methodToKey: ResolvedJavaMethod is neither an AnalysisMethod nor a HostedMethod: " + method);
}
return result;
}
/** A visitor that aggregates callees and a callee-to-caller edge in the call graph. */
static class MethodAggregator extends AnalysisMethodCalleeWalker.CallPathVisitor {
/** The map from a callee to a caller that is being constructed. */
private final Map calleeToCallerMap;
/** The constructor {@link AssertionError#AssertionError()}. */
private final List assertionErrorConstructorList;
/** Constructor. */
MethodAggregator(Map calleeToCallerMap, List assertionErrorConstructorList) {
this.calleeToCallerMap = calleeToCallerMap;
this.assertionErrorConstructorList = assertionErrorConstructorList;
}
/** Visit a method and add it to the set of methods that should not allocate. */
@Override
public VisitResult visitMethod(AnalysisMethod callee, AnalysisMethod caller, Invoke invoke, int depth) {
Access access = Access.UNRESTRICTED;
boolean overridesCallers = false;
if (callee.isAnnotationPresent(Uninterruptible.class)) {
access = Access.NO_ALLOCATION;
}
RestrictHeapAccess annotation = callee.getAnnotation(RestrictHeapAccess.class);
if (annotation != null) {
access = annotation.access();
overridesCallers = annotation.overridesCallers();
}
if (overridesCallers || caller == null) {
if (access == Access.UNRESTRICTED) {
return VisitResult.CUT;
}
} else {
Access callerAccess = calleeToCallerMap.get(caller).getAccess();
if (callerAccess.isMoreRestrictiveThan(access)) {
access = callerAccess;
}
}
if (access == Access.NO_ALLOCATION && assertionErrorConstructorList != null && assertionErrorConstructorList.contains(callee)) {
/* Ignore AssertionError allocations: ImplicitExceptionsPlugin will replace them */
return VisitResult.CUT;
}
RestrictionInfo restrictionInfo = calleeToCallerMap.get(callee);
if (restrictionInfo != null && !access.isMoreRestrictiveThan(restrictionInfo.getAccess())) {
/* Earlier traversal with same or higher level of restriction, so stop here. */
return VisitResult.CUT;
}
StackTraceElement callerStackTraceElement = (caller != null) ? caller.asStackTraceElement(invoke.bci()) : null;
restrictionInfo = new RestrictionInfo(access, caller, callerStackTraceElement, callee);
calleeToCallerMap.put(callee, restrictionInfo);
return VisitResult.CONTINUE;
}
}
/** Information about a restricted method, for error messages. */
public static class RestrictionInfo {
/** The transitively determined level of restricted access. */
private final RestrictHeapAccess.Access access;
/** The caller in the invocation, if any. */
private final AnalysisMethod caller;
/** The stack trace element of the invocation, if any. */
private final StackTraceElement invocationStackTraceElement;
/** The method to which the restriction applies. */
private final AnalysisMethod method;
RestrictionInfo(Access access, AnalysisMethod caller, StackTraceElement stackTraceElement, AnalysisMethod method) {
this.access = access;
this.caller = caller;
this.invocationStackTraceElement = stackTraceElement;
this.method = method;
}
public Access getAccess() {
return access;
}
public AnalysisMethod getCaller() {
return caller;
}
public StackTraceElement getInvocationStackTraceElement() {
return invocationStackTraceElement;
}
public AnalysisMethod getMethod() {
return method;
}
}
}
@AutomaticFeature
class RestrictHeapAccessCalleesFeature implements Feature {
/** This is called early, to register in the VMConfiguration. */
@Override
public void afterRegistration(AfterRegistrationAccess access) {
ImageSingletons.add(RestrictHeapAccessCallees.class, new RestrictHeapAccessCalleesImpl());
}
/** This is called during analysis, to find the AssertionError constructors. */
@Override
public void duringAnalysis(DuringAnalysisAccess access) {
List assertionErrorConstructorList = initializeAssertionErrorConstructors(access);
((RestrictHeapAccessCalleesImpl) ImageSingletons.lookup(RestrictHeapAccessCallees.class)).setAssertionErrorConstructors(assertionErrorConstructorList);
}
private static List initializeAssertionErrorConstructors(DuringAnalysisAccess access) {
final List result = new ArrayList<>();
result.add(findAssertionConstructor(access));
result.add(findAssertionConstructor(access, boolean.class));
result.add(findAssertionConstructor(access, char.class));
result.add(findAssertionConstructor(access, int.class));
result.add(findAssertionConstructor(access, long.class));
result.add(findAssertionConstructor(access, float.class));
result.add(findAssertionConstructor(access, double.class));
result.add(findAssertionConstructor(access, Object.class));
result.add(findAssertionConstructor(access, String.class, Throwable.class));
return result;
}
/** Look up an AssertionError constructor. */
private static ResolvedJavaMethod findAssertionConstructor(DuringAnalysisAccess access, Class>... parameterTypes) {
try {
final Constructor reflectiveConstructor = AssertionError.class.getConstructor(parameterTypes);
final ResolvedJavaMethod resolvedConstructor = ((DuringAnalysisAccessImpl) access).getMetaAccess().lookupJavaMethod(reflectiveConstructor);
return resolvedConstructor;
} catch (NoSuchMethodException | SecurityException ex) {
throw VMError.shouldNotReachHere("Should have found AssertionError constructor." + ex);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy