diff options
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java')
-rw-r--r-- | java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java | 610 |
1 files changed, 610 insertions, 0 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java new file mode 100644 index 000000000000..1a7ab15722f9 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java @@ -0,0 +1,610 @@ +/* + * 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.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 LeakingParameters { + public final Frame<Value>[] frames; + public final boolean[] parameters; + public final boolean[] nullableParameters; + + public LeakingParameters(Frame<Value>[] frames, boolean[] parameters, boolean[] nullableParameters) { + this.frames = frames; + this.parameters = parameters; + this.nullableParameters = nullableParameters; + } + + public static LeakingParameters build(String className, MethodNode methodNode, boolean jsr) throws AnalyzerException { + Frame<ParamsValue>[] frames = jsr ? + new Analyzer<ParamsValue>(new ParametersUsage(methodNode)).analyze(className, methodNode) : + new LiteAnalyzer<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); + } + } + } + boolean[] notNullParameters = collector.leaking; + boolean[] nullableParameters = collector.nullableLeaking; + for (int i = 0; i < nullableParameters.length; i++) { + nullableParameters[i] |= notNullParameters[i]; + } + return new LeakingParameters((Frame<Value>[])(Frame<?>[])frames, notNullParameters, nullableParameters); + } + + public static LeakingParameters buildFast(String className, MethodNode methodNode, boolean jsr) throws AnalyzerException { + IParametersUsage parametersUsage = new IParametersUsage(methodNode); + Frame<?>[] frames = jsr ? + new Analyzer<IParamsValue>(parametersUsage).analyze(className, methodNode) : + new LiteAnalyzer<IParamsValue>(parametersUsage).analyze(className, methodNode); + int leakingMask = parametersUsage.leaking; + int nullableLeakingMask = parametersUsage.nullableLeaking; + boolean[] notNullParameters = new boolean[parametersUsage.arity]; + boolean[] nullableParameters = new boolean[parametersUsage.arity]; + for (int i = 0; i < notNullParameters.length; i++) { + notNullParameters[i] = (leakingMask & (1 << i)) != 0; + nullableParameters[i] = ((leakingMask | nullableLeakingMask) & (1 << i)) != 0; + } + return new LeakingParameters((Frame<Value>[])frames, notNullParameters, nullableParameters); + } +} + +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 nullableLeaking = 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: + leaking |= value1.params; + size = 1; + break; + case PUTFIELD: + leaking |= value1.params; + nullableLeaking |= value2.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 BASTORE: + case CASTORE: + case SASTORE: + leaking |= value1.params; + break; + case AASTORE: + leaking |= value1.params; + nullableLeaking |= value3.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; + final boolean[] nullableLeaking; + LeakingParametersCollector(MethodNode methodNode) { + super(methodNode); + leaking = new boolean[arity]; + nullableLeaking = 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: + boolean[] params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + break; + case PUTFIELD: + params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + params = value2.params; + for (int i = 0; i < arity; i++) { + nullableLeaking[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 BASTORE: + case CASTORE: + case SASTORE: + boolean[] params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + break; + case AASTORE: + params = value1.params; + for (int i = 0; i < arity; i++) { + leaking[i] |= params[i]; + } + params = value3.params; + for (int i = 0; i < arity; i++) { + nullableLeaking[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); + } +} + |