blob: 40a730190d2e9d7905932662fabdebbb111f6b01 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* 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 com.intellij.codeInspection.bytecodeAnalysis.asm;
import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph.Edge;
import gnu.trove.TIntArrayList;
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException;
import java.util.HashSet;
import java.util.Set;
/**
* @author lambdamix
*/
public final class ControlFlowGraph {
public static final class Edge {
public final int from, to;
public Edge(int from, int to) {
this.from = from;
this.to = to;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Edge)) {
return false;
}
Edge edge = (Edge) o;
return from == edge.from && to == edge.to;
}
@Override
public int hashCode() {
return 31 * from + to;
}
}
public final String className;
public final MethodNode methodNode;
public final int[][] transitions;
public final int edgeCount;
public final boolean[] errors;
public final Set<Edge> errorTransitions;
ControlFlowGraph(String className, MethodNode methodNode, int[][] transitions, int edgeCount, boolean[] errors, Set<Edge> errorTransitions) {
this.className = className;
this.methodNode = methodNode;
this.transitions = transitions;
this.edgeCount = edgeCount;
this.errors = errors;
this.errorTransitions = errorTransitions;
}
public static ControlFlowGraph build(String className, MethodNode methodNode, boolean jsr) throws AnalyzerException {
return jsr ? new ControlFlowBuilder(className, methodNode).buildCFG() : new LiteControlFlowBuilder(className, methodNode).buildCFG();
}
}
final class ControlFlowBuilder extends FramelessAnalyzer {
final String className;
final MethodNode methodNode;
final TIntArrayList[] transitions;
final Set<ControlFlowGraph.Edge> errorTransitions;
private final boolean[] errors;
private int edgeCount;
ControlFlowBuilder(String className, MethodNode methodNode) {
this.className = className;
this.methodNode = methodNode;
transitions = new TIntArrayList[methodNode.instructions.size()];
errors = new boolean[methodNode.instructions.size()];
for (int i = 0; i < transitions.length; i++) {
transitions[i] = new TIntArrayList();
}
errorTransitions = new HashSet<Edge>();
}
final ControlFlowGraph buildCFG() throws AnalyzerException {
if ((methodNode.access & (ACC_ABSTRACT | ACC_NATIVE)) == 0) {
analyze(methodNode);
}
int[][] resultTransitions = new int[transitions.length][];
for (int i = 0; i < resultTransitions.length; i++) {
resultTransitions[i] = transitions[i].toNativeArray();
}
return new ControlFlowGraph(className, methodNode, resultTransitions, edgeCount, errors, errorTransitions);
}
@Override
protected final void newControlFlowEdge(int insn, int successor) {
if (!transitions[insn].contains(successor)) {
transitions[insn].add(successor);
edgeCount++;
}
}
@Override
protected final boolean newControlFlowExceptionEdge(int insn, int successor) {
if (!transitions[insn].contains(successor)) {
transitions[insn].add(successor);
edgeCount++;
errorTransitions.add(new Edge(insn, successor));
errors[successor] = true;
}
return true;
}
}
final class LiteControlFlowBuilder extends LiteFramelessAnalyzer {
final String className;
final MethodNode methodNode;
final TIntArrayList[] transitions;
final Set<ControlFlowGraph.Edge> errorTransitions;
private final boolean[] errors;
private int edgeCount;
LiteControlFlowBuilder(String className, MethodNode methodNode) {
this.className = className;
this.methodNode = methodNode;
transitions = new TIntArrayList[methodNode.instructions.size()];
errors = new boolean[methodNode.instructions.size()];
for (int i = 0; i < transitions.length; i++) {
transitions[i] = new TIntArrayList();
}
errorTransitions = new HashSet<Edge>();
}
final ControlFlowGraph buildCFG() throws AnalyzerException {
if ((methodNode.access & (ACC_ABSTRACT | ACC_NATIVE)) == 0) {
analyze(methodNode);
}
int[][] resultTransitions = new int[transitions.length][];
for (int i = 0; i < resultTransitions.length; i++) {
resultTransitions[i] = transitions[i].toNativeArray();
}
return new ControlFlowGraph(className, methodNode, resultTransitions, edgeCount, errors, errorTransitions);
}
@Override
protected final void newControlFlowEdge(int insn, int successor) {
if (!transitions[insn].contains(successor)) {
transitions[insn].add(successor);
edgeCount++;
}
}
@Override
protected final boolean newControlFlowExceptionEdge(int insn, int successor) {
if (!transitions[insn].contains(successor)) {
transitions[insn].add(successor);
edgeCount++;
errorTransitions.add(new Edge(insn, successor));
errors[successor] = true;
}
return true;
}
}