1
2
3
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
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
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
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
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
308 ReflectiveVMInterface() {
309
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
342
343 throw ix;
344 }
345
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
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 }