summaryrefslogtreecommitdiff
path: root/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/asm/LeakingParameters.java
diff options
context:
space:
mode:
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.java610
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);
+ }
+}
+