/*
 * 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.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

import com.oracle.graal.pointsto.flow.InvokeTypeFlow;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.svm.core.annotate.RestrictHeapAccess;
import com.oracle.svm.hosted.code.AnalysisMethodCalleeWalker.CallPathVisitor.VisitResult;

import jdk.vm.ci.code.BytecodePosition;

Gather a list of the transitive blacklisted callees from methods annotated with RestrictHeapAccess that allocate.
/** * Gather a list of the transitive blacklisted callees from methods annotated with * {@link RestrictHeapAccess} that allocate. */
public class AnalysisMethodCalleeWalker {
A stack of methods that are currently being examined, to detect cycles in the call graph.
/** A stack of methods that are currently being examined, to detect cycles in the call graph. */
private final List<AnalysisMethod> path; public AnalysisMethodCalleeWalker() { path = new ArrayList<>(); }
Walk a method by applying a visitor to the method and all of its callees. Returns true if all the visits returned true, else returns false.
/** * Walk a method by applying a visitor to the method and all of its callees. Returns true if all * the visits returned true, else returns false. */
@SuppressWarnings("try") public boolean walkMethod(AnalysisMethod method, CallPathVisitor visitor) { if (visitor.prologue() != VisitResult.CONTINUE) { return false; } /* Initialize the path of callers. */ path.clear(); /* Walk the method and callees, but ignore the result. */ walkMethodAndCallees(method, null, null, visitor); final VisitResult epilogueResult = visitor.epilogue(); return (epilogueResult != VisitResult.CONTINUE); }
Visit this method, and the methods it calls.
/** Visit this method, and the methods it calls. */
VisitResult walkMethodAndCallees(AnalysisMethod method, AnalysisMethod caller, BytecodePosition invokePosition, CallPathVisitor visitor) { if (path.contains(method)) { /* * If the method is already on the path then I am in the middle of visiting it, so just * keep walking. */ return VisitResult.CUT; } path.add(method); try { /* Visit the method directly. */ final VisitResult directResult = visitor.visitMethod(method, caller, invokePosition, path.size()); if (directResult != VisitResult.CONTINUE) { return directResult; } /* Visit the callees of this method. */ final VisitResult calleeResult = walkCallees(method, visitor); if (calleeResult != VisitResult.CONTINUE) { return calleeResult; } /* Visit all the implementations of this method, ignoring if any of them says CUT. */ for (AnalysisMethod impl : method.getImplementations()) { walkMethodAndCallees(impl, caller, invokePosition, visitor); } return VisitResult.CONTINUE; } finally { path.remove(method); } }
Visit the callees of this method.
/** Visit the callees of this method. */
VisitResult walkCallees(AnalysisMethod method, CallPathVisitor visitor) { for (InvokeTypeFlow invoke : method.getTypeFlow().getInvokes()) { walkMethodAndCallees(invoke.getTargetMethod(), method, invoke.getSource(), visitor); } return VisitResult.CONTINUE; }
A visitor for HostedMethods, with a caller path.
/** A visitor for HostedMethods, with a caller path. */
abstract static class CallPathVisitor { public enum VisitResult { CONTINUE, CUT, QUIT }
Called before any method is visited. Returns true if visiting should continue, else false.
/** * Called before any method is visited. Returns true if visiting should continue, else * false. */
public VisitResult prologue() { /* The default is to continue the walk. */ return VisitResult.CONTINUE; }
Called for each method. Returns VisitResult.CONTINUE if the walk should continue, VisitResult.CUT if the walk should cut at this method, or VisitResult.QUIT if the walk should be stopped altogether.
/** * Called for each method. Returns {@link VisitResult#CONTINUE} if the walk should continue, * {@link VisitResult#CUT} if the walk should cut at this method, or * {@link VisitResult#QUIT} if the walk should be stopped altogether. */
public abstract VisitResult visitMethod(AnalysisMethod method, AnalysisMethod caller, BytecodePosition invokePosition, int depth);
Called after every method has been visited. Returns true if visiting should continue, else false.
/** * Called after every method has been visited. Returns true if visiting should continue, * else false. */
public VisitResult epilogue() { /* The default is to continue the walk. */ return VisitResult.CONTINUE; }
Printing a path to a stream.
/** Printing a path to a stream. */
void printPath(PrintStream trace, List<AnalysisMethod> path) { trace.print(" [Path: "); for (AnalysisMethod element : path) { trace.println(); trace.print(" " + element.format("%h.%n(%p)")); } trace.println("]"); } } }