/*
* FindBugs - Find bugs in Java programs
* Copyright (C) 2004-2006 University of Maryland
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package edu.umd.cs.findbugs.detect;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.Method;
import edu.umd.cs.findbugs.BugAccumulator;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.MethodAnnotation;
import edu.umd.cs.findbugs.OpcodeStack;
import edu.umd.cs.findbugs.ProgramPoint;
import edu.umd.cs.findbugs.SourceLineAnnotation;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.FieldSummary;
import edu.umd.cs.findbugs.ba.PutfieldScanner;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
public class ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass extends OpcodeStackDetector {
final BugAccumulator accumulator;
public ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass(BugReporter bugReporter) {
this.accumulator = new BugAccumulator(bugReporter);
}
Set<XField> initializedFields, nullCheckedFields;
@Override
public void visit(Code obj) {
if (getMethod().isStatic())
return;
initializedFields = new HashSet<XField>();
nullCheckedFields = new HashSet<XField>();
super.visit(obj);
accumulator.reportAccumulatedBugs();
}
@Override
public void sawOpcode(int opcode) {
if (opcode == PUTFIELD) {
XField f = getXFieldOperand();
OpcodeStack.Item item = stack.getStackItem(1);
if (item.getRegisterNumber() != 0)
return;
initializedFields.add(f);
return;
}
if (opcode != GETFIELD)
return;
OpcodeStack.Item item = stack.getStackItem(0);
if (item.getRegisterNumber() != 0)
return;
XField f = getXFieldOperand();
if (f == null || !f.getClassDescriptor().equals(getClassDescriptor()))
return;
if (f.isSynthetic() || f.getName().startsWith("this$"))
return;
if (initializedFields.contains(f))
return;
FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
ClassDescriptor superClassDescriptor = DescriptorFactory.createClassDescriptor(getSuperclassName());
Set<ProgramPoint> calledFrom = fieldSummary.getCalledFromSuperConstructor(superClassDescriptor, getXMethod());
if (calledFrom.isEmpty())
return;
UnreadFieldsData unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFieldsData();
int priority;
if (!unreadFields.isWrittenInConstructor(f))
return;
if (f.isFinal())
priority = HIGH_PRIORITY;
else if (unreadFields.isWrittenDuringInitialization(f) || unreadFields.isWrittenOutsideOfInitialization(f))
priority = NORMAL_PRIORITY;
else
priority = HIGH_PRIORITY;
int nextOpcode = getNextOpcode();
if (nullCheckedFields.contains(f) || nextOpcode == IFNULL || nextOpcode == IFNONNULL || nextOpcode == IFEQ
|| nextOpcode == IFNE) {
priority++;
nullCheckedFields.add(f);
}
for (ProgramPoint p : calledFrom) {
XMethod upcall = getConstructorThatCallsSuperConstructor(p.method);
if (upcall == null)
continue;
Method upcallMethod = null;
for (Method m : getThisClass().getMethods()) {
if (m.getName().equals(upcall.getName()) && m.getSignature().equals(upcall.getSignature())) {
upcallMethod = m;
break;
}
}
if (upcallMethod == null)
continue;
Map<Integer, OpcodeStack.Item> putfieldsAt = PutfieldScanner.getPutfieldsFor(getThisClass(), upcallMethod, f);
if (putfieldsAt.isEmpty())
continue;
Map.Entry<Integer, OpcodeStack.Item> e = putfieldsAt.entrySet().iterator().next();
int pc = e.getKey();
OpcodeStack.Item value = e.getValue();
if (value.isNull() || value.hasConstantValue(0))
priority++;
SourceLineAnnotation fieldSetAt = SourceLineAnnotation.fromVisitedInstruction(getThisClass(), upcallMethod, pc);
BugInstance bug = new BugInstance(this, "UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR", priority).addClassAndMethod(
this).addField(f);
bug.addMethod(p.method).describe(MethodAnnotation.METHOD_SUPERCLASS_CONSTRUCTOR)
.addSourceLine(p.getSourceLineAnnotation()).describe(SourceLineAnnotation.ROLE_CALLED_FROM_SUPERCLASS_AT)
.addMethod(upcall).describe(MethodAnnotation.METHOD_CONSTRUCTOR).add(fieldSetAt)
.describe(SourceLineAnnotation.ROLE_FIELD_SET_TOO_LATE_AT);
accumulator.accumulateBug(bug, this);
}
}
private @CheckForNull
XMethod getConstructorThatCallsSuperConstructor(XMethod superConstructor) {
FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
XMethod lookfor = superConstructor.getSignature().equals("()V") ? null : superConstructor;
for (XMethod m : getXClass().getXMethods())
if (m.getName().equals("<init>")) {
if (fieldSummary.getSuperCall(m) == lookfor)
return m;
}
return null;
}
}