2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
19 package gov.nasa.jpf.vm;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.JPFNativePeerException;
23 import gov.nasa.jpf.util.JPFLogger;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
29 * a MethodInfo for a native peer executed method
31 public class NativeMethodInfo extends MethodInfo {
33 static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.NativePeer");
35 static final int MAX_NARGS = 6;
36 static Object[][] argCache;
39 argCache = new Object[MAX_NARGS][];
41 for (int i = 0; i < MAX_NARGS; i++) {
42 argCache[i] = new Object[i];
46 protected Method mth; // the native method to enter in lieu
47 protected NativePeer peer;
49 public NativeMethodInfo (MethodInfo mi, Method mth, NativePeer peer){
50 super(mi); // <2do> do we want any operands or locals?
55 ci.setNativeCallCode(this);
58 public void replace( MethodInfo mi){
59 mthTable.set(mi.globalId, this);
60 mi.ci.putDeclaredMethod(this);
64 public boolean isUnresolvedNativeMethod() {
65 // we are already a NativeMethodInfo
70 public boolean isMJI () {
75 public boolean hasEmptyBody (){
81 public boolean isJPFExecutable(){
82 return true; // that's our only purpose in life
85 public NativePeer getNativePeer() {
89 public Method getMethod() {
94 public String getStackTraceSource() {
96 return peer.getPeerClassName();
103 public int getLineNumber (Instruction pc) {
104 return -1; // we have no line numbers
107 public Instruction executeNative (ThreadInfo ti) {
109 Object[] args = null;
110 MJIEnv env = ti.getMJIEnv();
112 NativeStackFrame nativeFrame = (NativeStackFrame)ti.getTopFrame();
114 env.setCallEnvironment(this);
116 if (isUnsatisfiedLinkError(env)) {
117 return ti.createAndThrowException("java.lang.UnsatisfiedLinkError",
118 "cannot find native " + ci.getName() + '.' + getName());
122 args = nativeFrame.getArguments();
124 // this is the reflection call into the native peer
125 ret = mth.invoke(peer, args);
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());
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;
141 if (env.isInvocationRepeated()){
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
150 ntop.setReturnValue(ret);
151 ntop.setReturnAttr(env.getReturnAttribute());
153 return ntop.getPC().getNext(); // that should be the NATIVERETURN
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
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) {
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());
179 if (itx.getTargetException() instanceof UncaughtException) { // Native methods could
180 throw (UncaughtException) itx.getTargetException();
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());
190 protected boolean isUnsatisfiedLinkError(MJIEnv env){
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
199 protected Object[] getArguments (ThreadInfo ti) {
200 // these are just local refs to speed up
201 int nArgs = getNumberOfArguments();
202 byte[] argTypes = getArgumentTypes();
204 //Object[] a = getArgArray(nArgs + 2);
205 Object[] a = new Object[nArgs+2];
211 StackFrame caller = ti.getTopFrame();
214 for (i = 0, stackOffset = 0, j = nArgs + 1, k = nArgs - 1;
217 switch (argTypes[k]) {
218 case Types.T_BOOLEAN:
219 ival = caller.peek(stackOffset);
220 a[j] = Boolean.valueOf(Types.intToBoolean(ival));
225 ival = caller.peek(stackOffset);
226 a[j] = Byte.valueOf((byte) ival);
231 ival = caller.peek(stackOffset);
232 a[j] = Character.valueOf((char) ival);
237 ival = caller.peek(stackOffset);
238 a[j] = new Short((short) ival);
243 ival = caller.peek(stackOffset);
244 a[j] = new Integer(ival);
249 lval = caller.peekLong(stackOffset);
250 stackOffset++; // 2 stack words
251 a[j] = new Long(lval);
256 ival = caller.peek(stackOffset);
257 a[j] = new Float(Types.intToFloat(ival));
262 lval = caller.peekLong(stackOffset);
263 stackOffset++; // 2 stack words
264 a[j] = new Double(Types.longToDouble(lval));
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);
278 //--- set our standard MJI header arguments
280 a[1] = new Integer(ci.getClassObjectRef());
282 a[1] = new Integer(ti.getCalleeThis(this));
285 a[0] = ti.getMJIEnv();