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

proguard.analysis.cpa.jvm.cfa.JvmCfa 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-2022 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.cpa.jvm.cfa;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import proguard.analysis.cpa.defaults.Cfa;
import proguard.analysis.cpa.interfaces.CfaNode;
import proguard.analysis.cpa.jvm.cfa.edges.JvmCallCfaEdge;
import proguard.analysis.cpa.jvm.cfa.edges.JvmCfaEdge;
import proguard.analysis.cpa.jvm.cfa.nodes.JvmCatchCfaNode;
import proguard.analysis.cpa.jvm.cfa.nodes.JvmCfaNode;
import proguard.analysis.cpa.jvm.cfa.nodes.JvmUnknownCfaNode;
import proguard.analysis.datastructure.callgraph.Call;
import proguard.analysis.datastructure.callgraph.ConcreteCall;
import proguard.classfile.Clazz;
import proguard.classfile.MethodSignature;

/**
 * A JVM specific implementation of {@link Cfa}.
 *
 * 

The keys of the function maps are the {@link MethodSignature}s. * *

The nodes of a function are identified by the offset of the code location. Besides the normal * exit node for return instructions, function can also have a special exit node for uncaught * exceptions. * *

An additional {@link JvmCatchCfaNode} is added for each handler in the method exception table. * *

A unique {@link JvmUnknownCfaNode} node is used for instructions the successor of which is * unknown. * * @author Carlo Alberto Pozzoli */ public class JvmCfa extends Cfa { private final Map> functionCatchNodes = new HashMap<>(); @Override public Stream getAllNodes() { return Stream.of(functionNodes.values(), functionCatchNodes.values()) .flatMap(Collection::stream) .flatMap(it -> it.values().stream()); } /** * Returns all the catch nodes of a specific method, returns an empty collection if the function * is not in the graph or if it has no catch nodes. * * @param signature The signature of the method. */ public Collection getFunctionCatchNodes(MethodSignature signature) { return functionCatchNodes.getOrDefault(signature, Collections.emptyMap()).values(); } /** * Returns the catch node of a method the handler of which begins at the specific code offset, * returns null if the method or the specific catch node are not in the graph. * * @param signature The signature of the method, might be different for different domains. * @param offset The offset of the catch handler represented by the node. */ public JvmCatchCfaNode getFunctionCatchNode(MethodSignature signature, int offset) { return functionCatchNodes .getOrDefault(signature, Collections.emptyMap()) .getOrDefault(offset, null); } /** * Adds a catch node to the CFA (i.e. a node indicating the beginning of an exception handler). */ public void addFunctionCatchNode(MethodSignature signature, JvmCatchCfaNode node, int offset) { functionCatchNodes.computeIfAbsent(signature, x -> new HashMap<>()).put(offset, node); } /** * Returns true if the catch node of the specified method at the specified offset is present in * the graph. */ public boolean containsFunctionCatchNode(MethodSignature signature, int offset) { return functionCatchNodes.containsKey(signature) && functionCatchNodes.get(signature).containsKey(offset); } /** * Returns the exit node of the specified method if present, otherwise creates the exit node for * the method and returns it. */ public JvmCfaNode getFunctionReturnExitNode(MethodSignature signature, Clazz clazz) { // get the return exit location node JvmCfaNode exitNode = getFunctionNode(signature, CfaNode.RETURN_EXIT_NODE_OFFSET); if (exitNode == null) { exitNode = new JvmCfaNode(signature, CfaNode.RETURN_EXIT_NODE_OFFSET, clazz); addFunctionNode(signature, exitNode, CfaNode.RETURN_EXIT_NODE_OFFSET); } return exitNode; } /** * Returns the exception exit node (i.e. the exit node in case of not caught exception) of the * specified method if present, otherwise creates the exit node for the method and returns it. */ public JvmCfaNode getFunctionExceptionExitNode(MethodSignature signature, Clazz clazz) { // get the exit location node JvmCfaNode exitNode = getFunctionNode(signature, CfaNode.EXCEPTION_EXIT_NODE_OFFSET); if (exitNode == null) { exitNode = new JvmCfaNode(signature, CfaNode.EXCEPTION_EXIT_NODE_OFFSET, clazz); addFunctionNode(signature, exitNode, CfaNode.EXCEPTION_EXIT_NODE_OFFSET); } return exitNode; } /** * If the requested function node is present in the graph return it. If the node is not present * add it to the graph and return the new node. */ public JvmCfaNode addNodeIfAbsent(MethodSignature signature, int offset, Clazz clazz) { // if the location at the current offset is already in the CFA (e.g. created by a branch or // goto) retrieve it, otherwise create a new node JvmCfaNode node = getFunctionNode(signature, offset); if (node == null) { node = new JvmCfaNode(signature, offset, clazz); // add the new node to the CFA if (offset == 0) { // first node of the functions' graph addFunctionEntryNode(signature, node); } else { addFunctionNode(signature, node, offset); } } return node; } /** Adds a call node between two methods. */ public void addInterproceduralEdge(Call call) { JvmCfaNode callNode = addNodeIfAbsent( ((MethodSignature) call.caller.signature), call.caller.offset, call.caller.clazz); JvmCfaNode calledNode = addNodeIfAbsent( call.getTarget(), 0, call instanceof ConcreteCall ? ((ConcreteCall) call).getTargetClass() : null); new JvmCallCfaEdge(callNode, calledNode, call); } /** * Adds a call node between two methods. This is used when the target method has no {@link * proguard.classfile.attribute.CodeAttribute}, which means the target method is not present in * the CFA. The unknown node is set as the call target. */ public void addUnknownTargetInterproceduralEdge(Call call) { JvmCfaNode callNode = addNodeIfAbsent( ((MethodSignature) call.caller.signature), call.caller.offset, call.caller.clazz); JvmCfaNode calledNode = JvmUnknownCfaNode.INSTANCE; new JvmCallCfaEdge(callNode, calledNode, call); } /** * Removes references to this CFA nodes from the singleton {@link JvmUnknownCfaNode} and clears * its node collections making it garbage collectable. */ public void clear() { List unknownEnteringEdges = JvmUnknownCfaNode.INSTANCE.getEnteringEdges(); if (!unknownEnteringEdges.isEmpty()) { Set edges = getAllNodes() .flatMap(it -> it.getLeavingEdges().stream()) .filter(e -> e.getTarget() == JvmUnknownCfaNode.INSTANCE) .collect(Collectors.toSet()); unknownEnteringEdges.removeAll(edges); } functionCatchNodes.clear(); functionNodes.clear(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy