001: /*
002: * Bytecode Analysis Framework
003: * Copyright (C) 2003-2005 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.ba.npe;
021:
022: import java.util.Map;
023:
024: import org.apache.bcel.generic.ACONST_NULL;
025: import org.apache.bcel.generic.ANEWARRAY;
026: import org.apache.bcel.generic.CHECKCAST;
027: import org.apache.bcel.generic.ConstantPoolGen;
028: import org.apache.bcel.generic.GETFIELD;
029: import org.apache.bcel.generic.GETSTATIC;
030: import org.apache.bcel.generic.INVOKEINTERFACE;
031: import org.apache.bcel.generic.INVOKESPECIAL;
032: import org.apache.bcel.generic.INVOKESTATIC;
033: import org.apache.bcel.generic.INVOKEVIRTUAL;
034: import org.apache.bcel.generic.Instruction;
035: import org.apache.bcel.generic.InvokeInstruction;
036: import org.apache.bcel.generic.LDC;
037: import org.apache.bcel.generic.LDC2_W;
038: import org.apache.bcel.generic.MULTIANEWARRAY;
039: import org.apache.bcel.generic.NEW;
040: import org.apache.bcel.generic.NEWARRAY;
041: import org.apache.bcel.generic.PUTFIELD;
042: import org.apache.bcel.generic.ReferenceType;
043: import org.apache.bcel.generic.Type;
044:
045: import edu.umd.cs.findbugs.SystemProperties;
046: import edu.umd.cs.findbugs.ba.AbstractFrameModelingVisitor;
047: import edu.umd.cs.findbugs.ba.AnalysisContext;
048: import edu.umd.cs.findbugs.ba.AssertionMethods;
049: import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
050: import edu.umd.cs.findbugs.ba.NullnessAnnotation;
051: import edu.umd.cs.findbugs.ba.XFactory;
052: import edu.umd.cs.findbugs.ba.XField;
053: import edu.umd.cs.findbugs.ba.XMethod;
054: import edu.umd.cs.findbugs.ba.vna.AvailableLoad;
055: import edu.umd.cs.findbugs.ba.vna.ValueNumber;
056: import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysisFeatures;
057: import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
058: import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
059:
060: public class IsNullValueFrameModelingVisitor extends
061: AbstractFrameModelingVisitor<IsNullValue, IsNullValueFrame> {
062:
063: private static final boolean NO_ASSERT_HACK = SystemProperties
064: .getBoolean("inva.noAssertHack");
065: private static final boolean MODEL_NONNULL_RETURN = SystemProperties
066: .getBoolean("fnd.modelNonnullReturn", true);
067: private AssertionMethods assertionMethods;
068: private ValueNumberDataflow vnaDataflow;
069: private final boolean trackValueNumbers;
070: private int slotContainingNewNullValue;
071:
072: public IsNullValueFrameModelingVisitor(ConstantPoolGen cpg,
073: AssertionMethods assertionMethods,
074: ValueNumberDataflow vnaDataflow, boolean trackValueNumbers) {
075: super (cpg);
076: this .assertionMethods = assertionMethods;
077: this .vnaDataflow = vnaDataflow;
078: this .trackValueNumbers = trackValueNumbers;
079: }
080:
081: /* (non-Javadoc)
082: * @see edu.umd.cs.findbugs.ba.AbstractFrameModelingVisitor#analyzeInstruction(org.apache.bcel.generic.Instruction)
083: */
084: @Override
085: public void analyzeInstruction(Instruction ins)
086: throws DataflowAnalysisException {
087: slotContainingNewNullValue = -1;
088: super .analyzeInstruction(ins);
089: }
090:
091: /**
092: * @return Returns the slotContainingNewNullValue; or -1 if no new null value
093: * was produced
094: */
095: public int getSlotContainingNewNullValue() {
096: return slotContainingNewNullValue;
097: }
098:
099: @Override
100: public IsNullValue getDefaultValue() {
101: return IsNullValue.nonReportingNotNullValue();
102: }
103:
104: // Overrides of specific instruction visitor methods.
105: // ACONST_NULL obviously produces a value that is DEFINITELY NULL.
106: // LDC produces values that are NOT NULL.
107: // NEW produces values that are NOT NULL.
108:
109: // Note that all instructions that have an implicit null
110: // check (field access, invoke, etc.) are handled in IsNullValueAnalysis,
111: // because handling them relies on control flow (the existence of
112: // an ETB and exception edge prior to the block containing the
113: // instruction with the null check.)
114:
115: // Note that we don't override IFNULL and IFNONNULL.
116: // Those are handled in the analysis itself, because we need
117: // to produce different values in each of the control successors.
118:
119: private void produce(IsNullValue value) {
120: IsNullValueFrame frame = getFrame();
121: frame.pushValue(value);
122: newValueOnTOS();
123: }
124:
125: private void produce2(IsNullValue value) {
126: IsNullValueFrame frame = getFrame();
127: frame.pushValue(value);
128: frame.pushValue(value);
129: }
130:
131: /**
132: * Handle method invocations.
133: * Generally, we want to get rid of null information following a
134: * call to a likely exception thrower or assertion.
135: */
136: private void handleInvoke(InvokeInstruction obj) {
137: Type callType = obj.getLoadClassType(getCPG());
138: Type returnType = obj.getReturnType(getCPG());
139:
140: boolean stringMethodCall = callType.equals(Type.STRING)
141: && returnType.equals(Type.STRING);
142:
143: // Determine if we are going to model the return value of this call.
144: boolean modelCallReturnValue = MODEL_NONNULL_RETURN
145: && returnType instanceof ReferenceType;
146:
147: if (!modelCallReturnValue) {
148: // Normal case: Assume returned values are non-reporting non-null.
149: handleNormalInstruction(obj);
150: } else {
151: // Special case: some special value is pushed on the stack for the return value
152: IsNullValue pushValue = null;
153: if (false && stringMethodCall) {
154: // String methods always return a non-null value
155: pushValue = IsNullValue.nonNullValue();
156: } else {
157: // Check to see if this method is in either database
158: XMethod calledMethod = XFactory.createXMethod(obj,
159: getCPG());
160: if (IsNullValueAnalysis.DEBUG)
161: System.out.println("Check " + calledMethod
162: + " for null return...");
163: NullnessAnnotation annotation = AnalysisContext
164: .currentAnalysisContext()
165: .getNullnessAnnotationDatabase()
166: .getResolvedAnnotation(calledMethod, false);
167: Boolean alwaysNonNull = AnalysisContext
168: .currentAnalysisContext()
169: .getReturnValueNullnessPropertyDatabase()
170: .getProperty(calledMethod.getMethodDescriptor());
171: if (annotation == NullnessAnnotation.CHECK_FOR_NULL) {
172: if (IsNullValueAnalysis.DEBUG) {
173: System.out.println("Null value returned from "
174: + calledMethod);
175: }
176: pushValue = IsNullValue
177: .nullOnSimplePathValue()
178: .markInformationAsComingFromReturnValueOfMethod(
179: calledMethod);
180: } else if (annotation == NullnessAnnotation.NULLABLE) {
181: pushValue = IsNullValue.nonReportingNotNullValue();
182: } else if (annotation == NullnessAnnotation.NONNULL
183: || (alwaysNonNull != null && alwaysNonNull
184: .booleanValue())) {
185: // Method is declared NOT to return null
186: if (IsNullValueAnalysis.DEBUG) {
187: System.out.println("NonNull value return from "
188: + calledMethod);
189: }
190: pushValue = IsNullValue
191: .nonNullValue()
192: .markInformationAsComingFromReturnValueOfMethod(
193: calledMethod);
194:
195: } else {
196: pushValue = IsNullValue.nonReportingNotNullValue();
197: }
198: }
199:
200: modelInstruction(obj, getNumWordsConsumed(obj),
201: getNumWordsProduced(obj), pushValue);
202: newValueOnTOS();
203: }
204:
205: if (!NO_ASSERT_HACK) {
206: if (assertionMethods.isAssertionCall(obj)) {
207: IsNullValueFrame frame = getFrame();
208: for (int i = 0; i < frame.getNumSlots(); ++i) {
209: IsNullValue value = frame.getValue(i);
210: if (value.isDefinitelyNull()
211: || value.isNullOnSomePath()) {
212: frame.setValue(i, IsNullValue
213: .nonReportingNotNullValue());
214: }
215: }
216: for (Map.Entry<ValueNumber, IsNullValue> e : frame
217: .getKnownValueMapEntrySet()) {
218: IsNullValue value = e.getValue();
219: if (value.isDefinitelyNull()
220: || value.isNullOnSomePath())
221: e.setValue(IsNullValue
222: .nonReportingNotNullValue());
223:
224: }
225: }
226: }
227: }
228:
229: /**
230: * Hook indicating that a new (possibly-null) value is on the
231: * top of the stack.
232: */
233: private void newValueOnTOS() {
234: IsNullValueFrame frame = getFrame();
235: if (frame.getStackDepth() < 1) {
236: return;
237: }
238: int tosSlot = frame.getNumSlots() - 1;
239: IsNullValue tos = frame.getValue(tosSlot);
240: if (tos.isDefinitelyNull()) {
241: slotContainingNewNullValue = tosSlot;
242: }
243: if (trackValueNumbers) {
244: try {
245: ValueNumberFrame vnaFrameAfter = vnaDataflow
246: .getFactAfterLocation(getLocation());
247: if (vnaFrameAfter.isValid()) {
248: ValueNumber tosVN = vnaFrameAfter.getTopValue();
249: getFrame().setKnownValue(tosVN, tos);
250: }
251: } catch (DataflowAnalysisException e) {
252: AnalysisContext.logError("error", e);
253: }
254: }
255: }
256:
257: @Override
258: public void visitPUTFIELD(PUTFIELD obj) {
259: if (getNumWordsConsumed(obj) != 2) {
260: super .visitPUTFIELD(obj);
261: return;
262: }
263:
264: IsNullValue nullValueStored = null;
265: try {
266: nullValueStored = getFrame().getTopValue();
267: } catch (DataflowAnalysisException e1) {
268: AnalysisContext.logError("Oops", e1);
269: }
270: super .visitPUTFIELD(obj);
271: XField field = (XField) XFactory.createXField(obj, cpg);
272: if (nullValueStored != null
273: && ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION)
274: try {
275: ValueNumberFrame vnaFrameBefore = vnaDataflow
276: .getFactAtLocation(getLocation());
277: ValueNumber refValue = vnaFrameBefore.getStackValue(1);
278: AvailableLoad load = new AvailableLoad(refValue, field);
279: ValueNumberFrame vnaFrameAfter = vnaDataflow
280: .getFactAfterLocation(getLocation());
281: ValueNumber[] newValueNumbersForField = vnaFrameAfter
282: .getAvailableLoad(load);
283: if (newValueNumbersForField != null
284: && trackValueNumbers)
285: for (ValueNumber v : newValueNumbersForField)
286: getFrame().setKnownValue(v, nullValueStored);
287: } catch (DataflowAnalysisException e) {
288: AnalysisContext.logError("Oops", e);
289: }
290: }
291:
292: @Override
293: public void visitGETFIELD(GETFIELD obj) {
294: if (getNumWordsProduced(obj) != 1) {
295: super .visitGETFIELD(obj);
296: return;
297: }
298:
299: if (checkForKnownValue(obj)) {
300: return;
301: }
302:
303: XField field = XFactory.createXField(obj, cpg);
304:
305: NullnessAnnotation annotation = AnalysisContext
306: .currentAnalysisContext()
307: .getNullnessAnnotationDatabase().getResolvedAnnotation(
308: field, false);
309: if (annotation == NullnessAnnotation.NONNULL) {
310: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
311: produce(IsNullValue.nonNullValue());
312: } else if (annotation == NullnessAnnotation.CHECK_FOR_NULL) {
313: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
314: produce(IsNullValue.nullOnSimplePathValue()
315: .markInformationAsComingFromFieldValue(field));
316: } else {
317:
318: super .visitGETFIELD(obj);
319: }
320:
321: }
322:
323: /* (non-Javadoc)
324: * @see edu.umd.cs.findbugs.ba.AbstractFrameModelingVisitor#visitGETSTATIC(org.apache.bcel.generic.GETSTATIC)
325: */
326: @Override
327: public void visitGETSTATIC(GETSTATIC obj) {
328: if (getNumWordsProduced(obj) != 1) {
329: super .visitGETSTATIC(obj);
330: return;
331: }
332:
333: if (checkForKnownValue(obj)) {
334: return;
335: }
336: XField field = XFactory.createXField(obj, cpg);
337: if (field.getClassName().equals("java.util.logging.Level")
338: && field.getName().equals("SEVERE")
339: || field.getClassName()
340: .equals("org.apache.log4j.Level")
341: && (field.getName().equals("ERROR") || field.getName()
342: .equals("FATAL")))
343: getFrame().toExceptionValues();
344:
345: if (field.getName().startsWith("class$")) {
346: produce(IsNullValue.nonNullValue());
347: return;
348: }
349: NullnessAnnotation annotation = AnalysisContext
350: .currentAnalysisContext()
351: .getNullnessAnnotationDatabase().getResolvedAnnotation(
352: field, false);
353: if (annotation == NullnessAnnotation.NONNULL) {
354: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
355: produce(IsNullValue.nonNullValue());
356: } else if (annotation == NullnessAnnotation.CHECK_FOR_NULL) {
357: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
358: produce(IsNullValue.nullOnSimplePathValue()
359: .markInformationAsComingFromFieldValue(field));
360: } else {
361:
362: super .visitGETSTATIC(obj);
363: }
364: }
365:
366: /**
367: * Check given Instruction to see if it produces a known value.
368: * If so, model the instruction and return true.
369: * Otherwise, do nothing and return false.
370: * Should only be used for instructions that produce a single
371: * value on the top of the stack.
372: *
373: * @param obj the Instruction the instruction
374: * @return true if the instruction produced a known value and was modeled,
375: * false otherwise
376: */
377: private boolean checkForKnownValue(Instruction obj) {
378: if (trackValueNumbers) {
379: try {
380: // See if the value number loaded here is a known value
381: ValueNumberFrame vnaFrameAfter = vnaDataflow
382: .getFactAfterLocation(getLocation());
383: if (vnaFrameAfter.isValid()) {
384: ValueNumber tosVN = vnaFrameAfter.getTopValue();
385: IsNullValue knownValue = getFrame().getKnownValue(
386: tosVN);
387: if (knownValue != null) {
388: //System.out.println("Produce known value!");
389: // The value produced by this instruction is known.
390: // Push the known value.
391: modelNormalInstruction(obj,
392: getNumWordsConsumed(obj), 0);
393: produce(knownValue);
394: return true;
395: }
396: }
397: } catch (DataflowAnalysisException e) {
398: // Ignore...
399: }
400: }
401: return false;
402: }
403:
404: @Override
405: public void visitACONST_NULL(ACONST_NULL obj) {
406: produce(IsNullValue.nullValue());
407: }
408:
409: @Override
410: public void visitNEW(NEW obj) {
411: produce(IsNullValue.nonNullValue());
412: }
413:
414: @Override
415: public void visitNEWARRAY(NEWARRAY obj) {
416: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
417: produce(IsNullValue.nonNullValue());
418: }
419:
420: @Override
421: public void visitANEWARRAY(ANEWARRAY obj) {
422: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
423: produce(IsNullValue.nonNullValue());
424: }
425:
426: @Override
427: public void visitMULTIANEWARRAY(MULTIANEWARRAY obj) {
428: modelNormalInstruction(obj, getNumWordsConsumed(obj), 0);
429: produce(IsNullValue.nonNullValue());
430: }
431:
432: @Override
433: public void visitLDC(LDC obj) {
434: produce(IsNullValue.nonNullValue());
435: }
436:
437: @Override
438: public void visitLDC2_W(LDC2_W obj) {
439: produce2(IsNullValue.nonNullValue());
440: }
441:
442: @Override
443: public void visitCHECKCAST(CHECKCAST obj) {
444: // Do nothing
445: }
446:
447: @Override
448: public void visitINVOKESTATIC(INVOKESTATIC obj) {
449: handleInvoke(obj);
450: }
451:
452: @Override
453: public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
454: handleInvoke(obj);
455: }
456:
457: @Override
458: public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) {
459: handleInvoke(obj);
460: }
461:
462: @Override
463: public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) {
464: handleInvoke(obj);
465: }
466:
467: }
468:
469: // vim:ts=4
|