Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / main / gov / nasa / jpf / vm / Instruction.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import gov.nasa.jpf.util.ObjectList;
21 import gov.nasa.jpf.util.Source;
22 import gov.nasa.jpf.vm.bytecode.InstructionInterface;
23
24
25
26 /**
27  * common root of all JPF bytecode instruction classes 
28  * 
29  */
30 public abstract class Instruction implements Cloneable, InstructionInterface {
31
32   protected int insnIndex;        // code[] index of instruction
33   protected int position;     // accumulated bytecode position (prev pos + prev bc-length)
34   protected MethodInfo mi;    // the method this insn belongs to
35
36   // property/mode specific attributes
37   protected Object attr;
38   
39   // this is for changing from InstructionInterface types to Instruction types
40   @Override
41   public Instruction asInstruction(){
42     return this;
43   }
44   
45   // to allow a classname and methodname context for each instruction
46   public void setContext(String className, String methodName, int lineNumber,
47           int offset) {
48   }
49
50   /**
51    * is this the first instruction in a method
52    */
53   @Override
54   public boolean isFirstInstruction() {
55     return (insnIndex == 0);
56   }
57
58
59   /**
60    * answer if this is a potential loop closing jump
61    */
62   @Override
63   public boolean isBackJump() {
64     return false;
65   }
66
67   /**
68    * is this instruction part of a monitorenter code pattern 
69    */
70   public boolean isMonitorEnterPrologue(){
71     return false;
72   }
73
74   /**
75    * is this one of our own, artificial insns?
76    */
77   @Override
78   public boolean isExtendedInstruction() {
79     return false;
80   }
81
82
83   @Override
84   public MethodInfo getMethodInfo() {
85     return mi;
86   }
87
88
89   /**
90    * that's used for explicit construction of MethodInfos (synthetic methods)
91    */
92   public void setMethodInfo(MethodInfo mi) {
93     this.mi = mi;
94   }
95
96   /**
97    * this returns the instruction at the following code insnIndex within the same
98    * method, which might or might not be the next one to enter (branches, overlay calls etc.).
99    */
100   @Override
101   public Instruction getNext() {
102     return mi.getInstruction(insnIndex + 1);
103   }
104
105   @Override
106   public int getInstructionIndex() {
107     return insnIndex;
108   }
109
110   @Override
111   public int getPosition() {
112     return position;
113   }
114
115   public void setLocation(int insnIdx, int pos) {
116     insnIndex = insnIdx;
117     position = pos;
118   }
119
120   /**
121    * return the length in bytes of this instruction.
122    * override if this is not 1
123    */
124   @Override
125   public int getLength() {
126     return 1;
127   }
128
129   @Override
130   public Instruction getPrev() {
131     if (insnIndex > 0) {
132       return mi.getInstruction(insnIndex - 1);
133     } else {
134       return null;
135     }
136   }
137
138   /**
139    * this is for listeners that process instructionExecuted(), but need to
140    * determine if there was a CG registration, an overlayed direct call
141    * (like clinit) etc.
142    * The easy case is the instruction not having been executed yet, in
143    * which case ti.getNextPC() == null
144    * There are two cases for re-execution: either nextPC was set to the
145    * same insn (which is what CG creators usually use), or somebody just
146    * pushed another stackframe that executes something which will return to the
147    * same insn (that is what automatic <clinit> calls and the like do - we call
148    * it overlays)
149    */
150   @Override
151   public boolean isCompleted(ThreadInfo ti) {
152     Instruction nextPc = ti.getNextPC();
153
154     if (nextPc == null) {
155       return ti.isTerminated();
156
157     } else {
158
159       return (nextPc != this) && (ti.getStackFrameExecuting(this, 1) == null);
160     }
161
162     // <2do> how do we account for exceptions? 
163   }
164
165   /**
166    * this method can be overridden if instruction classes have to store
167    * information for instructionExecuted() notifications, and this information
168    * should not be stored persistent to avoid memory leaks (e.g. via traces).
169    * Called by ThreadInfo.executeInstruction
170    */
171   public void cleanupTransients(){
172     // nothing here
173   }
174   
175   public boolean isSchedulingRelevant(SystemState ss, KernelState ks, ThreadInfo ti) {
176     return false;
177   }
178
179   /**
180    * this is the real workhorse
181    * returns next instruction to enter in this thread
182    * 
183    * <2do> it's unfortunate we roll every side effect into this method, because
184    * it diminishes the value of the 'executeInstruction' notification: all
185    * insns that require some sort of late binding (InvokeVirtual, GetField, ..)
186    * are not yet fully analyzable (e.g. the callee of InvokeVirtuals is not
187    * known yet), putting the burden of duplicating the related code of
188    * enter() in the listener. It would be better if we factor this
189    * 'prepareExecution' out of enter()
190    */
191   @Override
192   public abstract Instruction execute(ThreadInfo ti);
193
194   @Override
195   public String toString() {
196     return getMnemonic();
197   }
198
199   /**
200    * this can contain additional info that was gathered/cached during execution 
201    */
202   @Override
203   public String toPostExecString(){
204     return toString();
205   }
206   
207   @Override
208   public String getMnemonic() {
209     String s = getClass().getSimpleName();
210     return s.toLowerCase();
211   }
212
213   @Override
214   public int getLineNumber() {
215     return mi.getLineNumber(this);
216   }
217
218   @Override
219   public String getSourceLine() {
220     ClassInfo ci = mi.getClassInfo();
221     if (ci != null) {
222       int line = mi.getLineNumber(this);
223       String fileName = ci.getSourceFileName();
224
225       Source src = Source.getSource(fileName);
226       if (src != null) {
227         String srcLine = src.getLine(line);
228         if (srcLine != null) {
229           return srcLine;
230         }
231       }
232     }
233     
234     return null;
235   }
236
237   /**
238    * this is for debugging/logging if we always want something back telling
239    * us where this insn came from
240    */
241   public String getSourceOrLocation(){
242     ClassInfo ci = mi.getClassInfo();
243     if (ci != null) {
244       int line = mi.getLineNumber(this);
245       String file = ci.getSourceFileName();
246
247       Source src = Source.getSource(file);
248       if (src != null) {
249         String srcLine = src.getLine(line);
250         if (srcLine != null) {
251           return srcLine;
252         }
253       }
254
255       return "(" + file + ':' + line + ')'; // fallback
256
257     } else {
258       return "[synthetic] " + mi.getName();
259     }
260   }
261   
262   
263   /**
264    * this returns a "pathname:line" string
265    */
266   @Override
267   public String getFileLocation() {
268     ClassInfo ci = mi.getClassInfo();
269     if (ci != null) {
270       int line = mi.getLineNumber(this);
271       String fname = ci.getSourceFileName();
272       return (fname + ':' + line);
273     } else {
274       return "[synthetic] " + mi.getName();
275     }
276   }
277
278   /**
279    * this returns a "filename:line" string
280    */
281   @Override
282   public String getFilePos() {
283     String file = null;
284     int line = -1;
285     ClassInfo ci = mi.getClassInfo();
286
287     if (ci != null){
288       line = mi.getLineNumber(this);
289       file = ci.getSourceFileName();
290       if (file != null){
291         int i = file.lastIndexOf('/'); // ClassInfo.sourceFileName is using '/'
292         if (i >= 0) {
293           file = file.substring(i + 1);
294         }
295       }
296     }
297
298     if (file != null) {
299       if (line != -1){
300         return (file + ':' + line);
301       } else {
302         return file;
303       }
304     } else {
305       return ("pc " + position);
306     }
307   }
308
309   /**
310    * this returns a "class.method(line)" string
311    */
312   @Override
313   public String getSourceLocation() {
314     ClassInfo ci = mi.getClassInfo();
315
316     if (ci != null) {
317       String s = ci.getName() + '.' + mi.getName() +
318               '(' + getFilePos() + ')';
319       return s;
320
321     } else {
322       return null;
323     }
324   }
325
326   public void init(MethodInfo mi, int offset, int position) {
327     this.mi = mi;
328     this.insnIndex = offset;
329     this.position = position;
330   }
331
332   /**
333    * this is a misnomer - we actually push the clinit calls here in case
334    * we need some. 'causedClinitCalls' might be more appropriate, but it is
335    * used in a number of external projects
336    */
337   public boolean requiresClinitExecution(ThreadInfo ti, ClassInfo ci) {
338     return ci.initializeClass(ti);
339   }
340
341   /**
342    * this is returning the next Instruction to enter, to be called to obtain
343    * the return value of enter() if this is not a branch insn
344    *
345    * Be aware of that we might have had exceptions caused by our execution
346    * (-> lower frame), or we might have had overlaid calls (-> higher frame),
347    * i.e. we can't simply assume it's the following insn. We have to
348    * acquire this through the top frame of the ThreadInfo.
349    *
350    * note: the System.exit() problem should be gone, now that it is implemented
351    * as ThreadInfo state (TERMINATED), rather than purged stacks
352    */
353   @Override
354   public Instruction getNext (ThreadInfo ti) {
355     return ti.getPC().getNext();
356   }
357
358   
359   //--- the generic attribute API
360
361   @Override
362   public boolean hasAttr () {
363     return (attr != null);
364   }
365
366   @Override
367   public boolean hasAttr (Class<?> attrType){
368     return ObjectList.containsType(attr, attrType);
369   }
370
371   /**
372    * this returns all of them - use either if you know there will be only
373    * one attribute at a time, or check/process result with ObjectList
374    */
375   @Override
376   public Object getAttr(){
377     return attr;
378   }
379
380   /**
381    * this replaces all of them - use only if you know 
382    *  - there will be only one attribute at a time
383    *  - you obtained the value you set by a previous getXAttr()
384    *  - you constructed a multi value list with ObjectList.createList()
385    */
386   @Override
387   public void setAttr (Object a){
388     attr = ObjectList.set(attr, a);    
389   }
390
391   @Override
392   public void addAttr (Object a){
393     attr = ObjectList.add(attr, a);
394   }
395
396   @Override
397   public void removeAttr (Object a){
398     attr = ObjectList.remove(attr, a);
399   }
400
401   @Override
402   public void replaceAttr (Object oldAttr, Object newAttr){
403     attr = ObjectList.replace(attr, oldAttr, newAttr);
404   }
405
406   /**
407    * this only returns the first attr of this type, there can be more
408    * if you don't use client private types or the provided type is too general
409    */
410   @Override
411   public <T> T getAttr (Class<T> attrType) {
412     return ObjectList.getFirst(attr, attrType);
413   }
414
415   @Override
416   public <T> T getNextAttr (Class<T> attrType, Object prev) {
417     return ObjectList.getNext(attr, attrType, prev);
418   }
419
420   @Override
421   public ObjectList.Iterator attrIterator(){
422     return ObjectList.iterator(attr);
423   }
424   
425   @Override
426   public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType){
427     return ObjectList.typedIterator(attr, attrType);
428   }
429
430   // -- end attrs --
431
432   /**
433    * this is overridden by any Instruction that use a cache for class or 
434    * method to provide a type safe cloning
435    */
436   public Instruction typeSafeClone(MethodInfo mi) {
437     Instruction clone = null;
438
439     try {
440       clone = (Instruction) super.clone();
441
442       // reset the method that this insn belongs to
443       clone.mi = mi;
444     } catch (CloneNotSupportedException e) {
445       e.printStackTrace();
446     }
447
448     return clone;
449   }
450 }