// // 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 fileName 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.vm; import cmu.conditional.Conditional; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.util.ObjectList; import gov.nasa.jpf.util.Source; /** * common root of all JPF bytecode instruction classes * */ public abstract class Instruction implements Cloneable { public int insnIndex; // code[] index of instruction protected int position; // accumulated bytecode position (prev pos + prev // bc-length) protected MethodInfo mi; // the method this insn belongs to // property/mode specific attributes protected Object attr; abstract public int getByteCode(); // to allow a classname and methodname context for each instruction public void setContext(String className, String methodName, int lineNumber, int offset) { } /** * is this the first instruction in a method */ public boolean isFirstInstruction() { return (insnIndex == 0); } /** * answer if this is a potential loop closing jump */ public boolean isBackJump() { return false; } /** * is this one of our own, artificial insns? */ public boolean isExtendedInstruction() { return false; } public MethodInfo getMethodInfo() { return mi; } /** * that's used for explicit construction of MethodInfos (synthetic methods) */ public void setMethodInfo(MethodInfo mi) { this.mi = mi; } /** * this returns the instruction at the following code insnIndex within the * same method, which might or might not be the next one to enter (branches, * overlay calls etc.). */ public Instruction getNext() { return mi.getInstruction(insnIndex + 1); } public int getInstructionIndex() { return insnIndex; } public int getPosition() { return position; } public void setLocation(int insnIdx, int pos) { insnIndex = insnIdx; position = pos; } /** * return the length in bytes of this instruction. override if this is not 1 */ public int getLength() { return 1; } public Instruction getPrev() { if (insnIndex > 0) { return mi.getInstruction(insnIndex - 1); } else { return null; } } /** * this is for listeners that process instructionExecuted(), but need to * determine if there was a CG registration, an overlayed direct call (like * clinit) etc. The easy case is the instruction not having been executed * yet, in which case ti.getNextPC() == null There are two cases for * re-execution: either nextPC was set to the same insn (which is what CG * creators usually use), or somebody just pushed another stackframe that * executes something which will return to the same insn (that is what * automatic <clinit> calls and the like do - we call it overlays) */ public boolean isCompleted(ThreadInfo ti) { Instruction nextPc = ti.getNextPC().getValue(); if (nextPc == null) { return ti.isTerminated(); } else { return (nextPc != this) && (ti.getStackFrameExecuting(this, 1) == null); } // <2do> how do we account for exceptions? } /** * this method can be overridden if instruction classes have to store * information for instructionExecuted() notifications, and this information * should not be stored persistent to avoid memory leaks (e.g. via traces). * Called by ThreadInfo.executeInstruction */ public void cleanupTransients() { // nothing here } public boolean isSchedulingRelevant(SystemState ss, KernelState ks, ThreadInfo ti) { return false; } /** * this is the real workhorse returns next instruction to enter in this * thread * * <2do> it's unfortunate we roll every side effect into this method, * because it diminishes the value of the 'executeInstruction' notification: * all insns that require some sort of late binding (InvokeVirtual, * GetField, ..) are not yet fully analyzable (e.g. the callee of * InvokeVirtuals is not known yet), putting the burden of duplicating the * related code of enter() in the listener. It would be better if we factor * this 'prepareExecution' out of enter() * */ public abstract Conditional<Instruction> execute(FeatureExpr ctx, ThreadInfo ti); public String toString() { return mi.getFullName() + " " + getMnemonic() + " " + getFilePos();//getPosition(); } public String getMnemonic() { String s = getClass().getSimpleName(); return s.toLowerCase(); } public int getLineNumber() { return mi.getLineNumber(this); } public String getSourceLine() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String fileName = ci.getSourceFileName(); Source src = Source.getSource(fileName); if (src != null) { String srcLine = src.getLine(line); if (srcLine != null) { return srcLine; } } } return null; } /** * this is for debugging/logging if we always want something back telling us * where this insn came from */ public String getSourceOrLocation() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String file = ci.getSourceFileName(); Source src = Source.getSource(file); if (src != null) { String srcLine = src.getLine(line); if (srcLine != null) { return srcLine; } } return "(" + file + ':' + line + ')'; // fallback } else { return "[synthetic] " + mi.getName(); } } /** * this returns a "pathname:line" string */ public String getFileLocation() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String fname = ci.getSourceFileName(); return (fname + ':' + line); } else { return "[synthetic] " + mi.getName(); } } /** * this returns a "filename:line" string */ public String getFilePos() { String file = null; int line = -1; if (mi == null) { return ""; } ClassInfo ci = mi.getClassInfo(); if (ci != null) { line = mi.getLineNumber(this); file = ci.getSourceFileName(); if (file != null) { int i = file.lastIndexOf('/'); // ClassInfo.sourceFileName is // using '/' if (i >= 0) { file = file.substring(i + 1); } } } if (file != null) { if (line != -1) { return (file + ':' + line); } else { return file; } } else { return ("pc " + position); } } /** * this returns a "class.method(line)" string */ public String getSourceLocation() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { String s = ci.getName() + '.' + mi.getName() + '(' + getFilePos() + ')'; return s; } else { return null; } } public void init(MethodInfo mi, int offset, int position) { this.mi = mi; this.insnIndex = offset; this.position = position; } /** * this is a misnomer - we actually push the clinit calls here in case we * need some. 'causedClinitCalls' might be more appropriate, but it is used * in a number of external projects */ public boolean requiresClinitExecution(FeatureExpr ctx, ThreadInfo ti, ClassInfo ci) { return ci.pushRequiredClinits(ctx, ti); } /** * this is returning the next Instruction to enter, to be called to obtain * the return value of enter() if this is not a branch insn * * Be aware of that we might have had exceptions caused by our execution (-> * lower frame), or we might have had overlaid calls (-> higher frame), i.e. * we can't simply assume it's the following insn. We have to acquire this * through the top frame of the ThreadInfo. * * note: the System.exit() problem should be gone, now that it is * implemented as ThreadInfo state (TERMINATED), rather than purged stacks */ public Conditional<Instruction> getNext(final FeatureExpr ctx, ThreadInfo ti) { return new One<>(getNext()); } // --- the generic attribute API public boolean hasAttr() { return (attr != null); } public boolean hasAttr(Class<?> attrType) { return ObjectList.containsType(attr, attrType); } /** * 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 */ public Object getAttr() { return attr; } /** * 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() */ public void setAttr(Object a) { attr = ObjectList.set(attr, a); } public void addAttr(Object a) { attr = ObjectList.add(attr, a); } public void removeAttr(Object a) { attr = ObjectList.remove(attr, a); } public void replaceAttr(Object oldAttr, Object newAttr) { attr = ObjectList.replace(attr, oldAttr, newAttr); } /** * 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 getAttr(Class<T> attrType) { return ObjectList.getFirst(attr, attrType); } public <T> T getNextAttr(Class<T> attrType, Object prev) { return ObjectList.getNext(attr, attrType, prev); } public ObjectList.Iterator attrIterator() { return ObjectList.iterator(attr); } public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType) { return ObjectList.typedIterator(attr, attrType); } // -- end attrs -- /** * this is overridden by any Instruction that use a cache for class or * method to provide a type safe cloning */ public Instruction typeSafeClone(MethodInfo mi) { Instruction clone = null; try { clone = (Instruction) super.clone(); // reset the method that this insn belongs to clone.mi = mi; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } @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 (insnIndex == ((Instruction)o).insnIndex && // position == ((Instruction)o).position && // mi == ((Instruction)o).mi) { // return true; // } // } return false; } @Override public int hashCode() { int hash = 7; hash = hash * 31 + insnIndex; hash = hash * 31 + position; return hash * 31; } }