summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java')
-rw-r--r--java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java222
1 files changed, 222 insertions, 0 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java
new file mode 100644
index 000000000000..06dd39ffab96
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LiteAnalyzerExt.java
@@ -0,0 +1,222 @@
+/*
+ * 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 org.jetbrains.org.objectweb.asm.Opcodes;
+import org.jetbrains.org.objectweb.asm.Type;
+import org.jetbrains.org.objectweb.asm.tree.*;
+import org.jetbrains.org.objectweb.asm.tree.analysis.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Extended version of {@link com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzer}.
+ * It handles frames <b>and</b> additional data.
+ *
+ * @author lambdamix
+ */
+public class LiteAnalyzerExt<V extends Value, Data, MyInterpreter extends Interpreter<V> & InterpreterExt<Data>> implements Opcodes {
+
+ private final MyInterpreter interpreter;
+ private Frame<V>[] frames;
+ private boolean[] queued;
+ private int[] queue;
+ private int top;
+
+ public Data[] getData() {
+ return data;
+ }
+
+ private Data[] data;
+
+ public LiteAnalyzerExt(final MyInterpreter interpreter, Data[] data, Data startData) {
+ this.interpreter = interpreter;
+ this.data = data;
+ if (data.length > 0) {
+ data[0] = startData;
+ }
+ }
+
+ public Frame<V>[] analyze(final String owner, final MethodNode m) throws AnalyzerException {
+ if ((m.access & (ACC_ABSTRACT | ACC_NATIVE)) != 0) {
+ frames = (Frame<V>[]) new Frame<?>[0];
+ return frames;
+ }
+
+ final V refV = (V) BasicValue.REFERENCE_VALUE;
+
+ int n = m.instructions.size();
+ InsnList insns = m.instructions;
+ List<TryCatchBlockNode>[] handlers = (List<TryCatchBlockNode>[]) new List<?>[n];
+ frames = (Frame<V>[]) new Frame<?>[n];
+ queued = new boolean[n];
+ queue = new int[n];
+ top = 0;
+
+ // computes exception handlers for each instruction
+ for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
+ TryCatchBlockNode tcb = m.tryCatchBlocks.get(i);
+ int begin = insns.indexOf(tcb.start);
+ int end = insns.indexOf(tcb.end);
+ for (int j = begin; j < end; ++j) {
+ List<TryCatchBlockNode> insnHandlers = handlers[j];
+ if (insnHandlers == null) {
+ insnHandlers = new ArrayList<TryCatchBlockNode>();
+ handlers[j] = insnHandlers;
+ }
+ insnHandlers.add(tcb);
+ }
+ }
+
+ // initializes the data structures for the control flow analysis
+ Frame<V> current = newFrame(m.maxLocals, m.maxStack);
+ Frame<V> handler = newFrame(m.maxLocals, m.maxStack);
+ current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)));
+ Type[] args = Type.getArgumentTypes(m.desc);
+ int local = 0;
+ if ((m.access & ACC_STATIC) == 0) {
+ Type ctype = Type.getObjectType(owner);
+ current.setLocal(local++, interpreter.newValue(ctype));
+ }
+ for (int i = 0; i < args.length; ++i) {
+ current.setLocal(local++, interpreter.newValue(args[i]));
+ if (args[i].getSize() == 2) {
+ current.setLocal(local++, interpreter.newValue(null));
+ }
+ }
+ while (local < m.maxLocals) {
+ current.setLocal(local++, interpreter.newValue(null));
+ }
+
+ interpreter.init(data[0]);
+ merge(0, current);
+
+ // control flow analysis
+ while (top > 0) {
+ int insn = queue[--top];
+ Frame<V> f = frames[insn];
+ queued[insn] = false;
+
+ AbstractInsnNode insnNode = null;
+ try {
+ insnNode = m.instructions.get(insn);
+ int insnOpcode = insnNode.getOpcode();
+ int insnType = insnNode.getType();
+
+ if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
+ interpreter.init(data[insn]);
+ merge(insn + 1, f);
+ } else {
+ // delta
+ interpreter.init(data[insn]);
+ current.init(f).execute(insnNode, interpreter);
+
+ if (insnNode instanceof JumpInsnNode) {
+ JumpInsnNode j = (JumpInsnNode) insnNode;
+ if (insnOpcode != GOTO && insnOpcode != JSR) {
+ merge(insn + 1, current);
+ }
+ int jump = insns.indexOf(j.label);
+ merge(jump, current);
+ } else if (insnNode instanceof LookupSwitchInsnNode) {
+ LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
+ int jump = insns.indexOf(lsi.dflt);
+ merge(jump, current);
+ for (int j = 0; j < lsi.labels.size(); ++j) {
+ LabelNode label = lsi.labels.get(j);
+ jump = insns.indexOf(label);
+ merge(jump, current);
+ }
+ } else if (insnNode instanceof TableSwitchInsnNode) {
+ TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
+ int jump = insns.indexOf(tsi.dflt);
+ merge(jump, current);
+ for (int j = 0; j < tsi.labels.size(); ++j) {
+ LabelNode label = tsi.labels.get(j);
+ jump = insns.indexOf(label);
+ merge(jump, current);
+ }
+ } else if (insnOpcode != ATHROW
+ && (insnOpcode < IRETURN || insnOpcode > RETURN)) {
+ merge(insn + 1, current);
+ }
+ }
+
+ List<TryCatchBlockNode> insnHandlers = handlers[insn];
+ if (insnHandlers != null) {
+ for (int i = 0; i < insnHandlers.size(); ++i) {
+ TryCatchBlockNode tcb = insnHandlers.get(i);
+ int jump = insns.indexOf(tcb.handler);
+ handler.init(f);
+ handler.clearStack();
+ handler.push(refV);
+ merge(jump, handler);
+ }
+ }
+ } catch (AnalyzerException e) {
+ throw new AnalyzerException(e.node, "Error at instruction " + insn + ": " + e.getMessage(), e);
+ } catch (Exception e) {
+ throw new AnalyzerException(insnNode, "Error at instruction " + insn + ": " + e.getMessage(), e);
+ }
+ }
+
+ return frames;
+ }
+
+ public Frame<V>[] getFrames() {
+ return frames;
+ }
+
+ protected Frame<V> newFrame(final int nLocals, final int nStack) {
+ return new Frame<V>(nLocals, nStack);
+ }
+
+ protected Frame<V> newFrame(final Frame<? extends V> src) {
+ return new Frame<V>(src);
+ }
+
+ // -------------------------------------------------------------------------
+
+ private void merge(final int insn, final Frame<V> frame) throws AnalyzerException {
+ Frame<V> oldFrame = frames[insn];
+ boolean changes;
+
+ if (oldFrame == null) {
+ frames[insn] = newFrame(frame);
+ changes = true;
+ } else {
+ changes = oldFrame.merge(frame, interpreter);
+ }
+
+ Data oldData = data[insn];
+ Data newData = interpreter.getAfterData(insn);
+
+ if (oldData == null) {
+ data[insn] = newData;
+ changes = true;
+ } else if (newData != null) {
+ Data mergedData = interpreter.merge(oldData, newData);
+ data[insn] = mergedData;
+ changes |= !oldData.equals(mergedData);
+ }
+
+ if (changes && !queued[insn]) {
+ queued[insn] = true;
+ queue[top++] = insn;
+ }
+ }
+}