diff options
Diffstat (limited to 'java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java')
-rw-r--r-- | java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java | 335 |
1 files changed, 227 insertions, 108 deletions
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java index 645c00260af1..dd10492785e7 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/Combined.java @@ -34,64 +34,116 @@ import java.util.Set; import static com.intellij.codeInspection.bytecodeAnalysis.AbstractValues.*; import static org.jetbrains.org.objectweb.asm.Opcodes.*; +import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*; +import static com.intellij.codeInspection.bytecodeAnalysis.CombinedData.*; -final class ParamKey { - final Method method; - final int i; - final boolean stable; +// additional data structures for combined analysis +interface CombinedData { + final class ParamKey { + final Method method; + final int i; + final boolean stable; - ParamKey(Method method, int i, boolean stable) { - this.method = method; - this.i = i; - this.stable = stable; - } + ParamKey(Method method, int i, boolean stable) { + this.method = method; + this.i = i; + this.stable = stable; + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - ParamKey paramKey = (ParamKey)o; + ParamKey paramKey = (ParamKey)o; - if (i != paramKey.i) return false; - if (stable != paramKey.stable) return false; - if (!method.equals(paramKey.method)) return false; + if (i != paramKey.i) return false; + if (stable != paramKey.stable) return false; + if (!method.equals(paramKey.method)) return false; - return true; + return true; + } + + @Override + public int hashCode() { + int result = method.hashCode(); + result = 31 * result + i; + result = 31 * result + (stable ? 1 : 0); + return result; + } } - @Override - public int hashCode() { - int result = method.hashCode(); - result = 31 * result + i; - result = 31 * result + (stable ? 1 : 0); - return result; + // value knowing at which instruction it was created + interface Trackable { + int getOriginInsnIndex(); } -} -final class CombinedCall extends BasicValue { - final Method method; - final boolean stableCall; - final List<? extends BasicValue> args; + final class TrackableCallValue extends BasicValue implements Trackable { + private final int originInsnIndex; + final Method method; + final List<? extends BasicValue> args; + final boolean stableCall; + final boolean thisCall; + + TrackableCallValue(int originInsnIndex, Type tp, Method method, List<? extends BasicValue> args, boolean stableCall, boolean thisCall) { + super(tp); + this.originInsnIndex = originInsnIndex; + this.method = method; + this.args = args; + this.stableCall = stableCall; + this.thisCall = thisCall; + } - CombinedCall(Type tp, Method method, boolean stableCall, List<? extends BasicValue> args) { - super(tp); - this.method = method; - this.stableCall = stableCall; - this.args = args; + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } } -} -final class NParamValue extends BasicValue { - final int n; - public NParamValue(Type type, int n) { - super(type); - this.n = n; + final class NthParamValue extends BasicValue { + final int n; + + public NthParamValue(Type type, int n) { + super(type); + this.n = n; + } + } + + final class TrackableNullValue extends BasicValue implements Trackable { + static final Type NullType = Type.getObjectType("null"); + private final int originInsnIndex; + public TrackableNullValue(int originInsnIndex) { + super(NullType); + this.originInsnIndex = originInsnIndex; + } + + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } } + + final class TrackableValue extends BasicValue implements Trackable { + private final int originInsnIndex; + + public TrackableValue(int originInsnIndex, Type type) { + super(type); + this.originInsnIndex = originInsnIndex; + } + + @Override + public int getOriginInsnIndex() { + return originInsnIndex; + } + } + + BasicValue ThisValue = new BasicValue(Type.getObjectType("java/lang/Object")); } -final class CombinedSingleAnalysis { +// specialized class for analyzing methods without branching in single pass +final class CombinedAnalysis { + private final ControlFlowGraph controlFlow; private final Method method; private final CombinedInterpreter interpreter; @@ -99,11 +151,11 @@ final class CombinedSingleAnalysis { private boolean exception; private final MethodNode methodNode; - CombinedSingleAnalysis(Method method, ControlFlowGraph controlFlow) { + CombinedAnalysis(Method method, ControlFlowGraph controlFlow) { this.method = method; this.controlFlow = controlFlow; methodNode = controlFlow.methodNode; - interpreter = new CombinedInterpreter(Type.getArgumentTypes(methodNode.desc).length); + interpreter = new CombinedInterpreter(methodNode.instructions, Type.getArgumentTypes(methodNode.desc).length); } final void analyze() throws AnalyzerException { @@ -144,11 +196,11 @@ final class CombinedSingleAnalysis { final Equation<Key, Value> notNullParamEquation(int i, boolean stable) { final Key key = new Key(method, new In(i, In.NOT_NULL), stable); final Result<Key, Value> result; - if (interpreter.dereferenced[i]) { + if (interpreter.dereferencedParams[i]) { result = new Final<Key, Value>(Value.NotNull); } else { - Set<ParamKey> calls = interpreter.callDerefs[i]; + Set<ParamKey> calls = interpreter.parameterFlow[i]; if (calls == null || calls.isEmpty()) { result = new Final<Key, Value>(Value.Top); } @@ -166,11 +218,11 @@ final class CombinedSingleAnalysis { final Equation<Key, Value> nullableParamEquation(int i, boolean stable) { final Key key = new Key(method, new In(i, In.NULLABLE), stable); final Result<Key, Value> result; - if (interpreter.dereferenced[i] || interpreter.notNullable[i] || returnValue instanceof NParamValue && ((NParamValue)returnValue).n == i) { + if (interpreter.dereferencedParams[i] || interpreter.notNullableParams[i] || returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) { result = new Final<Key, Value>(Value.Top); } else { - Set<ParamKey> calls = interpreter.callDerefs[i]; + Set<ParamKey> calls = interpreter.parameterFlow[i]; if (calls == null || calls.isEmpty()) { result = new Final<Key, Value>(Value.Null); } @@ -188,7 +240,7 @@ final class CombinedSingleAnalysis { final Equation<Key, Value> contractEquation(int i, Value inValue, boolean stable) { final Key key = new Key(method, new InOut(i, inValue), stable); final Result<Key, Value> result; - if (exception || (inValue == Value.Null && interpreter.dereferenced[i])) { + if (exception || (inValue == Value.Null && interpreter.dereferencedParams[i])) { result = new Final<Key, Value>(Value.Bot); } else if (FalseValue == returnValue) { @@ -197,29 +249,29 @@ final class CombinedSingleAnalysis { else if (TrueValue == returnValue) { result = new Final<Key, Value>(Value.True); } - else if (NullValue == returnValue) { + else if (returnValue instanceof TrackableNullValue) { result = new Final<Key, Value>(Value.Null); } - else if (returnValue instanceof NotNullValue) { + else if (returnValue instanceof NotNullValue || ThisValue == returnValue) { result = new Final<Key, Value>(Value.NotNull); } - else if (returnValue instanceof NParamValue && ((NParamValue)returnValue).n == i) { + else if (returnValue instanceof NthParamValue && ((NthParamValue)returnValue).n == i) { result = new Final<Key, Value>(inValue); } - else if (returnValue instanceof CombinedCall) { - CombinedCall call = (CombinedCall)returnValue; + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; HashSet<Key> keys = new HashSet<Key>(); for (int argI = 0; argI < call.args.size(); argI++) { BasicValue arg = call.args.get(argI); - if (arg instanceof NParamValue) { - NParamValue npv = (NParamValue)arg; + if (arg instanceof NthParamValue) { + NthParamValue npv = (NthParamValue)arg; if (npv.n == i) { keys.add(new Key(call.method, new InOut(argI, inValue), call.stableCall)); } } } if (ASMUtils.isReferenceType(call.getType())) { - keys.add(new Key(call.method, new Out(), call.stableCall)); + keys.add(new Key(call.method, Out, call.stableCall)); } if (keys.isEmpty()) { result = new Final<Key, Value>(Value.Top); @@ -234,7 +286,7 @@ final class CombinedSingleAnalysis { } final Equation<Key, Value> outContractEquation(boolean stable) { - final Key key = new Key(method, new Out(), stable); + final Key key = new Key(method, Out, stable); final Result<Key, Value> result; if (exception) { result = new Final<Key, Value>(Value.Bot); @@ -245,15 +297,15 @@ final class CombinedSingleAnalysis { else if (TrueValue == returnValue) { result = new Final<Key, Value>(Value.True); } - else if (NullValue == returnValue) { + else if (returnValue instanceof TrackableNullValue) { result = new Final<Key, Value>(Value.Null); } - else if (returnValue instanceof NotNullValue) { + else if (returnValue instanceof NotNullValue || returnValue == ThisValue) { result = new Final<Key, Value>(Value.NotNull); } - else if (returnValue instanceof CombinedCall) { - CombinedCall call = (CombinedCall)returnValue; - Key callKey = new Key(call.method, new Out(), call.stableCall); + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; + Key callKey = new Key(call.method, Out, call.stableCall); Set<Key> keys = new SingletonSet<Key>(callKey); result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Top, keys))); } @@ -263,6 +315,28 @@ final class CombinedSingleAnalysis { return new Equation<Key, Value>(key, result); } + final Equation<Key, Value> nullableResultEquation(boolean stable) { + final Key key = new Key(method, NullableOut, stable); + final Result<Key, Value> result; + if (exception || + returnValue instanceof Trackable && interpreter.dereferencedValues[((Trackable)returnValue).getOriginInsnIndex()]) { + result = new Final<Key, Value>(Value.Bot); + } + else if (returnValue instanceof TrackableCallValue) { + TrackableCallValue call = (TrackableCallValue)returnValue; + Key callKey = new Key(call.method, NullableOut, call.stableCall || call.thisCall); + Set<Key> keys = new SingletonSet<Key>(callKey); + result = new Pending<Key, Value>(new SingletonSet<Product<Key, Value>>(new Product<Key, Value>(Value.Null, keys))); + } + else if (returnValue instanceof TrackableNullValue) { + result = new Final<Key, Value>(Value.Null); + } + else { + result = new Final<Key, Value>(Value.Bot); + } + return new Equation<Key, Value>(key, result); + } + final Frame<BasicValue> createStartFrame() { Frame<BasicValue> frame = new Frame<BasicValue>(methodNode.maxLocals, methodNode.maxStack); Type returnType = Type.getReturnType(methodNode.desc); @@ -272,10 +346,10 @@ final class CombinedSingleAnalysis { Type[] args = Type.getArgumentTypes(methodNode.desc); int local = 0; if ((methodNode.access & Opcodes.ACC_STATIC) == 0) { - frame.setLocal(local++, new AbstractValues.NotNullValue(Type.getObjectType(controlFlow.className))); + frame.setLocal(local++, ThisValue); } for (int i = 0; i < args.length; i++) { - BasicValue value = new NParamValue(args[i], i); + BasicValue value = new NthParamValue(args[i], i); frame.setLocal(local++, value); if (args[i].getSize() == 2) { frame.setLocal(local++, BasicValue.UNINITIALIZED_VALUE); @@ -289,25 +363,47 @@ final class CombinedSingleAnalysis { } final class CombinedInterpreter extends BasicInterpreter { - final boolean[] dereferenced; - final boolean[] notNullable; - final Set<ParamKey>[] callDerefs; - - CombinedInterpreter(int arity) { - dereferenced = new boolean[arity]; - notNullable = new boolean[arity]; - callDerefs = new Set[arity]; + // Parameters dereferenced during execution of a method, tracked by parameter's indices. + // Dereferenced parameters are @NotNull. + final boolean[] dereferencedParams; + // Parameters, that are written to something or passed to an interface methods. + // This parameters cannot be @Nullable. + final boolean[] notNullableParams; + // parameterFlow(i) for i-th parameter stores a set parameter positions it is passed to + // parameter is @NotNull if any of its usages are @NotNull + final Set<ParamKey>[] parameterFlow; + + // Trackable values that were dereferenced during execution of a method + // Values are are identified by `origin` index + final boolean[] dereferencedValues; + private final InsnList insns; + + CombinedInterpreter(InsnList insns, int arity) { + dereferencedParams = new boolean[arity]; + notNullableParams = new boolean[arity]; + parameterFlow = new Set[arity]; + this.insns = insns; + dereferencedValues = new boolean[insns.size()]; + } + + private int insnIndex(AbstractInsnNode insn) { + return insns.indexOf(insn); + } + + private static BasicValue track(int origin, BasicValue basicValue) { + return basicValue == null ? null : new TrackableValue(origin, basicValue.getType()); } @Override public BasicValue newOperation(AbstractInsnNode insn) throws AnalyzerException { + int origin = insnIndex(insn); switch (insn.getOpcode()) { case ICONST_0: return FalseValue; case ICONST_1: return TrueValue; case ACONST_NULL: - return NullValue; + return new TrackableNullValue(origin); case LDC: Object cst = ((LdcInsnNode)insn).cst; if (cst instanceof Type) { @@ -330,22 +426,26 @@ final class CombinedInterpreter extends BasicInterpreter { return new NotNullValue(Type.getObjectType(((TypeInsnNode)insn).desc)); default: } - return super.newOperation(insn); + return track(origin, super.newOperation(insn)); } @Override public BasicValue unaryOperation(AbstractInsnNode insn, BasicValue value) throws AnalyzerException { + int origin = insnIndex(insn); switch (insn.getOpcode()) { case GETFIELD: case ARRAYLENGTH: case MONITORENTER: - if (value instanceof NParamValue) { - dereferenced[((NParamValue)value).n] = true; + if (value instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value).n] = true; } - return super.unaryOperation(insn, value); + if (value instanceof Trackable) { + dereferencedValues[((Trackable)value).getOriginInsnIndex()] = true; + } + return track(origin, super.unaryOperation(insn, value)); case CHECKCAST: - if (value instanceof NParamValue) { - return new NParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NParamValue)value).n); + if (value instanceof NthParamValue) { + return new NthParamValue(Type.getObjectType(((TypeInsnNode)insn).desc), ((NthParamValue)value).n); } break; case NEWARRAY: @@ -353,12 +453,23 @@ final class CombinedInterpreter extends BasicInterpreter { return new NotNullValue(super.unaryOperation(insn, value).getType()); default: } - return super.unaryOperation(insn, value); + return track(origin, super.unaryOperation(insn, value)); } @Override public BasicValue binaryOperation(AbstractInsnNode insn, BasicValue value1, BasicValue value2) throws AnalyzerException { switch (insn.getOpcode()) { + case PUTFIELD: + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; + } + if (value2 instanceof NthParamValue) { + notNullableParams[((NthParamValue)value2).n] = true; + } + break; case IALOAD: case LALOAD: case FALOAD: @@ -367,21 +478,16 @@ final class CombinedInterpreter extends BasicInterpreter { case BALOAD: case CALOAD: case SALOAD: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; - } - break; - case PUTFIELD: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; } - if (value2 instanceof NParamValue) { - notNullable[((NParamValue)value2).n] = true; + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } break; default: } - return super.binaryOperation(insn, value1, value2); + return track(insnIndex(insn), super.binaryOperation(insn, value1, value2)); } @Override @@ -395,35 +501,46 @@ final class CombinedInterpreter extends BasicInterpreter { case BASTORE: case CASTORE: case SASTORE: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } break; case AASTORE: - if (value1 instanceof NParamValue) { - dereferenced[((NParamValue)value1).n] = true; + if (value1 instanceof NthParamValue) { + dereferencedParams[((NthParamValue)value1).n] = true; + } + if (value1 instanceof Trackable) { + dereferencedValues[((Trackable)value1).getOriginInsnIndex()] = true; } - if (value3 instanceof NParamValue) { - notNullable[((NParamValue)value3).n] = true; + if (value3 instanceof NthParamValue) { + notNullableParams[((NthParamValue)value3).n] = true; } break; default: } - return super.ternaryOperation(insn, value1, value2, value3); + return null; } @Override public BasicValue naryOperation(AbstractInsnNode insn, List<? extends BasicValue> values) throws AnalyzerException { int opCode = insn.getOpcode(); int shift = opCode == INVOKESTATIC ? 0 : 1; - + int origin = insnIndex(insn); switch (opCode) { case INVOKESPECIAL: case INVOKEINTERFACE: case INVOKEVIRTUAL: - if (values.get(0) instanceof NParamValue) { - dereferenced[((NParamValue)values.get(0)).n] = true; + BasicValue receiver = values.get(0); + if (receiver instanceof NthParamValue) { + dereferencedParams[((NthParamValue)receiver).n] = true; + } + if (receiver instanceof Trackable) { + dereferencedValues[((Trackable)receiver).getOriginInsnIndex()] = true; } + default: } switch (opCode) { @@ -437,29 +554,31 @@ final class CombinedInterpreter extends BasicInterpreter { Type retType = Type.getReturnType(mNode.desc); for (int i = shift; i < values.size(); i++) { - if (values.get(i) instanceof NParamValue) { - int n = ((NParamValue)values.get(i)).n; + if (values.get(i) instanceof NthParamValue) { + int n = ((NthParamValue)values.get(i)).n; if (opCode == INVOKEINTERFACE) { - notNullable[n] = true; + notNullableParams[n] = true; } else { - Set<ParamKey> npKeys = callDerefs[n]; + Set<ParamKey> npKeys = parameterFlow[n]; if (npKeys == null) { npKeys = new HashSet<ParamKey>(); - callDerefs[n] = npKeys; + parameterFlow[n] = npKeys; } npKeys.add(new ParamKey(method, i - shift, stable)); } } } + BasicValue receiver = null; if (shift == 1) { - values.remove(0); + receiver = values.remove(0); } - return new CombinedCall(retType, method, stable, values); + boolean thisCall = (opCode == INVOKEINTERFACE || opCode == INVOKEVIRTUAL) && receiver == ThisValue; + return new TrackableCallValue(origin, retType, method, values, stable, thisCall); case MULTIANEWARRAY: return new NotNullValue(super.naryOperation(insn, values).getType()); default: } - return super.naryOperation(insn, values); + return track(origin, super.naryOperation(insn, values)); } } |