| /* |
| * 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; |
| } |
| } |
| |