// // 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 java.util.Iterator; import java.util.function.BiFunction; 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.jvm.JVMInstruction; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.VM; /** * abstraction for the various return instructions */ public abstract class ReturnInstruction extends JVMInstruction implements gov.nasa.jpf.vm.ReturnInstruction { // to store where we came from protected StackFrame returnFrame; abstract public int getReturnTypeSize(); abstract protected Object getReturnedOperandAttr(FeatureExpr ctx, StackFrame frame); // note these are only callable from within the same enter - thread interleavings // would cause races abstract protected void getAndSaveReturnValue (StackFrame frame, FeatureExpr ctx); abstract protected void pushReturnValue (FeatureExpr ctx, StackFrame frame); public abstract Object getReturnValue(FeatureExpr ctx, ThreadInfo ti); public StackFrame getReturnFrame() { return returnFrame; } public void setReturnFrame(StackFrame frame){ returnFrame = frame; } /** * this is important since keeping the StackFrame alive would be a major * memory leak */ @Override public void cleanupTransients(){ returnFrame = null; } //--- attribute accessors // the accessors are here to save the client some effort regarding the // return type (slot size). // Since these are all public methods that can be called by listeners, // we stick to the ThreadInfo argument public boolean hasReturnAttr (ThreadInfo ti){ StackFrame frame = ti.getTopFrame(); return frame.hasOperandAttr(); } public boolean hasReturnAttr (ThreadInfo ti, Class<?> type){ StackFrame frame = ti.getTopFrame(); return frame.hasOperandAttr(type); } /** * this returns all of them - use either if you know there will be only * one attribute at a time, or check/process result with ObjectList * * obviously, this only makes sense from an instructionExecuted(), since * the value is pushed during the enter(). Use ObjectList to access values */ public Object getReturnAttr (ThreadInfo ti){ StackFrame frame = ti.getTopFrame(); return frame.getOperandAttr(FeatureExprFactory.True()); } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() * * we don't clone since pushing a return value already changed the caller frame */ public void setReturnAttr (ThreadInfo ti, Object a){ StackFrame frame = ti.getModifiableTopFrame(); frame.setOperandAttr(a); } public void addReturnAttr (ThreadInfo ti, Object attr){ StackFrame frame = ti.getModifiableTopFrame(); frame.addOperandAttr(attr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ public <T> T getReturnAttr (ThreadInfo ti, Class<T> type){ StackFrame frame = ti.getTopFrame(); return frame.getOperandAttr(type); } public <T> T getNextReturnAttr (ThreadInfo ti, Class<T> type, Object prev){ StackFrame frame = ti.getTopFrame(); return frame.getNextOperandAttr(type, prev); } public Iterator<?> returnAttrIterator (ThreadInfo ti){ StackFrame frame = ti.getTopFrame(); return frame.operandAttrIterator(); } public <T> Iterator<T> returnAttrIterator (ThreadInfo ti, Class<T> type){ StackFrame frame = ti.getTopFrame(); return frame.operandAttrIterator(type); } // -- end attribute accessors -- public Conditional<Instruction> execute (FeatureExpr ctx, ThreadInfo ti) { if (!ti.isFirstStepInsn()) { ti.leave(); // takes care of unlocking before potentially creating a CG if (mi.isSynchronized()) { final Conditional<Integer> objref = mi.isStatic() ? One.valueOf(mi.getClassInfo().getClassObjectRef()) : ti.getThis(); if (objref.isOne()) { ElementInfo ei = ti.getElementInfo(objref.getValue()); if (ei.getLockCount() == 0){ ei = ei.getInstanceWithUpdatedSharedness(ti); if (ei.isShared()) { VM vm = ti.getVM(); ChoiceGenerator<ThreadInfo> cg = vm.getSchedulerFactory().createSyncMethodExitCG(ei, ti); if (cg != null) { if (vm.setNextChoiceGenerator(cg)) { ti.skipInstructionLogging(); return new One<Instruction>(this); // re-enter } } } } } else { objref.map(ref -> { ElementInfo ei = ti.getElementInfo(ref); if (ei.getLockCount() == 0) { ei = ei.getInstanceWithUpdatedSharedness(ti); if (ei.isShared()) { VM vm = ti.getVM(); ChoiceGenerator<ThreadInfo> cg = vm.getSchedulerFactory().createSyncMethodExitCG(ei, ti); if (cg != null) { if (vm.setNextChoiceGenerator(cg)) { ti.skipInstructionLogging(); // return new One<Instruction>(this); // re-enter throw new RuntimeException("incomplete lift"); // XXX shouldn't mater for vae } } } } return null; }); } } } StackFrame frame = ti.getModifiableTopFrame(); returnFrame = frame; Object attr = getReturnedOperandAttr(ctx, frame); // the return attr - get this before we pop getAndSaveReturnValue(frame, ctx); // note that this is never the first frame, since we start all threads (incl. main) // through a direct call frame = ti.popAndGetModifiableTopFrame(ctx); // remove args, push return value and continue with next insn // (DirectCallStackFrames don't use this) frame.removeArguments(ctx, mi); pushReturnValue(ctx, frame); if (attr != null) { setReturnAttr(ti, attr); } return getNext(ctx, ti); } public void accept(InstructionVisitor insVisitor) { insVisitor.visit(this); } @Override public boolean equals(Object o) { if (o == null) { return false; } if (o == this) { return true; } if (o.getClass().equals(getClass()) && o instanceof Instruction) { if (getMethodInfo() == ((Instruction)o).getMethodInfo()) { return true; } } return false; } @Override public int hashCode() { return getMethodInfo().hashCode() + getClass().hashCode(); } @Override public Conditional<Instruction> getNext(final FeatureExpr ctx, ThreadInfo ti) { // we have to find the corresponding instruction at the callee method return ti.getPC().mapf(ctx, new BiFunction<FeatureExpr, Instruction, Conditional<Instruction>>() { @Override public Conditional<Instruction> apply(final FeatureExpr f, final Instruction y) { return ChoiceFactory.create(ctx, new One<>(y.getNext()), new One<>(y)); } }).simplify(); } }