View Javadoc

1   // ReflectiveInterpreter.java, created Mon Feb  5 23:23:21 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Interpreter;
5   
6   import java.util.Arrays;
7   import java.util.HashSet;
8   import java.util.Iterator;
9   import java.lang.reflect.Array;
10  import java.lang.reflect.Constructor;
11  import java.lang.reflect.InvocationTargetException;
12  import java.lang.reflect.Method;
13  import joeq.Class.PrimordialClassLoader;
14  import joeq.Class.jq_Array;
15  import joeq.Class.jq_Class;
16  import joeq.Class.jq_Initializer;
17  import joeq.Class.jq_Method;
18  import joeq.Class.jq_Primitive;
19  import joeq.Class.jq_StaticMethod;
20  import joeq.Class.jq_Type;
21  import joeq.Main.HostedVM;
22  import joeq.Runtime.Reflection;
23  import joeq.UTF.Utf8;
24  import jwutil.util.Assert;
25  import jwutil.util.Convert;
26  
27  /***
28   * @author  John Whaley <jwhaley@alum.mit.edu>
29   * @version $Id: ReflectiveInterpreter.java 2250 2005-04-29 07:41:11Z joewhaley $
30   */
31  public class ReflectiveInterpreter extends BytecodeInterpreter {
32  
33      /*** Creates new ReflectiveInterpreter */
34      public ReflectiveInterpreter(State initialState) {
35          super(new ReflectiveVMInterface(), initialState);
36      }
37  
38      public Object invokeReflective(jq_Method m) throws Throwable {
39          //System.out.println("Invoking reflectively: "+m);
40          jq_Class t = m.getDeclaringClass();
41          Assert._assert(t.isClsInitialized());
42          Class c = Reflection.getJDKType(t);
43          jq_Type[] param_jq = m.getParamTypes();
44          int offset = 0;
45          if (!m.isStatic()) offset = 1;
46          Class[] param_jdk = new Class[param_jq.length-offset];
47          Object[] param = new Object[param_jq.length-offset];
48          for (int i=param_jq.length-1; i>=offset; --i) {
49              Class pc = param_jdk[i-offset] = Reflection.getJDKType(param_jq[i]);
50              if (pc.isPrimitive()) {
51                  if (pc == Integer.TYPE) param[i-offset] = new Integer(istate.pop_I());
52                  else if (pc == Long.TYPE) param[i-offset] = new Long(istate.pop_L());
53                  else if (pc == Float.TYPE) param[i-offset] = new Float(istate.pop_F());
54                  else if (pc == Double.TYPE) param[i-offset] = new Double(istate.pop_D());
55                  else if (pc == Byte.TYPE) param[i-offset] = new Byte((byte)istate.pop_I());
56                  else if (pc == Short.TYPE) param[i-offset] = new Short((short)istate.pop_I());
57                  else if (pc == Character.TYPE) param[i-offset] = new Character((char)istate.pop_I());
58                  else if (pc == Boolean.TYPE) param[i-offset] = Convert.getBoolean(istate.pop_I()!=0);
59                  else Assert.UNREACHABLE(pc.toString());
60              } else {
61                  param[i-offset] = istate.pop_A();
62              }
63          }
64          try {
65              if (m instanceof jq_Initializer) {
66                  Constructor co = c.getDeclaredConstructor(param_jdk);
67                  co.setAccessible(true);
68                  UninitializedType u = (UninitializedType)istate.pop_A();
69                  Assert._assert(u.k == m.getDeclaringClass());
70                  Object inited = co.newInstance(param);
71                  ((ReflectiveState)istate).replaceUninitializedReferences(inited, u);
72                  return null;
73              }
74              Method mr = c.getDeclaredMethod(m.getName().toString(), param_jdk);
75              mr.setAccessible(true);
76              Object thisptr;
77              if (!m.isStatic()) thisptr = istate.pop_A();
78              else thisptr = null;
79              return mr.invoke(thisptr, param);
80          } catch (NoSuchMethodException x) {
81              Assert.UNREACHABLE("host jdk does not contain method "+m);
82          } catch (InstantiationException x) {
83              Assert.UNREACHABLE();
84          } catch (IllegalAccessException x) {
85              Assert.UNREACHABLE();
86          } catch (IllegalArgumentException x) {
87              Assert.UNREACHABLE();
88          } catch (InvocationTargetException x) {
89              throw new WrappedException(x.getTargetException());
90          }
91          return null;
92      }
93      static HashSet cantInterpret = new HashSet();
94      static {
95          jq_Class k = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljava/io/PrintStream;");
96          jq_Method m = k.getOrCreateInstanceMethod("write", "(Ljava/lang/String;)V");
97          cantInterpret.add(m);
98      }
99  
100     public Object invokeMethod(jq_Method m, State callee) throws Throwable {
101         //Runtime.SystemInterface.debugwriteln("Invoking method "+m);
102         jq_Class k = m.getDeclaringClass();
103         Assert._assert(k.isClsInitialized());
104         Assert._assert(m.getBytecode() != null);
105         jq_Type[] paramTypes = m.getParamTypes();
106         Object[] params = new Object[paramTypes.length];
107         for (int i=paramTypes.length-1; i>=0; --i) {
108             jq_Type t = paramTypes[i];
109             if (t.isPrimitiveType()) {
110                 if (t == jq_Primitive.LONG) {
111                     params[i] = new Long(istate.pop_L());
112                 } else if (t == jq_Primitive.FLOAT) {
113                     params[i] = new Float(istate.pop_F());
114                 } else if (t == jq_Primitive.DOUBLE) {
115                     params[i] = new Double(istate.pop_D());
116                 } else {
117                     params[i] = new Integer(istate.pop_I());
118                 }
119             } else {
120                 params[i] = istate.pop_A();
121             }
122             //System.out.println("Param "+i+": "+params[i]);
123         }
124         for (int i=0, j=0; i<paramTypes.length; ++i, ++j) {
125             jq_Type t = paramTypes[i];
126             if (t.isPrimitiveType()) {
127                 if (t == jq_Primitive.LONG) {
128                     long v = ((Long)params[i]).longValue();
129                     callee.setLocal_L(j, v);
130                     ++j;
131                 } else if (t == jq_Primitive.FLOAT) {
132                     float v = ((Float)params[i]).floatValue();
133                     callee.setLocal_F(j, v);
134                 } else if (t == jq_Primitive.DOUBLE) {
135                     long v = Double.doubleToRawLongBits(((Double)params[i]).doubleValue());
136                     callee.setLocal_D(j, Double.longBitsToDouble(v));
137                     ++j;
138                 } else {
139                     int v = ((Integer)params[i]).intValue();
140                     callee.setLocal_I(j, v);
141                 }
142             } else {
143                 Object v = params[i];
144                 callee.setLocal_A(j, v);
145             }
146         }
147         State oldState = this.istate;
148         this.istate = callee;
149         MethodInterpreter mi = new MethodInterpreter(m);
150         Object synchobj = null;
151         try {
152             if (m.isSynchronized()) {
153                 if (!m.isStatic()) {
154                     if (mi.getTraceFlag()) mi.getTraceOut().println("synchronized instance method, locking 'this' object");
155                     vm.monitorenter(synchobj = istate.getLocal_A(0), mi);
156                 } else {
157                     if (mi.getTraceFlag()) mi.getTraceOut().println("synchronized static method, locking class object");
158                     vm.monitorenter(synchobj = Reflection.getJDKType(m.getDeclaringClass()), mi);
159                 }
160             }
161             mi.forwardTraversal();
162             this.istate = oldState;
163             if (m.isSynchronized()) {
164                 if (mi.getTraceFlag()) mi.getTraceOut().println("exiting synchronized method, unlocking object");
165                 vm.monitorexit(synchobj);
166             }
167             jq_Type returnType = m.getReturnType();
168             Object retval;
169             if (returnType.isReferenceType()) {
170                 retval = callee.getReturnVal_A();
171             } else if (returnType == jq_Primitive.VOID) {
172                 retval = null;
173             } else if (returnType == jq_Primitive.LONG) {
174                 retval = new Long(callee.getReturnVal_L());
175             } else if (returnType == jq_Primitive.FLOAT) {
176                 retval = new Float(callee.getReturnVal_F());
177             } else if (returnType == jq_Primitive.DOUBLE) {
178                 retval = new Double(callee.getReturnVal_D());
179             } else {
180                 retval = new Integer(callee.getReturnVal_I());
181             }
182             if (mi.getTraceFlag())
183                 mi.getTraceOut().println("Return value: "+retval);
184             return retval;
185         } catch (WrappedException ix) {
186             this.istate = oldState;
187             if (m.isSynchronized()) {
188                 if (mi.getTraceFlag()) mi.getTraceOut().println("exiting synchronized method, unlocking object");
189                 vm.monitorexit(synchobj);
190             }
191             throw ix.t;
192         }
193     }
194 
195     public Object invokeMethod(jq_Method m) throws Throwable {
196         if (cantInterpret.contains(m)) {
197             return invokeReflective(m);
198         }
199         if (m.isNative() || m instanceof jq_Initializer) {
200             return invokeReflective(m);
201         } else {
202             ReflectiveState callee = new ReflectiveState(m);
203             try {
204                 return this.invokeMethod(m, callee);
205             } catch (MonitorExit x) {
206                 Assert._assert(m.isSynchronized());
207                 Assert._assert(istate != callee);
208                 return callee.getReturnVal_A();
209             }
210         }
211     }
212     public Object invokeUnsafeMethod(jq_Method f) throws Throwable {
213         jq_Class _class = (jq_Class) PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Runtime/Unsafe;");
214         jq_StaticMethod _floatToIntBits = _class.getOrCreateStaticMethod("floatToIntBits", "(F)I");
215         jq_StaticMethod _intBitsToFloat = _class.getOrCreateStaticMethod("intBitsToFloat", "(I)F");
216         jq_StaticMethod _doubleToLongBits = _class.getOrCreateStaticMethod("doubleToLongBits", "(D)J");
217         jq_StaticMethod _longBitsToDouble = _class.getOrCreateStaticMethod("longBitsToDouble", "(J)D");
218 
219         if (f == _floatToIntBits) {
220             return new Integer(Float.floatToRawIntBits(istate.pop_F()));
221         } else if (f == _intBitsToFloat) {
222             return new Float(Float.intBitsToFloat(istate.pop_I()));
223         } else if (f == _doubleToLongBits) {
224             return new Long(Double.doubleToRawLongBits(istate.pop_D()));
225         } else if (f == _longBitsToDouble) {
226             return new Double(Double.longBitsToDouble(istate.pop_L()));
227         } else {
228             return invokeReflective(f);
229         }
230     }
231     
232     public static class ReflectiveState extends BytecodeInterpreter.State {
233         final Object[] locals;
234         final Object[] stack;
235         final jq_Method m;
236         int sp;
237         Object result;
238         
239         public ReflectiveState(Object[] incoming_args) {
240             this.m = null;
241             this.locals = new Object[0];
242             this.stack = incoming_args;
243             this.sp = incoming_args.length;
244         }
245         
246         public ReflectiveState(jq_Method m) {
247             //System.out.println("Initializing state: "+m.getMaxLocals()+" locals and "+m.getMaxStack()+" stack");
248             this.m = m;
249             this.locals = new Object[m.getMaxLocals()];
250             this.stack = new Object[m.getMaxStack()];
251             this.sp = 0;
252         }
253 
254         public void push_I(int v) { stack[sp++] = new Integer(v); }
255         public void push_L(long v) { stack[sp++] = new Long(v); stack[sp++] = null; }
256         public void push_F(float v) { stack[sp++] = new Float(v); }
257         public void push_D(double v) { stack[sp++] = new Double(v); stack[sp++] = null; }
258         public void push_A(Object v) { stack[sp++] = v; }
259         public void push(Object v) { stack[sp++] = v; }
260         public int pop_I() { return ((Integer)stack[--sp]).intValue(); }
261         public long pop_L() { --sp; return ((Long)stack[--sp]).longValue(); }
262         public float pop_F() { return ((Float)stack[--sp]).floatValue(); }
263         public double pop_D() { --sp; return ((Double)stack[--sp]).doubleValue(); }
264         public Object pop_A() { return stack[--sp]; }
265         public Object pop() { return stack[--sp]; }
266         public void popAll() { sp = 0; }
267         public Object peek_A(int depth) { return stack[sp-depth-1]; }
268         public void setLocal_I(int i, int v) { locals[i] = new Integer(v); }
269         public void setLocal_L(int i, long v) { locals[i] = new Long(v); }
270         public void setLocal_F(int i, float v) { locals[i] = new Float(v); }
271         public void setLocal_D(int i, double v) { locals[i] = new Double(v); }
272         public void setLocal_A(int i, Object v) { locals[i] = v; }
273         public int getLocal_I(int i) { return ((Integer)locals[i]).intValue(); }
274         public long getLocal_L(int i) { return ((Long)locals[i]).longValue(); }
275         public float getLocal_F(int i) { return ((Float)locals[i]).floatValue(); }
276         public double getLocal_D(int i) { return ((Double)locals[i]).doubleValue(); }
277         public Object getLocal_A(int i) { return locals[i]; }
278         public void return_I(int v) { result = new Integer(v); }
279         public void return_L(long v) { result = new Long(v); }
280         public void return_F(float v) { result = new Float(v); }
281         public void return_D(double v) { result = new Double(v); }
282         public void return_A(Object v) { result = v; }
283         public void return_V() {}
284         public int getReturnVal_I() { return ((Integer)result).intValue(); }
285         public long getReturnVal_L() { return ((Long)result).longValue(); }
286         public float getReturnVal_F() { return ((Float)result).floatValue(); }
287         public double getReturnVal_D() { return ((Double)result).doubleValue(); }
288         public Object getReturnVal_A() { return result; }
289         
290         void replaceUninitializedReferences(Object o, UninitializedType u) {
291             int p = sp;
292             while (--p >= 0) {
293                 if (stack[p] == u) stack[p] = o;
294             }
295             for (p=0; p<locals.length; ++p) {
296                 if (locals[p] == u) locals[p] = o;
297             }
298         }
299     }
300     
301     static class UninitializedType {
302         jq_Class k;
303         UninitializedType(jq_Class k) { this.k = k; }
304     }
305     
306     public static class ReflectiveVMInterface extends BytecodeInterpreter.VMInterface {
307         //ObjectTraverser ot;
308         ReflectiveVMInterface() {
309             //ot = new ObjectTraverser.Empty();
310         }
311         public static final ReflectiveVMInterface INSTANCE = new ReflectiveVMInterface();
312         public Object new_obj(jq_Type t) {
313             t.cls_initialize();
314             return new UninitializedType((jq_Class) t);
315         }
316         public Object new_array(jq_Type t, int length) {
317             t.cls_initialize();
318             return Array.newInstance(Reflection.getJDKType(((jq_Array)t).getElementType()), length);
319         }
320         public Object checkcast(Object o, jq_Type t) {
321             if (o == null) return o;
322             if (!Reflection.getJDKType(t).isAssignableFrom(o.getClass()))
323                 throw new ClassCastException();
324             return o;
325         }
326         public boolean instance_of(Object o, jq_Type t) {
327             if (o == null) return false;
328             return Reflection.getJDKType(t).isAssignableFrom(o.getClass());
329         }
330         public int arraylength(Object o) {
331             return Array.getLength(o);
332         }
333         public void monitorenter(Object o, MethodInterpreter v) {
334             synchronized (o) {
335                 try {
336                     v.continueForwardTraversal();
337                 } catch (MonitorExit x) {
338                     Assert._assert(x.o == o, "synchronization blocks are not nested!");
339                     return;
340                 } catch (WrappedException ix) {
341                     // if the method throws an exception, the object will automatically be unlocked
342                     // when we exit this synchronized block.
343                     throw ix;
344                 }
345                 // method exit
346             }
347         }
348         public void monitorexit(Object o) {
349             throw new MonitorExit(o);
350         }
351         public Object multinewarray(int[] dims, jq_Type t) {
352             for (int i=0; i<dims.length; ++i) {
353                 t.cls_initialize();
354                 t = ((jq_Array)t).getElementType();
355             }
356             return Array.newInstance(Reflection.getJDKType(t), dims);
357         }
358         
359     }
360 
361     static class MonitorExit extends RuntimeException {
362         /***
363          * Version ID for serialization.
364          */
365         private static final long serialVersionUID = 3835157242168096821L;
366         Object o;
367         MonitorExit(Object o) { this.o = o; }
368     }
369     
370     // Invoke reflective interpreter from command line.
371     public static void main(String[] s_args) throws Throwable {
372         String s = s_args[0];
373         int dotloc = s.lastIndexOf('.');
374         String rootMethodClassName = s.substring(0, dotloc);
375         String rootMethodName = s.substring(dotloc+1);
376         
377         HostedVM.initialize();
378         
379         jq_Class c = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("L"+rootMethodClassName.replace('.','/')+";");
380         c.cls_initialize();
381 
382         jq_StaticMethod rootm = null;
383         Utf8 rootm_name = Utf8.get(rootMethodName);
384         for(Iterator it = Arrays.asList(c.getDeclaredStaticMethods()).iterator();
385             it.hasNext(); ) {
386             jq_StaticMethod m = (jq_StaticMethod)it.next();
387             if (m.getName() == rootm_name) {
388                 rootm = m;
389                 break;
390             }
391         }
392         if (rootm == null)
393             Assert.UNREACHABLE("root method not found: "+rootMethodClassName+"."+rootMethodName);
394         Object[] args = parseMethodArgs(rootm.getParamWords(), rootm.getParamTypes(), s_args, 0);
395         ReflectiveState initialState = new ReflectiveState(args);
396         Object retval = new ReflectiveInterpreter(initialState).invokeMethod(rootm);
397         System.out.println("Return value: "+retval);
398     }
399     
400     public static Object[] parseMethodArgs(int argsSize, jq_Type[] paramTypes, String[] s_args, int j) {
401         Object[] args = new Object[argsSize];
402         try {
403             for (int i=0, m=0; i<paramTypes.length; ++i, ++m) {
404                 if (paramTypes[i] == PrimordialClassLoader.getJavaLangString())
405                     args[m] = s_args[++j];
406                 else if (paramTypes[i] == jq_Primitive.BOOLEAN)
407                     args[m] = Boolean.valueOf(s_args[++j]);
408                 else if (paramTypes[i] == jq_Primitive.BYTE)
409                     args[m] = Byte.valueOf(s_args[++j]);
410                 else if (paramTypes[i] == jq_Primitive.SHORT)
411                     args[m] = Short.valueOf(s_args[++j]);
412                 else if (paramTypes[i] == jq_Primitive.CHAR)
413                     args[m] = new Character(s_args[++j].charAt(0));
414                 else if (paramTypes[i] == jq_Primitive.INT)
415                     args[m] = Integer.valueOf(s_args[++j]);
416                 else if (paramTypes[i] == jq_Primitive.LONG) {
417                     args[m] = Long.valueOf(s_args[++j]);
418                     if (argsSize != paramTypes.length) ++m;
419                 } else if (paramTypes[i] == jq_Primitive.FLOAT)
420                     args[m] = Float.valueOf(s_args[++j]);
421                 else if (paramTypes[i] == jq_Primitive.DOUBLE) {
422                     args[m] = Double.valueOf(s_args[++j]);
423                     if (argsSize != paramTypes.length) ++m;
424                 } else if (paramTypes[i].isArrayType()) {
425                     if (!s_args[++j].equals("{")) 
426                         Assert.UNREACHABLE("array parameter doesn't start with {");
427                     int count=0;
428                     while (!s_args[++j].equals("}")) ++count;
429                     jq_Type elementType = ((jq_Array)paramTypes[i]).getElementType();
430                     if (elementType == PrimordialClassLoader.getJavaLangString()) {
431                         String[] array = new String[count];
432                         for (int k=0; k<count; ++k)
433                             array[k] = s_args[j-count+k];
434                         args[m] = array;
435                     } else if (elementType == jq_Primitive.BOOLEAN) {
436                         boolean[] array = new boolean[count];
437                         for (int k=0; k<count; ++k)
438                             array[k] = Boolean.valueOf(s_args[j-count+k]).booleanValue();
439                         args[m] = array;
440                     } else if (elementType == jq_Primitive.BYTE) {
441                         byte[] array = new byte[count];
442                         for (int k=0; k<count; ++k)
443                             array[k] = Byte.parseByte(s_args[j-count+k]);
444                         args[m] = array;
445                     } else if (elementType == jq_Primitive.SHORT) {
446                         short[] array = new short[count];
447                         for (int k=0; k<count; ++k)
448                             array[k] = Short.parseShort(s_args[j-count+k]);
449                         args[m] = array;
450                     } else if (elementType == jq_Primitive.CHAR) {
451                         char[] array = new char[count];
452                         for (int k=0; k<count; ++k)
453                             array[k] = s_args[j-count+k].charAt(0);
454                         args[m] = array;
455                     } else if (elementType == jq_Primitive.INT) {
456                         int[] array = new int[count];
457                         for (int k=0; k<count; ++k)
458                             array[k] = Integer.parseInt(s_args[j-count+k]);
459                         args[m] = array;
460                     } else if (elementType == jq_Primitive.LONG) {
461                         long[] array = new long[count];
462                         for (int k=0; k<count; ++k)
463                             array[k] = Long.parseLong(s_args[j-count+k]);
464                         args[m] = array;
465                     } else if (elementType == jq_Primitive.FLOAT) {
466                         float[] array = new float[count];
467                         for (int k=0; k<count; ++k)
468                             array[k] = Float.parseFloat(s_args[j-count+k]);
469                         args[m] = array;
470                     } else if (elementType == jq_Primitive.DOUBLE) {
471                         double[] array = new double[count];
472                         for (int k=0; k<count; ++k)
473                             array[k] = Double.parseDouble(s_args[j-count+k]);
474                         args[m] = array;
475                     } else
476                         Assert.UNREACHABLE("Parsing an argument of type "+paramTypes[i]+" is not implemented");
477                 } else
478                     Assert.UNREACHABLE("Parsing an argument of type "+paramTypes[i]+" is not implemented");
479             }
480         } catch (ArrayIndexOutOfBoundsException x) {
481             x.printStackTrace();
482             Assert.UNREACHABLE("not enough method arguments");
483         }
484         return args;
485     }
486 
487 }