}
CFG cfg = classContext.getCFG(method);
LockChecker lockChecker = classContext.getLockChecker(method);
ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
boolean isGetterMethod = isGetterMethod(classContext, method);
MethodDescriptor methodDescriptor = DescriptorFactory.instance().getMethodDescriptor(classContext.getJavaClass(), method);
if (DEBUG) {
System.out.println("**** Analyzing method " + SignatureConverter.convertMethodSignature(methodGen));
}
for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
Location location = i.next();
try {
Instruction ins = location.getHandle().getInstruction();
XField xfield = null;
boolean isWrite = false;
boolean isLocal = false;
boolean isNullCheck = false;
if (ins instanceof FieldInstruction) {
InstructionHandle n = location.getHandle().getNext();
isNullCheck = n.getInstruction() instanceof IFNONNULL || n.getInstruction() instanceof IFNULL;
if (DEBUG && isNullCheck) {
System.out.println("is null check");
}
FieldInstruction fins = (FieldInstruction) ins;
xfield = Hierarchy.findXField(fins, cpg);
if (xfield == null) {
continue;
}
isWrite = ins.getOpcode() == Constants.PUTFIELD;
isLocal = fins.getClassName(cpg).equals(classContext.getJavaClass().getClassName());
if (DEBUG) {
System.out.println("Handling field access: " + location.getHandle() + " (frame="
+ vnaDataflow.getFactAtLocation(location) + ") :" + n);
}
} else if (ins instanceof INVOKESTATIC) {
INVOKESTATIC inv = (INVOKESTATIC) ins;
InnerClassAccess access = icam.getInnerClassAccess(inv, cpg);
if (access != null && access.getMethodSignature().equals(inv.getSignature(cpg))) {
xfield = access.getField();
isWrite = !access.isLoad();
isLocal = false;
if (DEBUG) {
System.out.println("Handling inner class access: " + location.getHandle() + " (frame="
+ vnaDataflow.getFactAtLocation(location) + ")");
}
}
}
if (xfield == null) {
continue;
}
// We only care about mutable nonvolatile nonpublic instance
// fields.
if (xfield.isStatic() || xfield.isPublic() || xfield.isVolatile() || xfield.isFinal()) {
continue;
}
// The value number frame could be invalid if the basic
// block became unreachable due to edge pruning (dead code).
ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location);
if (!frame.isValid()) {
continue;
}
// Get lock set and instance value
ValueNumber thisValue = !method.isStatic() ? vnaDataflow.getAnalysis().getThisValue() : null;
LockSet lockSet = lockChecker.getFactAtLocation(location);
InstructionHandle handle = location.getHandle();
ValueNumber instance = frame.getInstance(handle.getInstruction(), cpg);
if (DEBUG) {
System.out.println("Lock set: " + lockSet);
System.out.println("value number: " + instance.getNumber());
System.out.println("Lock count: " + lockSet.getLockCount(instance.getNumber()));
}
// Is the instance locked?
// We consider the access to be locked if either
// - the object is explicitly locked, or
// - the field is accessed through the "this" reference,
// and the method is in the locked method set, or
// - any value returned by a called method is locked;
// the (conservative) assumption is that the return lock object
// is correct for synchronizing the access
boolean isExplicitlyLocked = lockSet.getLockCount(instance.getNumber()) > 0;
boolean isAccessedThroughThis = thisValue != null && thisValue.equals(instance);
boolean isLocked = isExplicitlyLocked
|| ((isConstructor(method.getName()) || lockedMethodSet.contains(method)) && isAccessedThroughThis)
|| lockSet.containsReturnValue(vnaDataflow.getAnalysis().getFactory());
// Adjust the field so its class name is the same
// as the type of reference it is accessed through.
// This helps fix false positives produced when a
// threadsafe class is extended by a subclass that
// doesn't care about thread safety.
if (ADJUST_SUBCLASS_ACCESSES) {
// Find the type of the object instance
TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
if (!typeFrame.isValid()) {
continue;
}
Type instanceType = typeFrame.getInstance(handle.getInstruction(), cpg);
if (instanceType instanceof TopType) {
if (DEBUG) {
System.out.println("Freaky: typeFrame is " + typeFrame);
}
continue;
}
// Note: instance type can be Null,
// in which case we won't adjust the field type.
if (instanceType != TypeFrame.getNullType() && instanceType != TypeFrame.getBottomType()) {
if (!(instanceType instanceof ObjectType)) {
throw new DataflowAnalysisException("Field accessed through non-object reference " + instanceType,
methodGen, handle);
}
ObjectType objType = (ObjectType) instanceType;
// If instance class name is not the same as that of the
// field,
// make it so
String instanceClassName = objType.getClassName();
if (!instanceClassName.equals(xfield.getClassName())) {
xfield = XFactory.getExactXField(instanceClassName, xfield.getName(), xfield.getSignature(),
xfield.isStatic());
}
}
}
int kind = 0;
kind |= isLocked ? LOCKED : UNLOCKED;
kind |= isWrite ? WRITE : isNullCheck ? NULLCHECK : READ;
// if (isLocked || !isConstructor(method.getName())) {
if (DEBUG) {
System.out.println("IS2:\t" + SignatureConverter.convertMethodSignature(methodGen) + "\t" + xfield + "\t"
+ ((isWrite ? "W" : "R") + "/" + (isLocked ? "L" : "U")));
}
if (!isLocked && methodDescriptor.getClassDescriptor().isAnonymousClass()) {
continue;
}
FieldStats stats = getStats(xfield);