// // 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.ChoiceFactory; import cmu.conditional.Conditional; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.Config; import gov.nasa.jpf.jvm.JVMInstruction; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.FieldLockInfo; import gov.nasa.jpf.vm.FieldLockInfoFactory; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.Types; import gov.nasa.jpf.vm.VM; /** * parent class for PUT/GET FIELD/STATIC insns * * <2do> there is a inheritance level missing to deal with instance/static * fields - w/o the instance/static helper methods we would have to duplicate * code in the getters/setters */ public abstract class FieldInstruction extends JVMInstruction implements VariableAccessor { //--- vm.por.sync_detection related settings static FieldLockInfoFactory fliFactory; static boolean skipFinals; // do we ignore final fields for POR static boolean skipStaticFinals; // do we ignore static final fields for POR static boolean skipConstructedFinals; // do we ignore final fields for POR after the object's constructor has finished? protected String fname; protected String className; protected String varId; protected FieldInfo fi; // lazy eval, hence not public protected int size; // is it a word or a double word field protected boolean isReferenceField; protected Conditional<?> lastValue; public static void init (Config config) { if (config.getBoolean("vm.por")) { skipFinals = config.getBoolean("vm.por.skip_finals", true); skipStaticFinals = config.getBoolean("vm.por.skip_static_finals", false); skipConstructedFinals = config.getBoolean("vm.por.skip_constructed_finals", false); if (config.getBoolean("vm.por.sync_detection")) { fliFactory = config.getEssentialInstance("vm.por.fli_factory.class", FieldLockInfoFactory.class); } } } protected FieldInstruction() {} protected FieldInstruction(String name, String clsName, String fieldDescriptor){ fname = name; className = Types.getClassNameFromTypeName(clsName); isReferenceField = Types.isReferenceSignature(fieldDescriptor); size = Types.getTypeSize(fieldDescriptor); } public String getClassName(){ return className; } public String getFieldName(){ return fname; } /** * only defined in instructionExecuted() notification context */ public long getLastValue() { if (lastValue.getValue() instanceof Long) { return (Long)lastValue.getValue(); } throw new RuntimeException(lastValue.getValue() + " not instance of Long"); } public abstract boolean isRead(); public abstract FieldInfo getFieldInfo (FeatureExpr ctx); // that's for an instructionExecuted() context public abstract ElementInfo getLastElementInfo(); // that's for an executeInstruction() or choiceGeneratorSet() context public abstract ElementInfo peekElementInfo (ThreadInfo ti); // pop operands for a 1 slot value protected abstract void popOperands1( FeatureExpr ctx, StackFrame frame); // pop operands for a 2 slot value protected abstract void popOperands2( FeatureExpr ctx, StackFrame frame); protected abstract boolean isSkippedFinalField (ElementInfo ei); public boolean isReferenceField () { return isReferenceField; } protected Conditional<Instruction> put1 (FeatureExpr ctx, ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner, boolean initStatic) { Object attr = frame.getOperandAttr(ctx); Conditional<Integer> val; Conditional<Integer> field = eiFieldOwner.get1SlotField(fi); if (initStatic) { /* * static fields need to be initialized for all contexts * because the static class objects are only initialized once */ val = frame.peek(ctx); } else { if (Conditional.isTautology(ctx)) { val = frame.peek(ctx); } else { val = ChoiceFactory.create(ctx, frame.peek(ctx), field).simplify(); } } lastValue = val; // we only have to modify the field owner if the values have changed, and only // if this is a modified reference do we might have to potential exposure re-enter if ((!field.simplify(ctx).equals(val.simplify(ctx))) || (eiFieldOwner.getFieldAttr(fi) != attr)) { ti.coverage.coverField(ctx, eiFieldOwner, val, field, frame, ti, fi); eiFieldOwner = eiFieldOwner.getModifiableInstance(); if (fi.isReference()) { if (initStatic) { eiFieldOwner.setReferenceField(FeatureExprFactory.True(), fi, val); } else { eiFieldOwner.setReferenceField(ctx, fi, val); } // this is kind of policy, but it seems more natural to overwrite instead of accumulate // (if we want to accumulate, this has to happen in ElementInfo/Fields) eiFieldOwner.setFieldAttr(fi, attr); // check if this might expose a previously unshared object if (ti.useBreakOnExposure()) { if (ti.isFirstStepInsn()) { // no use to break for exposure if we didn't break on shared field access ElementInfo eiFieldValue = ti.getElementInfo(val.simplify(ctx).getValue()); // note there is no point re-exposing if the object already got exposed along the same path. In fact, // without state matching this can cause endless-loops if ((eiFieldValue != null) && !eiFieldValue.isExposed() && !eiFieldValue.isReferencedBySameThreads(eiFieldOwner)) { // this is a potential exposure point, re-enter AFTER having done the assignment, // but BEFORE popping the operand stack // Note this doesn't make the referenced object shared yet, it just makes it accessible through an already shared // object (the field owner) and it might become shared in the future eiFieldValue.setExposed( ti, eiFieldOwner); if (createAndSetSharedObjectExposureCG(eiFieldValue, ti)) { return new One<Instruction>(this); } } } } } else { // not a reference, nothing exposed eiFieldOwner.set1SlotField(ctx, fi, val); eiFieldOwner.setFieldAttr(fi, attr); // see above about overwrite vs. accumulation } } frame = ti.getModifiableTopFrame(); // now we have to modify it popOperands1(ctx, frame); // .. val => .. return getNext(ctx, ti); } protected Conditional<Instruction> put2 (FeatureExpr ctx, ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner) { Object attr = frame.getLongOperandAttr(ctx); Conditional<Long> val = frame.peekLong(ctx); lastValue = val; Conditional<Long> field = eiFieldOwner.get2SlotField(fi); if ((!field.simplify(ctx).equals(val)) || (eiFieldOwner.getFieldAttr(fi) != attr)) { ti.coverage.coverField(ctx, eiFieldOwner, val, field, frame, ti, fi); eiFieldOwner = eiFieldOwner.getModifiableInstance(); eiFieldOwner.set2SlotField(ctx, fi, val); // see put1() reg. overwrite vs. accumulation eiFieldOwner.setFieldAttr(fi, attr); } frame = ti.getModifiableTopFrame(); // now we have to modify it popOperands2(ctx, frame); // .. highVal,lowVal => .. return getNext(ctx, ti); } protected Conditional<Instruction> put (FeatureExpr ctx, ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner, boolean initStatic) { if (size == 1) { return put1(ctx, ti, frame, eiFieldOwner, initStatic); } else { return put2(ctx, ti, frame, eiFieldOwner); } } public int getFieldSize() { return size; } public String getId(ElementInfo ei) { // <2do> - OUTCH, should be optimized (so far, it's only called during reporting) if (ei != null){ return (ei.toString() + '.' + fname); } else { return ("?." + fname); } } public String getVariableId () { if (varId == null) { varId = className + '.' + fname; } return varId; } /** * is this field supposed to be protected by a lock? * this only gets called if on-the-fly POR is in effect * * NOTE this has the side effect of setting FieldLockInfos, which * in turn can be heuristic and volatile (i.e. might force an * object to stay in memory just because we have to detect * subsequent lock assumption violations */ protected boolean isLockProtected (ThreadInfo ti, ElementInfo ei) { FieldInfo fi = getFieldInfo(null); // so that we make sure it's computed FieldLockInfo flInfo = ei.getFieldLockInfo(fi); FieldLockInfo flInfoNext; if (flInfo == null) { flInfoNext = fliFactory.createFieldLockInfo(ti, ei, fi); ei.setFieldLockInfo(fi, flInfoNext); } else { flInfoNext = flInfo.checkProtection(ti,ei,fi); if (flInfo != flInfoNext) { ei.setFieldLockInfo(fi, flInfoNext); } } return flInfoNext.isProtected(); } /** * do a little bytecode pattern analysis on the fly, to find out if a * GETFIELD or GETSTATIC is just part of a "..synchronized (obj) {..} .." * pattern, which usually translates into some * ... * getfield * dup * [astore] * monitorenter * ... * * pattern. If it does, there is no need to break the transition. * * <2do> We might want to extend this in the future to also cover sync on * local vars, like "Object o = myField; synchronized(o){..}..", but then * the check becomes more expensive since we get interspersed aload/astore * insns, and some of the locals could be used outside the sync block. Not * sure if it buys much on the bottom line * * <2do> does this rely on javac code patterns? The dup/astore could * lead to subsequent use of the object reference w/o corresponding get/putfield * insns (if it's not a volatile), but this access would be either a call * or a get/putfield on a share object, i.e. would be checked separately */ protected boolean isMonitorEnterPrologue () { Instruction[] code = mi.getInstructions(); int off = insnIndex+1; if (off < code.length-3) { // we don't reach out further than 3 instructions if (code[off] instanceof DUP) { off++; if (code[off] instanceof ASTORE) { off++; } if (code[off] instanceof MONITORENTER) { return true; } } } return false; // if in doubt, we break the transition } protected boolean createAndSetSharedFieldAccessCG ( ElementInfo eiFieldOwner, ThreadInfo ti) { VM vm = ti.getVM(); ChoiceGenerator<?> cg = vm.getSchedulerFactory().createSharedFieldAccessCG(eiFieldOwner, ti); if (cg != null) { if (vm.setNextChoiceGenerator(cg)){ ti.skipInstructionLogging(); // <2do> Hmm, might be more confusing not to see it return true; } } return false; } protected boolean createAndSetSharedObjectExposureCG ( ElementInfo eiFieldValue, ThreadInfo ti) { VM vm = ti.getVM(); ChoiceGenerator<?> cg = vm.getSchedulerFactory().createSharedObjectExposureCG(eiFieldValue, ti); if (cg != null) { if (vm.setNextChoiceGenerator(cg)){ ti.skipInstructionLogging(); // <2do> Hmm, might be more confusing not to see it return true; } } return false; } /** * for explicit construction */ public void setField(String fname, String fclsName) { this.fname = fname; this.className = fclsName; if (fclsName.equals("long") || fclsName.equals("double")) { this.size = 2; this.isReferenceField = false; } else { this.size = 1; if (fclsName.equals("boolean") || fclsName.equals("byte") || fclsName.equals("char") || fclsName.equals("short") || fclsName.equals("int")) { this.isReferenceField = false; } else { this.isReferenceField = true; } } } public void accept(InstructionVisitor insVisitor) { insVisitor.visit(this); } }