Initial import
[jpf-core.git] / src / main / gov / nasa / jpf / jvm / bytecode / VirtualInvocation.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.jvm.bytecode;
19
20 import gov.nasa.jpf.vm.ClassChangeException;
21 import gov.nasa.jpf.vm.ClassInfo;
22 import gov.nasa.jpf.vm.ElementInfo;
23 import gov.nasa.jpf.vm.Instruction;
24 import gov.nasa.jpf.vm.MJIEnv;
25 import gov.nasa.jpf.vm.MethodInfo;
26 import gov.nasa.jpf.vm.ThreadInfo;
27
28
29 /**
30  * a base class for virtual call instructions
31  */
32 public abstract class VirtualInvocation extends InstanceInvocation {
33
34   // note that we can't null laseCalleeCi and invokedMethod in cleanupTransients()
35   // since we use it as an internal optimization (loops with repeated calls on the
36   // same object)
37   
38   ClassInfo lastCalleeCi; // cached for performance
39
40   protected VirtualInvocation () {}
41
42   protected VirtualInvocation (String clsDescriptor, String methodName, String signature){
43     super(clsDescriptor, methodName, signature);
44   }
45
46   @Override
47   public String toPostExecString(){
48     StringBuilder sb = new StringBuilder();
49     sb.append(getMnemonic());
50     sb.append(' ');
51     
52     if (invokedMethod != null){
53       sb.append( lastCalleeCi.getName());
54       sb.append('@');
55       sb.append(Integer.toHexString(lastObj));
56       sb.append('.');
57       sb.append(invokedMethod.getUniqueName());
58
59       if (invokedMethod.isMJI()){
60         sb.append(" [native]");
61       }
62       
63     } else { // something went wrong, the method wasn't found
64       if (lastCalleeCi != null){
65         sb.append( lastCalleeCi.getName());
66       } else {
67         sb.append(cname);
68       }
69       sb.append('@');
70       if (lastObj == MJIEnv.NULL){
71         sb.append("<null>");
72       } else {
73         sb.append(Integer.toHexString(lastObj));
74       }
75       sb.append('.');
76       sb.append(mname);
77       sb.append(signature);
78       sb.append(" (?)");
79     }
80     
81     return sb.toString();
82   }
83   
84   @Override
85   public Instruction execute (ThreadInfo ti) {
86     int objRef = ti.getCalleeThis(getArgSize());
87     MethodInfo callee;
88
89     if (objRef == MJIEnv.NULL) {
90       lastObj = MJIEnv.NULL;
91       return ti.createAndThrowException("java.lang.NullPointerException", "Calling '" + mname + "' on null object");
92     }
93
94     try {
95       callee = getInvokedMethod(ti, objRef);
96     } catch (ClassChangeException ccx){
97       return ti.createAndThrowException("java.lang.IncompatibleClassChangeError", ccx.getMessage());
98     }
99     
100     ElementInfo ei = ti.getElementInfo(objRef);
101     
102     if (callee == null) {
103       String clsName = ti.getClassInfo(objRef).getName();
104       return ti.createAndThrowException("java.lang.NoSuchMethodError", clsName + '.' + mname);
105     } else {
106       if (callee.isAbstract()){
107         return ti.createAndThrowException("java.lang.AbstractMethodError", callee.getFullName() + ", object: " + ei);
108       }
109     }
110
111     if (callee.isSynchronized()) {
112       ei = ti.getScheduler().updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
113       if (reschedulesLockAcquisition(ti, ei)){
114         return this;
115       }
116     }
117
118     setupCallee( ti, callee); // this creates, initializes and pushes the callee StackFrame
119
120     return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception
121   }
122   
123   /**
124    * If the current thread already owns the lock, then the current thread can go on.
125    * For example, this is a recursive acquisition.
126    */
127   protected boolean isLockOwner(ThreadInfo ti, ElementInfo ei) {
128     return ei.getLockingThread() == ti;
129   }
130
131   /**
132    * If the object will still be owned, then the current thread can go on.
133    * For example, all but the last monitorexit for the object.
134    */
135   protected boolean isLastUnlock(ElementInfo ei) {
136     return ei.getLockCount() == 1;
137   }
138
139
140   @Override
141   public MethodInfo getInvokedMethod(ThreadInfo ti){
142     int objRef;
143
144     if (ti.getNextPC() == null){ // this is pre-exec
145       objRef = ti.getCalleeThis(getArgSize());
146     } else {                     // this is post-exec
147       objRef = lastObj;
148     }
149
150     return getInvokedMethod(ti, objRef);
151   }
152
153   public MethodInfo getInvokedMethod (ThreadInfo ti, int objRef) {
154
155     if (objRef != MJIEnv.NULL) {
156       lastObj = objRef;
157
158       ClassInfo cci = ti.getClassInfo(objRef);
159
160       if (lastCalleeCi != cci) { // callee ClassInfo has changed
161         lastCalleeCi = cci;
162         invokedMethod = cci.getMethod(mname, true);
163
164         if (invokedMethod == null) {
165           invokedMethod = cci.getDefaultMethod(mname);
166                     
167           if (invokedMethod == null){
168             lastObj = MJIEnv.NULL;
169             lastCalleeCi = null;
170           }
171         }
172       }
173
174     } else {
175       lastObj = MJIEnv.NULL;
176       lastCalleeCi = null;
177       invokedMethod = null;
178     }
179
180     return invokedMethod;
181   }
182
183   @Override
184   public Object getFieldValue (String id, ThreadInfo ti){
185     int objRef = getCalleeThis(ti);
186     ElementInfo ei = ti.getElementInfo(objRef);
187
188     Object v = ei.getFieldValueObject(id);
189
190     if (v == null){ // try a static field
191       v = ei.getClassInfo().getStaticFieldValueObject(id);
192     }
193
194     return v;
195   }
196   
197   @Override
198   public void accept(JVMInstructionVisitor insVisitor) {
199           insVisitor.visit(this);
200   }
201
202   @Override
203   public Instruction typeSafeClone(MethodInfo clonedMethod) {
204     VirtualInvocation clone = null;
205
206     try {
207       clone = (VirtualInvocation) super.clone();
208
209       // reset the method that this insn belongs to
210       clone.mi = clonedMethod;
211
212       clone.lastCalleeCi = null;
213       clone.invokedMethod = null;
214     } catch (CloneNotSupportedException e) {
215       e.printStackTrace();
216     }
217
218     return clone;
219   }
220 }