From 559fbf097f5339e6722bac5337b1cd95703e3c9f Mon Sep 17 00:00:00 2001 From: Ilya Klyuchnikov Date: Wed, 13 Aug 2014 13:18:00 +0400 Subject: [PATCH] refactoring: moving LeakingParametersAnalysis into asm package --- .../bytecodeAnalysis/ClassDataIndexer.java | 5 +- .../bytecodeAnalysis/ControlFlow.java | 543 +---------------- .../asm/LeakingParametersAnalysis.java | 563 ++++++++++++++++++ .../BytecodeAnalysisTest.java | 3 +- 4 files changed, 571 insertions(+), 543 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParametersAnalysis.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java index 40531d67c8c4..1043fa8e95e1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ClassDataIndexer.java @@ -17,6 +17,7 @@ package com.intellij.codeInspection.bytecodeAnalysis; import com.intellij.codeInspection.bytecodeAnalysis.asm.ASMUtils; import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph; +import com.intellij.codeInspection.bytecodeAnalysis.asm.LeakingParametersAnalysis; import com.intellij.codeInspection.bytecodeAnalysis.asm.OriginsAnalysis; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressManager; @@ -340,7 +341,9 @@ public class ClassDataIndexer implements DataIndexer MethodNode methodNode, Type[] argumentTypes) throws AnalyzerException { - return (argumentTypes.length < 32 ? cfg.fastLeakingParameters(method.internalClassName, methodNode) : cfg.leakingParameters(method.internalClassName, methodNode)); + return argumentTypes.length < 32 ? + LeakingParametersAnalysis.fastLeakingParameters(method.internalClassName, methodNode) : + LeakingParametersAnalysis.leakingParameters(method.internalClassName, methodNode); } }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ControlFlow.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ControlFlow.java index 57a8f8626b37..ccc930b97cb1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ControlFlow.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ControlFlow.java @@ -15,66 +15,23 @@ */ package com.intellij.codeInspection.bytecodeAnalysis; -import com.intellij.codeInspection.bytecodeAnalysis.asm.ASMUtils; import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph; import com.intellij.codeInspection.bytecodeAnalysis.asm.ControlFlowGraph.Edge; import com.intellij.codeInspection.bytecodeAnalysis.asm.FramelessAnalyzer; -import com.intellij.openapi.util.Pair; import gnu.trove.TIntArrayList; import gnu.trove.TIntHashSet; import gnu.trove.TIntIterator; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.org.objectweb.asm.Type; -import org.jetbrains.org.objectweb.asm.tree.*; -import org.jetbrains.org.objectweb.asm.tree.analysis.*; -import org.jetbrains.org.objectweb.asm.tree.analysis.Value; +import org.jetbrains.org.objectweb.asm.tree.MethodNode; +import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException; -import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; -import static org.jetbrains.org.objectweb.asm.Opcodes.*; - final class cfg { static ControlFlowGraph buildControlFlowGraph(String className, MethodNode methodNode) throws AnalyzerException { return new ControlFlowBuilder(className, methodNode).buildCFG(); } - static Pair[]> leakingParameters(String className, MethodNode methodNode) throws AnalyzerException { - Frame[] frames = new Analyzer(new ParametersUsage(methodNode)).analyze(className, methodNode); - InsnList insns = methodNode.instructions; - LeakingParametersCollector collector = new LeakingParametersCollector(methodNode); - for (int i = 0; i < frames.length; i++) { - AbstractInsnNode insnNode = insns.get(i); - Frame frame = frames[i]; - if (frame != null) { - switch (insnNode.getType()) { - case AbstractInsnNode.LABEL: - case AbstractInsnNode.LINE: - case AbstractInsnNode.FRAME: - break; - default: - new Frame(frame).execute(insnNode, collector); - } - } - } - // to hack type checker - Frame[] frames1 = frames; - return new Pair[]>(collector.leaking, (Frame[]) frames1); - } - - static Pair[]> fastLeakingParameters(String className, MethodNode methodNode) throws AnalyzerException { - IParametersUsage parametersUsage = new IParametersUsage(methodNode); - Frame[] frames = new Analyzer(parametersUsage).analyze(className, methodNode); - int leakingMask = parametersUsage.leaking; - boolean[] result = new boolean[parametersUsage.arity]; - for (int i = 0; i < result.length; i++) { - result[i] = (leakingMask & (1 << i)) != 0; - } - return new Pair[]>(result, (Frame[]) frames); - } - // Graphs: Theory and Algorithms. by K. Thulasiraman , M. N. S. Swamy (1992) // 11.7.2 DFS of a directed graph static DFSTree buildDFSTree(int[][] transitions, int edgeCount) { @@ -310,499 +267,3 @@ final class DFSTree { } } -final class ParamsValue implements Value { - @NotNull final boolean[] params; - final int size; - - ParamsValue(@NotNull boolean[] params, int size) { - this.params = params; - this.size = size; - } - - @Override - public int getSize() { - return size; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null) return false; - ParamsValue that = (ParamsValue)o; - return (this.size == that.size && Arrays.equals(this.params, that.params)); - } - - @Override - public int hashCode() { - return 31 * Arrays.hashCode(params) + size; - } -} - -// specialized version -final class IParamsValue implements Value { - final int params; - final int size; - - IParamsValue(int params, int size) { - this.params = params; - this.size = size; - } - - @Override - public int getSize() { - return size; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null) return false; - IParamsValue that = (IParamsValue)o; - return (this.size == that.size && this.params == that.params); - } - - @Override - public int hashCode() { - return 31 * params + size; - } -} - -class ParametersUsage extends Interpreter { - final ParamsValue val1; - final ParamsValue val2; - int called = -1; - final int rangeStart; - final int rangeEnd; - final int arity; - final int shift; - - ParametersUsage(MethodNode methodNode) { - super(ASM5); - arity = Type.getArgumentTypes(methodNode.desc).length; - boolean[] emptyParams = new boolean[arity]; - val1 = new ParamsValue(emptyParams, 1); - val2 = new ParamsValue(emptyParams, 2); - - shift = (methodNode.access & ACC_STATIC) == 0 ? 2 : 1; - rangeStart = shift; - rangeEnd = arity + shift; - } - - @Override - public ParamsValue newValue(Type type) { - if (type == null) return val1; - called++; - if (type == Type.VOID_TYPE) return null; - if (called < rangeEnd && rangeStart <= called && (ASMUtils.isReferenceType(type) || ASMUtils.isBooleanType(type))) { - boolean[] params = new boolean[arity]; - params[called - shift] = true; - return type.getSize() == 1 ? new ParamsValue(params, 1) : new ParamsValue(params, 2); - } - else { - return type.getSize() == 1 ? val1 : val2; - } - } - - @Override - public ParamsValue newOperation(final AbstractInsnNode insn) { - int size; - switch (insn.getOpcode()) { - case LCONST_0: - case LCONST_1: - case DCONST_0: - case DCONST_1: - size = 2; - break; - case LDC: - Object cst = ((LdcInsnNode) insn).cst; - size = cst instanceof Long || cst instanceof Double ? 2 : 1; - break; - case GETSTATIC: - size = Type.getType(((FieldInsnNode) insn).desc).getSize(); - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public ParamsValue copyOperation(AbstractInsnNode insn, ParamsValue value) { - return value; - } - - @Override - public ParamsValue unaryOperation(AbstractInsnNode insn, ParamsValue value) { - int size; - switch (insn.getOpcode()) { - case CHECKCAST: - return value; - case LNEG: - case DNEG: - case I2L: - case I2D: - case L2D: - case F2L: - case F2D: - case D2L: - size = 2; - break; - case GETFIELD: - size = Type.getType(((FieldInsnNode) insn).desc).getSize(); - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public ParamsValue binaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2) { - int size; - switch (insn.getOpcode()) { - case LALOAD: - case DALOAD: - case LADD: - case DADD: - case LSUB: - case DSUB: - case LMUL: - case DMUL: - case LDIV: - case DDIV: - case LREM: - case DREM: - case LSHL: - case LSHR: - case LUSHR: - case LAND: - case LOR: - case LXOR: - size = 2; - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { - return val1; - } - - @Override - public ParamsValue naryOperation(AbstractInsnNode insn, List values) { - int size; - int opcode = insn.getOpcode(); - if (opcode == MULTIANEWARRAY) { - size = 1; - } else { - String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc : ((MethodInsnNode) insn).desc; - size = Type.getReturnType(desc).getSize(); - } - return size == 1 ? val1 : val2; - } - - @Override - public void returnOperation(AbstractInsnNode insn, ParamsValue value, ParamsValue expected) {} - - @Override - public ParamsValue merge(ParamsValue v1, ParamsValue v2) { - if (v1.equals(v2)) return v1; - boolean[] params = new boolean[arity]; - boolean[] params1 = v1.params; - boolean[] params2 = v2.params; - for (int i = 0; i < arity; i++) { - params[i] = params1[i] || params2[i]; - } - return new ParamsValue(params, Math.min(v1.size, v2.size)); - } -} - -class IParametersUsage extends Interpreter { - static final IParamsValue val1 = new IParamsValue(0, 1); - static final IParamsValue val2 = new IParamsValue(0, 2); - int leaking = 0; - int called = -1; - final int rangeStart; - final int rangeEnd; - final int arity; - final int shift; - - IParametersUsage(MethodNode methodNode) { - super(ASM5); - arity = Type.getArgumentTypes(methodNode.desc).length; - shift = (methodNode.access & ACC_STATIC) == 0 ? 2 : 1; - rangeStart = shift; - rangeEnd = arity + shift; - } - - @Override - public IParamsValue newValue(Type type) { - if (type == null) return val1; - called++; - if (type == Type.VOID_TYPE) return null; - if (called < rangeEnd && rangeStart <= called && (ASMUtils.isReferenceType(type) || ASMUtils.isBooleanType(type))) { - int n = called - shift; - return type.getSize() == 1 ? new IParamsValue(1 << n, 1) : new IParamsValue(1 << n, 2); - } - else { - return type.getSize() == 1 ? val1 : val2; - } - } - - @Override - public IParamsValue newOperation(final AbstractInsnNode insn) { - int size; - switch (insn.getOpcode()) { - case LCONST_0: - case LCONST_1: - case DCONST_0: - case DCONST_1: - size = 2; - break; - case LDC: - Object cst = ((LdcInsnNode) insn).cst; - size = cst instanceof Long || cst instanceof Double ? 2 : 1; - break; - case GETSTATIC: - size = ASMUtils.getSizeFast(((FieldInsnNode)insn).desc); - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public IParamsValue copyOperation(AbstractInsnNode insn, IParamsValue value) { - return value; - } - - @Override - public IParamsValue unaryOperation(AbstractInsnNode insn, IParamsValue value) { - int size; - switch (insn.getOpcode()) { - case CHECKCAST: - return value; - case LNEG: - case DNEG: - case I2L: - case I2D: - case L2D: - case F2L: - case F2D: - case D2L: - size = 2; - break; - case GETFIELD: - size = ASMUtils.getSizeFast(((FieldInsnNode)insn).desc); - leaking |= value.params; - break; - case ARRAYLENGTH: - case MONITORENTER: - case INSTANCEOF: - case IRETURN: - case ARETURN: - case IFNONNULL: - case IFNULL: - case IFEQ: - case IFNE: - size = 1; - leaking |= value.params; - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public IParamsValue binaryOperation(AbstractInsnNode insn, IParamsValue value1, IParamsValue value2) { - int size; - switch (insn.getOpcode()) { - case LALOAD: - case DALOAD: - size = 2; - leaking |= value1.params; - break; - case LADD: - case DADD: - case LSUB: - case DSUB: - case LMUL: - case DMUL: - case LDIV: - case DDIV: - case LREM: - case DREM: - case LSHL: - case LSHR: - case LUSHR: - case LAND: - case LOR: - case LXOR: - size = 2; - break; - case IALOAD: - case FALOAD: - case AALOAD: - case BALOAD: - case CALOAD: - case SALOAD: - case PUTFIELD: - leaking |= value1.params; - size = 1; - break; - default: - size = 1; - } - return size == 1 ? val1 : val2; - } - - @Override - public IParamsValue ternaryOperation(AbstractInsnNode insn, IParamsValue value1, IParamsValue value2, IParamsValue value3) { - switch (insn.getOpcode()) { - case IASTORE: - case LASTORE: - case FASTORE: - case DASTORE: - case AASTORE: - case BASTORE: - case CASTORE: - case SASTORE: - leaking |= value1.params; - break; - default: - } - return val1; - } - - @Override - public IParamsValue naryOperation(AbstractInsnNode insn, List values) { - int opcode = insn.getOpcode(); - switch (opcode) { - case INVOKESTATIC: - case INVOKESPECIAL: - case INVOKEVIRTUAL: - case INVOKEINTERFACE: - for (IParamsValue value : values) { - leaking |= value.params; - } - break; - default: - } - int size; - if (opcode == MULTIANEWARRAY) { - size = 1; - } else { - String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc : ((MethodInsnNode) insn).desc; - size = ASMUtils.getReturnSizeFast(desc); - } - return size == 1 ? val1 : val2; - } - - @Override - public void returnOperation(AbstractInsnNode insn, IParamsValue value, IParamsValue expected) {} - - @Override - public IParamsValue merge(IParamsValue v1, IParamsValue v2) { - if (v1.equals(v2)) return v1; - return new IParamsValue(v1.params | v2.params, Math.min(v1.size, v2.size)); - } -} - -class LeakingParametersCollector extends ParametersUsage { - final boolean[] leaking; - LeakingParametersCollector(MethodNode methodNode) { - super(methodNode); - leaking = new boolean[arity]; - } - - @Override - public ParamsValue unaryOperation(AbstractInsnNode insn, ParamsValue value) { - switch (insn.getOpcode()) { - case GETFIELD: - case ARRAYLENGTH: - case MONITORENTER: - case INSTANCEOF: - case IRETURN: - case ARETURN: - case IFNONNULL: - case IFNULL: - case IFEQ: - case IFNE: - boolean[] params = value.params; - for (int i = 0; i < arity; i++) { - leaking[i] |= params[i]; - } - break; - default: - } - return super.unaryOperation(insn, value); - } - - @Override - public ParamsValue binaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2) { - switch (insn.getOpcode()) { - case IALOAD: - case LALOAD: - case FALOAD: - case DALOAD: - case AALOAD: - case BALOAD: - case CALOAD: - case SALOAD: - case PUTFIELD: - boolean[] params = value1.params; - for (int i = 0; i < arity; i++) { - leaking[i] |= params[i]; - } - break; - default: - } - return super.binaryOperation(insn, value1, value2); - } - - @Override - public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { - switch (insn.getOpcode()) { - case IASTORE: - case LASTORE: - case FASTORE: - case DASTORE: - case AASTORE: - case BASTORE: - case CASTORE: - case SASTORE: - boolean[] params = value1.params; - for (int i = 0; i < arity; i++) { - leaking[i] |= params[i]; - } - break; - default: - } - return super.ternaryOperation(insn, value1, value2, value3); - } - - @Override - public ParamsValue naryOperation(AbstractInsnNode insn, List values) { - switch (insn.getOpcode()) { - case INVOKESTATIC: - case INVOKESPECIAL: - case INVOKEVIRTUAL: - case INVOKEINTERFACE: - for (ParamsValue value : values) { - boolean[] params = value.params; - for (int i = 0; i < arity; i++) { - leaking[i] |= params[i]; - } - } - break; - default: - } - return super.naryOperation(insn, values); - } -} - diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParametersAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParametersAnalysis.java new file mode 100644 index 000000000000..4af6ca0228cb --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParametersAnalysis.java @@ -0,0 +1,563 @@ +/* + * 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.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +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.Arrays; +import java.util.List; + +import static org.jetbrains.org.objectweb.asm.Opcodes.*; + +/** + * @author lambdamix + */ +public class LeakingParametersAnalysis { + public static Pair[]> leakingParameters(String className, MethodNode methodNode) throws AnalyzerException { + Frame[] frames = new Analyzer(new ParametersUsage(methodNode)).analyze(className, methodNode); + InsnList insns = methodNode.instructions; + LeakingParametersCollector collector = new LeakingParametersCollector(methodNode); + for (int i = 0; i < frames.length; i++) { + AbstractInsnNode insnNode = insns.get(i); + Frame frame = frames[i]; + if (frame != null) { + switch (insnNode.getType()) { + case AbstractInsnNode.LABEL: + case AbstractInsnNode.LINE: + case AbstractInsnNode.FRAME: + break; + default: + new Frame(frame).execute(insnNode, collector); + } + } + } + // to hack type checker + Frame[] frames1 = frames; + return new Pair[]>(collector.leaking, (Frame[]) frames1); + } + + public static Pair[]> fastLeakingParameters(String className, MethodNode methodNode) throws AnalyzerException { + IParametersUsage parametersUsage = new IParametersUsage(methodNode); + Frame[] frames = new Analyzer(parametersUsage).analyze(className, methodNode); + int leakingMask = parametersUsage.leaking; + boolean[] result = new boolean[parametersUsage.arity]; + for (int i = 0; i < result.length; i++) { + result[i] = (leakingMask & (1 << i)) != 0; + } + return new Pair[]>(result, (Frame[]) frames); + } +} + +final class ParamsValue implements Value { + @NotNull final boolean[] params; + final int size; + + ParamsValue(@NotNull boolean[] params, int size) { + this.params = params; + this.size = size; + } + + @Override + public int getSize() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + ParamsValue that = (ParamsValue)o; + return (this.size == that.size && Arrays.equals(this.params, that.params)); + } + + @Override + public int hashCode() { + return 31 * Arrays.hashCode(params) + size; + } +} + +// specialized version +final class IParamsValue implements Value { + final int params; + final int size; + + IParamsValue(int params, int size) { + this.params = params; + this.size = size; + } + + @Override + public int getSize() { + return size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null) return false; + IParamsValue that = (IParamsValue)o; + return (this.size == that.size && this.params == that.params); + } + + @Override + public int hashCode() { + return 31 * params + size; + } +} + +class ParametersUsage extends Interpreter { + final ParamsValue val1; + final ParamsValue val2; + int called = -1; + final int rangeStart; + final int rangeEnd; + final int arity; + final int shift; + + ParametersUsage(MethodNode methodNode) { + super(ASM5); + arity = Type.getArgumentTypes(methodNode.desc).length; + boolean[] emptyParams = new boolean[arity]; + val1 = new ParamsValue(emptyParams, 1); + val2 = new ParamsValue(emptyParams, 2); + + shift = (methodNode.access & ACC_STATIC) == 0 ? 2 : 1; + rangeStart = shift; + rangeEnd = arity + shift; + } + + @Override + public ParamsValue newValue(Type type) { + if (type == null) return val1; + called++; + if (type == Type.VOID_TYPE) return null; + if (called < rangeEnd && rangeStart <= called && (ASMUtils.isReferenceType(type) || ASMUtils.isBooleanType(type))) { + boolean[] params = new boolean[arity]; + params[called - shift] = true; + return type.getSize() == 1 ? new ParamsValue(params, 1) : new ParamsValue(params, 2); + } + else { + return type.getSize() == 1 ? val1 : val2; + } + } + + @Override + public ParamsValue newOperation(final AbstractInsnNode insn) { + int size; + switch (insn.getOpcode()) { + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + size = 2; + break; + case LDC: + Object cst = ((LdcInsnNode) insn).cst; + size = cst instanceof Long || cst instanceof Double ? 2 : 1; + break; + case GETSTATIC: + size = Type.getType(((FieldInsnNode) insn).desc).getSize(); + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public ParamsValue copyOperation(AbstractInsnNode insn, ParamsValue value) { + return value; + } + + @Override + public ParamsValue unaryOperation(AbstractInsnNode insn, ParamsValue value) { + int size; + switch (insn.getOpcode()) { + case CHECKCAST: + return value; + case LNEG: + case DNEG: + case I2L: + case I2D: + case L2D: + case F2L: + case F2D: + case D2L: + size = 2; + break; + case GETFIELD: + size = Type.getType(((FieldInsnNode) insn).desc).getSize(); + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public ParamsValue binaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2) { + int size; + switch (insn.getOpcode()) { + case LALOAD: + case DALOAD: + case LADD: + case DADD: + case LSUB: + case DSUB: + case LMUL: + case DMUL: + case LDIV: + case DDIV: + case LREM: + case DREM: + case LSHL: + case LSHR: + case LUSHR: + case LAND: + case LOR: + case LXOR: + size = 2; + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { + return val1; + } + + @Override + public ParamsValue naryOperation(AbstractInsnNode insn, List values) { + int size; + int opcode = insn.getOpcode(); + if (opcode == MULTIANEWARRAY) { + size = 1; + } else { + String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc : ((MethodInsnNode) insn).desc; + size = Type.getReturnType(desc).getSize(); + } + return size == 1 ? val1 : val2; + } + + @Override + public void returnOperation(AbstractInsnNode insn, ParamsValue value, ParamsValue expected) {} + + @Override + public ParamsValue merge(ParamsValue v1, ParamsValue v2) { + if (v1.equals(v2)) return v1; + boolean[] params = new boolean[arity]; + boolean[] params1 = v1.params; + boolean[] params2 = v2.params; + for (int i = 0; i < arity; i++) { + params[i] = params1[i] || params2[i]; + } + return new ParamsValue(params, Math.min(v1.size, v2.size)); + } +} + +class IParametersUsage extends Interpreter { + static final IParamsValue val1 = new IParamsValue(0, 1); + static final IParamsValue val2 = new IParamsValue(0, 2); + int leaking = 0; + int called = -1; + final int rangeStart; + final int rangeEnd; + final int arity; + final int shift; + + IParametersUsage(MethodNode methodNode) { + super(ASM5); + arity = Type.getArgumentTypes(methodNode.desc).length; + shift = (methodNode.access & ACC_STATIC) == 0 ? 2 : 1; + rangeStart = shift; + rangeEnd = arity + shift; + } + + @Override + public IParamsValue newValue(Type type) { + if (type == null) return val1; + called++; + if (type == Type.VOID_TYPE) return null; + if (called < rangeEnd && rangeStart <= called && (ASMUtils.isReferenceType(type) || ASMUtils.isBooleanType(type))) { + int n = called - shift; + return type.getSize() == 1 ? new IParamsValue(1 << n, 1) : new IParamsValue(1 << n, 2); + } + else { + return type.getSize() == 1 ? val1 : val2; + } + } + + @Override + public IParamsValue newOperation(final AbstractInsnNode insn) { + int size; + switch (insn.getOpcode()) { + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + size = 2; + break; + case LDC: + Object cst = ((LdcInsnNode) insn).cst; + size = cst instanceof Long || cst instanceof Double ? 2 : 1; + break; + case GETSTATIC: + size = ASMUtils.getSizeFast(((FieldInsnNode)insn).desc); + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public IParamsValue copyOperation(AbstractInsnNode insn, IParamsValue value) { + return value; + } + + @Override + public IParamsValue unaryOperation(AbstractInsnNode insn, IParamsValue value) { + int size; + switch (insn.getOpcode()) { + case CHECKCAST: + return value; + case LNEG: + case DNEG: + case I2L: + case I2D: + case L2D: + case F2L: + case F2D: + case D2L: + size = 2; + break; + case GETFIELD: + size = ASMUtils.getSizeFast(((FieldInsnNode)insn).desc); + leaking |= value.params; + break; + case ARRAYLENGTH: + case MONITORENTER: + case INSTANCEOF: + case IRETURN: + case ARETURN: + case IFNONNULL: + case IFNULL: + case IFEQ: + case IFNE: + size = 1; + leaking |= value.params; + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public IParamsValue binaryOperation(AbstractInsnNode insn, IParamsValue value1, IParamsValue value2) { + int size; + switch (insn.getOpcode()) { + case LALOAD: + case DALOAD: + size = 2; + leaking |= value1.params; + break; + case LADD: + case DADD: + case LSUB: + case DSUB: + case LMUL: + case DMUL: + case LDIV: + case DDIV: + case LREM: + case DREM: + case LSHL: + case LSHR: + case LUSHR: + case LAND: + case LOR: + case LXOR: + size = 2; + break; + case IALOAD: + case FALOAD: + case AALOAD: + case BALOAD: + case CALOAD: + case SALOAD: + case PUTFIELD: + leaking |= value1.params; + size = 1; + break; + default: + size = 1; + } + return size == 1 ? val1 : val2; + } + + @Override + public IParamsValue ternaryOperation(AbstractInsnNode insn, IParamsValue value1, IParamsValue value2, IParamsValue value3) { + switch (insn.getOpcode()) { + case IASTORE: + case LASTORE: + case FASTORE: + case DASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + leaking |= value1.params; + break; + default: + } + return val1; + } + + @Override + public IParamsValue naryOperation(AbstractInsnNode insn, List values) { + int opcode = insn.getOpcode(); + switch (opcode) { + case INVOKESTATIC: + case INVOKESPECIAL: + case INVOKEVIRTUAL: + case INVOKEINTERFACE: + for (IParamsValue value : values) { + leaking |= value.params; + } + break; + default: + } + int size; + if (opcode == MULTIANEWARRAY) { + size = 1; + } else { + String desc = (opcode == INVOKEDYNAMIC) ? ((InvokeDynamicInsnNode) insn).desc : ((MethodInsnNode) insn).desc; + size = ASMUtils.getReturnSizeFast(desc); + } + return size == 1 ? val1 : val2; + } + + @Override + public void returnOperation(AbstractInsnNode insn, IParamsValue value, IParamsValue expected) {} + + @Override + public IParamsValue merge(IParamsValue v1, IParamsValue v2) { + if (v1.equals(v2)) return v1; + return new IParamsValue(v1.params | v2.params, Math.min(v1.size, v2.size)); + } +} + +class LeakingParametersCollector extends ParametersUsage { + final boolean[] leaking; + LeakingParametersCollector(MethodNode methodNode) { + super(methodNode); + leaking = new boolean[arity]; + } + + @Override + public ParamsValue unaryOperation(AbstractInsnNode insn, ParamsValue value) { + switch (insn.getOpcode()) { + case GETFIELD: + case ARRAYLENGTH: + case MONITORENTER: + case INSTANCEOF: + case IRETURN: + case ARETURN: + case IFNONNULL: + case IFNULL: + case IFEQ: + case IFNE: + boolean[] params = value.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + break; + default: + } + return super.unaryOperation(insn, value); + } + + @Override + public ParamsValue binaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2) { + switch (insn.getOpcode()) { + case IALOAD: + case LALOAD: + case FALOAD: + case DALOAD: + case AALOAD: + case BALOAD: + case CALOAD: + case SALOAD: + case PUTFIELD: + boolean[] params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + break; + default: + } + return super.binaryOperation(insn, value1, value2); + } + + @Override + public ParamsValue ternaryOperation(AbstractInsnNode insn, ParamsValue value1, ParamsValue value2, ParamsValue value3) { + switch (insn.getOpcode()) { + case IASTORE: + case LASTORE: + case FASTORE: + case DASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + boolean[] params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + break; + default: + } + return super.ternaryOperation(insn, value1, value2, value3); + } + + @Override + public ParamsValue naryOperation(AbstractInsnNode insn, List values) { + switch (insn.getOpcode()) { + case INVOKESTATIC: + case INVOKESPECIAL: + case INVOKEVIRTUAL: + case INVOKEINTERFACE: + for (ParamsValue value : values) { + boolean[] params = value.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + } + break; + default: + } + return super.naryOperation(insn, values); + } +} + diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java index ad0b0e016b52..26095f510fec 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/bytecodeAnalysis/BytecodeAnalysisTest.java @@ -17,6 +17,7 @@ package com.intellij.codeInspection.bytecodeAnalysis; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.InferredAnnotationsManager; +import com.intellij.codeInspection.bytecodeAnalysis.asm.LeakingParametersAnalysis; import com.intellij.codeInspection.bytecodeAnalysis.data.*; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; @@ -94,7 +95,7 @@ public class BytecodeAnalysisTest extends JavaCodeInsightFixtureTestCase { public void visitEnd() { super.visitEnd(); try { - map.put(method, cfg.leakingParameters(classReader.getClassName(), node).first); + map.put(method, LeakingParametersAnalysis.leakingParameters(classReader.getClassName(), node).first); } catch (AnalyzerException ignore) {} }