refactoring: moving LeakingParametersAnalysis into asm package

This commit is contained in:
Ilya Klyuchnikov
2014-08-19 09:04:33 +04:00
parent cd864c4a22
commit 559fbf097f
4 changed files with 571 additions and 543 deletions
@@ -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<HKey, HResult, FileContent>
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);
@@ -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<boolean[], Frame<Value>[]> leakingParameters(String className, MethodNode methodNode) throws AnalyzerException {
Frame<ParamsValue>[] frames = new Analyzer<ParamsValue>(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<ParamsValue> frame = frames[i];
if (frame != null) {
switch (insnNode.getType()) {
case AbstractInsnNode.LABEL:
case AbstractInsnNode.LINE:
case AbstractInsnNode.FRAME:
break;
default:
new Frame<ParamsValue>(frame).execute(insnNode, collector);
}
}
}
// to hack type checker
Frame<?>[] frames1 = frames;
return new Pair<boolean[], Frame<Value>[]>(collector.leaking, (Frame<Value>[]) frames1);
}
static Pair<boolean[], Frame<Value>[]> fastLeakingParameters(String className, MethodNode methodNode) throws AnalyzerException {
IParametersUsage parametersUsage = new IParametersUsage(methodNode);
Frame<?>[] frames = new Analyzer<IParamsValue>(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<boolean[], Frame<Value>[]>(result, (Frame<Value>[]) 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<ParamsValue> {
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<? extends ParamsValue> 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<IParamsValue> {
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<? extends IParamsValue> 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<? extends ParamsValue> 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);
}
}
@@ -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<boolean[], Frame<Value>[]> leakingParameters(String className, MethodNode methodNode) throws AnalyzerException {
Frame<ParamsValue>[] frames = new Analyzer<ParamsValue>(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<ParamsValue> frame = frames[i];
if (frame != null) {
switch (insnNode.getType()) {
case AbstractInsnNode.LABEL:
case AbstractInsnNode.LINE:
case AbstractInsnNode.FRAME:
break;
default:
new Frame<ParamsValue>(frame).execute(insnNode, collector);
}
}
}
// to hack type checker
Frame<?>[] frames1 = frames;
return new Pair<boolean[], Frame<Value>[]>(collector.leaking, (Frame<Value>[]) frames1);
}
public static Pair<boolean[], Frame<Value>[]> fastLeakingParameters(String className, MethodNode methodNode) throws AnalyzerException {
IParametersUsage parametersUsage = new IParametersUsage(methodNode);
Frame<?>[] frames = new Analyzer<IParamsValue>(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<boolean[], Frame<Value>[]>(result, (Frame<Value>[]) 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<ParamsValue> {
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<? extends ParamsValue> 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<IParamsValue> {
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<? extends IParamsValue> 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<? extends ParamsValue> 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);
}
}
@@ -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) {}
}