//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.jvm.bytecode;
import cmu.conditional.Conditional;
import cmu.conditional.One;
import de.fosd.typechef.featureexpr.FeatureExpr;
import gov.nasa.jpf.vm.ClassInfo;
import gov.nasa.jpf.vm.ClassLoaderInfo;
import gov.nasa.jpf.vm.ElementInfo;
import gov.nasa.jpf.vm.FieldInfo;
import gov.nasa.jpf.vm.MJIEnv;
import gov.nasa.jpf.vm.ThreadInfo;
import gov.nasa.jpf.vm.VM;
/**
* class abstracting instructions that access instance fields
*/
public abstract class InstanceFieldInstruction extends FieldInstruction
{
/**
* this is required for package external derived GET/PUTFIELDS that
* replace enter().
*
* USE WITH CARE, AND ONLY FROM DERIVED CLASSES
*/
protected Conditional<Integer> lastThis = One.MJIEnvNULL;
protected InstanceFieldInstruction() {}
protected InstanceFieldInstruction (String fieldName, String classType, String fieldDescriptor){
super(fieldName, classType, fieldDescriptor);
}
public FieldInfo getFieldInfo (FeatureExpr ctx) {
if (fi == null) {
ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(className);
if (ci != null) {
fi = ci.getInstanceField(fname);
}
}
return fi;
}
@Override
protected boolean isSkippedFinalField (ElementInfo ei) {
// cutting off finals might loose interesting defects where the
// reference escapes from a ctor that has a context switch before
// the field init. 'final' only means "can only be assigned once",
// it doesn't mean no read can happen before this assignment
if (skipFinals && fi.isFinal()) {
return true;
}
if (skipConstructedFinals && fi.isFinal() && ei.isConstructed()) {
return true;
}
return false;
}
protected boolean isSchedulingRelevant (ThreadInfo ti, int objRef) {
//--- configured override
FieldInfo fi = getFieldInfo(null);
if (fi.neverBreak()) {
// this should filter out the bulk in most real apps (library code)
return false;
} else if (fi.breakShared()){
return true;
}
ElementInfo ei = ti.getElementInfo(objRef);
if (ei.isImmutable() || isSkippedFinalField(ei)) {
return false;
}
if (!ei.isShared()){
return false;
}
if (!ti.hasOtherRunnables()) { // single threaded
return false;
}
//--- special code patterns
if (isMonitorEnterPrologue()) {
// if this is a GET followed by a MONITOR_ENTER then we just break on the monitor
return false;
}
if (fname.startsWith("this$")) {
// that one is an automatically created outer object reference in an inner class,
// it can't be set. Unfortunately, we don't have an 'immutable' attribute for
// fields, just objects, so we can't push it into class load time attributing.
// Must be filtered out before we call 'isLockProtected'
return false;
}
if (mi.isInit() && ti.useUnsharedInit()) {
// <2do> this should perhaps use the more expensive ti.isCtorOnStack(objref) to cover
// init methods called from the ctor
return false;
}
//--- from here on, we know this is a shared object that can be accessed concurrently
if (ti.usePorSyncDetection()) {
// this is a potentially more expensive test to identify fields
// that are protected by locks, for which get/putXX should not break.
// NOTE - here we get heuristic, and it is possible that we use
// assumptions that might later-on be violated (but not detected)
if (isLockProtected(ti, ei)) {
return false;
}
}
return true;
}
protected boolean isNewPorFieldBoundary (ThreadInfo ti, FieldInfo fi, int objRef) {
return (!ti.isFirstStepInsn()) && ti.usePorFieldBoundaries() && isSchedulingRelevant(ti, objRef);
}
/**
* NOTE - the return value is *only* valid in a instructionExecuted() context, since
* the same instruction can be executed from different threads
*/
public int getLastThis() {
return lastThis.getValue();
}
/**
* since this is based on getLastThis(), the same context restrictions apply
*/
public ElementInfo getLastElementInfo () {
if (lastThis.getValue() != MJIEnv.NULL) {
return VM.getVM().getHeap().get(lastThis.getValue()); // <2do> remove - should be in clients
}
return null;
}
/**
* this one can be used from a choiceGeneratorSet() or executeInstruction() context, since
* it peeks 'this' from the operand stack (enter didn't pop the value yet)
* it' less efficient than getLastElementInfo() from a instructionExecuted context
*/
public abstract ElementInfo peekElementInfo(ThreadInfo ti);
public String getFieldDescriptor () {
ElementInfo ei = getLastElementInfo();
FieldInfo fi = getFieldInfo(null);
return ei.toString() + '.' + fi.getName();
}
public void accept(InstructionVisitor insVisitor) {
insVisitor.visit(this);
}
}