All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proguard.analysis.datastructure.callgraph.Call Maven / Gradle / Ivy

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2021 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package proguard.analysis.datastructure.callgraph;

import java.util.List;
import java.util.Objects;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.analysis.CallResolver;
import proguard.analysis.CallVisitor;
import proguard.analysis.datastructure.CodeLocation;
import proguard.classfile.Method;
import proguard.classfile.MethodSignature;
import proguard.classfile.instruction.Instruction;
import proguard.evaluation.value.IdentifiedReferenceValue;
import proguard.evaluation.value.Value;

/**
 * Represents a method call. If the call target is a {@link Method} that is present
 * in the class pool, a {@link ConcreteCall} is instantiated. If the call target
 * is not a known method, a {@link SymbolicCall} is the appropriate subclass.
 *
 * @author Samuel Hopstock
 */
public abstract class Call
{

    private static final transient Logger       log = LogManager.getLogger(Call.class);
    /**
     * The location where the call was invoked.
     */
    public final                   CodeLocation caller;
    /**
     * Describes whether this call will throw a {@link NullPointerException} at runtime. Either {@link Value#NEVER}, {@link Value#MAYBE} or {@link Value#ALWAYS}.
     */
    public final                   int          throwsNullptr;
    /**
     * The type of this call. There are several different ways of invoking
     * a method call in the JVM:
     * 
    *
  • {@link Instruction#OP_INVOKESTATIC}
  • *
  • {@link Instruction#OP_INVOKEVIRTUAL}
  • *
  • {@link Instruction#OP_INVOKEINTERFACE}
  • *
  • {@link Instruction#OP_INVOKESPECIAL}
  • *
  • {@link Instruction#OP_INVOKEDYNAMIC}
  • *
* See the {@link CallResolver} for more details. */ public final byte invocationOpcode; /** * If false, control flow in the calling method will always reach this call. * Otherwise, whether the call will be invoked at runtime might depend on * e.g. specific branches being taken. */ public final boolean controlFlowDependent; /** * If true, this call might only be one of several alternative targets, * depending on the actual type of the called object during runtime. * Otherwise, this call is the only possible target. */ public final boolean runtimeTypeDependent; private Value instance; private List arguments; private Value returnValue; private boolean valuesCleared; protected Call(CodeLocation caller, Value instance, List arguments, Value returnValue, int throwsNullptr, byte invocationOpcode, boolean controlFlowDependent, boolean runtimeTypeDependent) { this.caller = caller; this.instance = instance; this.arguments = arguments; this.returnValue = returnValue; this.throwsNullptr = throwsNullptr; this.invocationOpcode = invocationOpcode; this.controlFlowDependent = controlFlowDependent; this.runtimeTypeDependent = runtimeTypeDependent; } /** * The {@link MethodSignature} of the method that is being called. */ public abstract MethodSignature getTarget(); /** * Returns the number of arguments. * *

Note: This is only to be used in implementations of {@link CallVisitor#visitCall(Call)}. * Afterwards, the values will have been cleared to reduce unnecessary memory usage, as argument * values are not needed for the full call graph reconstruction.

*/ public int getArgumentCount() { return arguments.size(); } /** * Get the value for a specific argument index. * *

Note: This is only to be used in implementations of {@link CallVisitor#visitCall(Call)}. * Afterwards, the values will have been cleared to reduce unnecessary memory usage, as argument * values are not needed for the full call graph reconstruction.

*/ public Value getArgument(int index) { if (valuesCleared) { log.error("Argument requested after values have been cleared!"); return null; } if (arguments.size() <= index) { return null; } return arguments.get(index); } public void setArguments(List arguments) { this.arguments = arguments; } /** * If this is a virtual call, this describes the this pointer * of the object whose method is called, usually an {@link IdentifiedReferenceValue}. * For static calls this is null. * *

Note: This is only to be used in implementations of {@link CallVisitor#visitCall(Call)}. * Afterwards, the value will have been cleared to reduce unnecessary memory usage, as the instance * value is not needed for the full call graph reconstruction.

*/ public Value getInstance() { if (valuesCleared) { log.error("Instance requested after values have been cleared!"); return null; } return instance; } public void setInstance(Value instance) { this.instance = instance; } /** * Get the return value of this call. * *

Note: This is only to be used in implementations of {@link CallVisitor#visitCall(Call)}. * Afterwards, the value will have been cleared to reduce unnecessary memory usage, as the return * value is not needed for the full call graph reconstruction.

*/ public Value getReturnValue() { if (valuesCleared) { log.error("Return value requested after values have been cleared!"); return null; } return returnValue; } public void setReturnValue(Value returnValue) { this.returnValue = returnValue; } /** * Clear all {@link Value} object references from this call. */ public void clearValues() { arguments.clear(); returnValue = null; instance = null; valuesCleared = true; } /** * Returns true if this call is always executed, no matter * which branch in the methods are taken and which type * the called object has during runtime. */ public boolean isCertainlyCalled() { return !controlFlowDependent && !runtimeTypeDependent; } @Override public String toString() { String nullSuffix = ""; if (throwsNullptr == Value.ALWAYS) { nullSuffix = " (always throws NullPointerException)"; } else if (throwsNullptr == Value.MAYBE) { nullSuffix = " (might throw NullPointerException)"; } String invocationType; switch (invocationOpcode) { case Instruction.OP_INVOKEVIRTUAL: invocationType = "[invokevirtual] "; break; case Instruction.OP_INVOKEINTERFACE: invocationType = "[invokeinterface] "; break; case Instruction.OP_INVOKESTATIC: invocationType = "[invokestatic] "; break; case Instruction.OP_INVOKEDYNAMIC: invocationType = "[invokedynamic] "; break; case Instruction.OP_INVOKESPECIAL: invocationType = "[invokespecial] "; break; default: invocationType = "[unknown call type] "; } return invocationType + caller + " -> " + getTarget() + nullSuffix; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Call that = (Call) o; return throwsNullptr == that.throwsNullptr && Objects.equals(caller, that.caller) && Objects.equals(arguments, that.arguments) && Objects.equals(invocationOpcode, that.invocationOpcode); } @Override public int hashCode() { return Objects.hash(caller, throwsNullptr); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy