Fixes default method resolution (#159)
[jpf-core.git] / src / main / gov / nasa / jpf / vm / NativeMethodInfo.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
19 package gov.nasa.jpf.vm;
20
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.JPFNativePeerException;
23 import gov.nasa.jpf.util.JPFLogger;
24
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27
28 /**
29  * a MethodInfo for a native peer executed method
30  */
31 public class NativeMethodInfo extends MethodInfo {
32
33   static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.NativePeer");
34
35   static final int  MAX_NARGS = 6;
36   static Object[][]  argCache;
37
38   static {
39     argCache = new Object[MAX_NARGS][];
40
41     for (int i = 0; i < MAX_NARGS; i++) {
42       argCache[i] = new Object[i];
43     }
44   }
45
46   protected Method mth; // the native method to enter in lieu
47   protected NativePeer peer;
48
49   public NativeMethodInfo (MethodInfo mi, Method mth, NativePeer peer){
50     super(mi);  // <2do> do we want any operands or locals?
51
52     this.peer = peer;
53     this.mth = mth;
54
55     ci.setNativeCallCode(this);
56   }
57
58   public void replace( MethodInfo mi){
59     mthTable.set(mi.globalId, this);
60     mi.ci.putDeclaredMethod(this);
61   }
62   
63   @Override
64   public boolean isUnresolvedNativeMethod() {
65     // we are already a NativeMethodInfo
66     return false;
67   }
68
69   @Override
70   public boolean isMJI () {
71     return true;
72   }
73
74   @Override
75   public boolean hasEmptyBody (){
76     // how would we know
77     return false;
78   }
79
80   @Override
81   public boolean isJPFExecutable(){
82     return true; // that's our only purpose in life
83   }
84
85   public NativePeer getNativePeer() {
86     return peer;
87   }
88
89   public Method getMethod() {
90     return mth;
91   }
92
93   @Override
94   public String getStackTraceSource() {
95     if (peer != null){
96       return peer.getPeerClassName();
97     } else {
98       return "no peer";
99     }
100   }
101
102   @Override
103   public int getLineNumber (Instruction pc) {
104     return -1; // we have no line numbers
105   }
106
107   public Instruction executeNative (ThreadInfo ti) {
108     Object   ret = null;
109     Object[] args = null;
110     MJIEnv   env = ti.getMJIEnv();
111         
112     NativeStackFrame nativeFrame = (NativeStackFrame)ti.getTopFrame();
113
114     env.setCallEnvironment(this);
115
116     if (isUnsatisfiedLinkError(env)) {
117       return ti.createAndThrowException("java.lang.UnsatisfiedLinkError",
118                                         "cannot find native " + ci.getName() + '.' + getName());
119     }
120
121     try {
122       args = nativeFrame.getArguments();
123
124       // this is the reflection call into the native peer
125       ret = mth.invoke(peer, args);
126
127       if (env.hasException()) {
128         // even though we should prefer throwing normal exceptionHandlers,
129         // sometimes it might be better/required to explicitly throw
130         // something that's not wrapped into a InvocationTargetException
131         // (e.g. InterruptedException), which is why there still is a
132         // MJIEnv.throwException()
133         return ti.throwException( env.popException());
134       }
135
136       StackFrame top = ti.getTopFrame();
137 //      if (top == nativeFrame){ // no roundtrips, straight return
138       if (top.originatesFrom(nativeFrame)){ // could have changed attributes
139         NativeStackFrame ntop = (NativeStackFrame)top;
140
141         if (env.isInvocationRepeated()){
142           // don't advance
143           return ntop.getPC();
144
145         } else {
146           // we don't have to do a ti.topClone() because the last insn left
147           // is NATIVERETURN. Even if a listener creates a CG on it, it won't
148           // modify its StackFrame, which is then popped anyways
149
150           ntop.setReturnValue(ret);
151           ntop.setReturnAttr(env.getReturnAttribute());
152
153           return ntop.getPC().getNext(); // that should be the NATIVERETURN
154         }
155
156       } else {
157         // direct calls from within the native method, i.e. nativeFrame is not
158         // on top anymore, but its current instruction (invoke) will be reexecuted
159         // because DirectCallStackFrames don't advance the pc of the new top top upon return
160         return top.getPC();
161       }
162
163     } catch (IllegalArgumentException iax) {
164       logger.warning(iax.toString());
165       return ti.createAndThrowException("java.lang.IllegalArgumentException",
166                                         "calling " + ci.getName() + '.' + getName());
167     } catch (IllegalAccessException ilax) {
168       logger.warning(ilax.toString());
169       return ti.createAndThrowException("java.lang.IllegalAccessException",
170                                         "calling " + ci.getName() + '.' + getName());
171     } catch (InvocationTargetException itx) {
172
173       // if loading a class throws an exception
174       if(itx.getTargetException() instanceof ClassInfoException) {
175         ClassInfoException cie = (ClassInfoException) itx.getTargetException();
176         return ti.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
177       }
178
179       if (itx.getTargetException() instanceof UncaughtException) {  // Native methods could 
180         throw (UncaughtException) itx.getTargetException();
181       } 
182        
183       // this will catch all exceptionHandlers thrown by the native method execution
184       // we don't try to hand them back to the application
185       throw new JPFNativePeerException("exception in native method "
186           + ci.getName() + '.' + getName(), itx.getTargetException());
187     }
188   }
189
190   protected boolean isUnsatisfiedLinkError(MJIEnv env){
191     return(mth == null);
192   }
193
194   /**
195    * Get and convert the native method parameters off the ThreadInfo stack.
196    * Use the MethodInfo parameter type info for this (not the reflect.Method
197    * type array), or otherwise we won't have any type check
198    */
199   protected Object[] getArguments (ThreadInfo ti) {
200     // these are just local refs to speed up
201     int      nArgs = getNumberOfArguments();
202     byte[]   argTypes = getArgumentTypes();
203
204     //Object[] a = getArgArray(nArgs + 2);
205     Object[] a = new Object[nArgs+2];
206
207     int      stackOffset;
208     int      i, j, k;
209     int      ival;
210     long     lval;
211     StackFrame caller = ti.getTopFrame();
212
213
214     for (i = 0, stackOffset = 0, j = nArgs + 1, k = nArgs - 1;
215          i < nArgs;
216          i++, j--, k--) {
217       switch (argTypes[k]) {
218       case Types.T_BOOLEAN:
219         ival = caller.peek(stackOffset);
220         a[j] = Boolean.valueOf(Types.intToBoolean(ival));
221
222         break;
223
224       case Types.T_BYTE:
225         ival = caller.peek(stackOffset);
226         a[j] = Byte.valueOf((byte) ival);
227
228         break;
229
230       case Types.T_CHAR:
231         ival = caller.peek(stackOffset);
232         a[j] = Character.valueOf((char) ival);
233
234         break;
235
236       case Types.T_SHORT:
237         ival = caller.peek(stackOffset);
238         a[j] = new Short((short) ival);
239
240         break;
241
242       case Types.T_INT:
243         ival = caller.peek(stackOffset);
244         a[j] = new Integer(ival);
245
246         break;
247
248       case Types.T_LONG:
249         lval = caller.peekLong(stackOffset);
250         stackOffset++; // 2 stack words
251         a[j] = new Long(lval);
252
253         break;
254
255       case Types.T_FLOAT:
256         ival = caller.peek(stackOffset);
257         a[j] = new Float(Types.intToFloat(ival));
258
259         break;
260
261       case Types.T_DOUBLE:
262         lval = caller.peekLong(stackOffset);
263         stackOffset++; // 2 stack words
264         a[j] = new Double(Types.longToDouble(lval));
265
266         break;
267
268       default:
269         // NOTE - we have to store T_REFERENCE as an Integer, because
270         // it shows up in our native method as an 'int'
271         ival = caller.peek(stackOffset);
272         a[j] = new Integer(ival);
273       }
274
275       stackOffset++;
276     }
277
278     //--- set  our standard MJI header arguments
279     if (isStatic()) {
280       a[1] = new Integer(ci.getClassObjectRef());
281     } else {
282       a[1] = new Integer(ti.getCalleeThis(this));
283     }
284
285     a[0] = ti.getMJIEnv();
286
287     return a;
288   }
289 }