001: //
002: // Copyright (C) 2005 United States Government as represented by the
003: // Administrator of the National Aeronautics and Space Administration
004: // (NASA). All Rights Reserved.
005: //
006: // This software is distributed under the NASA Open Source Agreement
007: // (NOSA), version 1.3. The NOSA has been approved by the Open Source
008: // Initiative. See the file NOSA-1.3-JPF at the top of the distribution
009: // directory tree for the complete NOSA document.
010: //
011: // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
012: // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
013: // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
014: // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
015: // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
016: // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
017: // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
018: //
019: package gov.nasa.jpf.tools;
020:
021: import gov.nasa.jpf.jvm.Types;
022:
023: import java.io.PrintWriter;
024:
025: import java.lang.reflect.*;
026:
027: /**
028: * tool to create NativePeerDispatchers, i.e. classes that do the NativePeer
029: * method lookup with explicit hahcode values instead of reflection.
030: * Given a NativePeer, this class computes the hashcodes and creates a
031: * dispatcher class that mostly consists of one big dispatcher method directly
032: * calling the native methods
033: * Since reflection call efficiency got significantly improved since Java 1.4,
034: * dispatcher classes are not really required anymore.
035: */
036: public class GenPeerDispatcher {
037: static final String SYS_PKG = "gov.nasa.jpf.jvm";
038: static final String INDENT = " ";
039: static final String EXECUTE = "Instruction executeMethod (ThreadInfo ti, MethodInfo mi)";
040: static final String IS_COND_DETERMINISTIC = "boolean isMethodCondDeterministic (ThreadInfo ti, MethodInfo mi)";
041: static final String IS_COND_EXECUTABLE = "boolean isMethodCondExecutable (ThreadInfo ti, MethodInfo mi)";
042: static final String EXEC_COND = "$isExecutable_";
043: static final String DETERM_COND = "$isDeterministic_";
044: static final int MJI_MODS = Modifier.PUBLIC | Modifier.STATIC;
045: static String clsName;
046: static PrintWriter pw;
047: static Method[] tmethods; // target class method cache (for reverse lookup)
048:
049: public static void main(String[] args) {
050: if ((args.length == 0) || !readOptions(args)) {
051: showUsage();
052:
053: return;
054: }
055:
056: pw = new PrintWriter(System.out, true);
057:
058: Class cls = getClass(clsName);
059:
060: if (cls != null) {
061: printNativePeerDispatcher(cls);
062: }
063: }
064:
065: static Class getClass(String cname) {
066: Class clazz = null;
067:
068: try {
069: clazz = Class.forName(cname);
070: } catch (ClassNotFoundException cnfx) {
071: System.err.println("target class not found: " + cname);
072: } catch (Throwable x) {
073: x.printStackTrace();
074: }
075:
076: return clazz;
077: }
078:
079: static boolean isMJIDetermCondCandidate(Method m) {
080: if ((m.getModifiers() & MJI_MODS) == MJI_MODS) {
081: String name = m.getName();
082:
083: return name.startsWith(DETERM_COND);
084: }
085:
086: return false;
087: }
088:
089: static boolean isMJIExecCondCandidate(Method m) {
090: if ((m.getModifiers() & MJI_MODS) == MJI_MODS) {
091: String name = m.getName();
092:
093: return name.startsWith(EXEC_COND);
094: }
095:
096: return false;
097: }
098:
099: static boolean isMJIExecuteCandidate(Method m) {
100: if ((m.getModifiers() & MJI_MODS) == MJI_MODS) {
101: String name = m.getName();
102:
103: return !(name.startsWith(EXEC_COND) || name
104: .startsWith(DETERM_COND));
105: }
106:
107: return false;
108: }
109:
110: static String getSignature(Method m) {
111: String mname = m.getName();
112:
113: if (mname.equals("$clinit") || mname.equals("$init")) {
114: // <2do> - if we want to be real good, we really have to treat ctors
115: return "()";
116: }
117:
118: // bad, we have to do a reverse lookup from the target class
119: // (can't use our argTypes because we lost the object classes)
120: Method tm = getTargetMethod(m);
121:
122: if (tm != null) {
123: // let's tinker us a signature
124: Class[] argTypes = tm.getParameterTypes();
125: StringBuffer sb = new StringBuffer();
126:
127: sb.append('(');
128:
129: for (int i = 0; i < argTypes.length; i++) {
130: Class t = argTypes[i];
131:
132: while (t.isArray()) {
133: sb.append('[');
134: t = t.getComponentType();
135: }
136:
137: if (t == Boolean.TYPE) {
138: sb.append('Z');
139: } else if (t == Byte.TYPE) {
140: sb.append('B');
141: } else if (t == Character.TYPE) {
142: sb.append('C');
143: } else if (t == Short.TYPE) {
144: sb.append('S');
145: } else if (t == Integer.TYPE) {
146: sb.append('I');
147: } else if (t == Long.TYPE) {
148: sb.append('J');
149: } else if (t == Float.TYPE) {
150: sb.append('F');
151: } else if (t == Double.TYPE) {
152: sb.append('D');
153: } else {
154: sb.append('L');
155: sb.append(t.getName().replace('.', '/'));
156: sb.append(';');
157: }
158: }
159:
160: sb.append(')');
161:
162: return sb.toString();
163: }
164:
165: return "()"; // a bloody guess, probably will fail
166: }
167:
168: static Method getTargetMethod(Method m) {
169: // <2do> - no non-default ctors support
170: String mname = m.getName();
171:
172: if (tmethods == null) {
173: String tcn = m.getDeclaringClass().getName();
174: tcn = tcn.substring(tcn.indexOf("JPF") + 4);
175: tcn = tcn.replace('_', '.');
176:
177: try {
178: // <2do> - doesn't work for bootclasspath candidates, of course!
179: Class tcls = Class.forName(tcn);
180: tmethods = tcls.getDeclaredMethods();
181: } catch (ClassNotFoundException cnfx) {
182: System.err.println("!! cannot find target class " + tcn
183: + " to determine signature of: " + mname);
184:
185: return null;
186: }
187: }
188:
189: for (int i = 0; i < tmethods.length; i++) {
190: if (tmethods[i].getName().equals(mname)) {
191: return tmethods[i];
192: }
193: }
194:
195: System.err
196: .println("!! cannot find target method to determine signature of: "
197: + mname);
198:
199: return null;
200: }
201:
202: static int calcStackSize(Class[] argTypes) {
203: int n = 0;
204:
205: // the first two args are the MJIEnv object and the 'this' / class object ref
206: // we don't count them here
207: for (int i = 2; i < argTypes.length; i++) {
208: if ((argTypes[i] == Long.TYPE)
209: || (argTypes[i] == Double.TYPE)) {
210: n += 2;
211: } else {
212: n++;
213: }
214: }
215:
216: return n;
217: }
218:
219: static void iprint(int level, String s) {
220: printIndent(level);
221: pw.print(s);
222: }
223:
224: static void iprintln(int level, String s) {
225: printIndent(level);
226: pw.println(s);
227: }
228:
229: static void printCall(Class cls, Method m) {
230: Class[] argTypes = m.getParameterTypes();
231: Class retType = m.getReturnType();
232: int stackOffset = calcStackSize(argTypes); // not counting this/class ref
233:
234: pw.print(cls.getName());
235: pw.print('.');
236: pw.print(m.getName());
237: pw.print("( env, rThis");
238:
239: if (argTypes.length > 2) {
240: pw.println(',');
241: }
242:
243: for (int i = 2; i < argTypes.length;) {
244: stackOffset--;
245:
246: if (argTypes[i] == Boolean.TYPE) {
247: iprint(7, "Types.intToBoolean( ti.peek(" + stackOffset
248: + "))");
249: } else if (argTypes[i] == Byte.TYPE) {
250: iprint(7, "(byte) ti.peek(" + stackOffset + ")");
251: } else if (argTypes[i] == Character.TYPE) {
252: iprint(7, "(char) ti.peek(" + stackOffset + ")");
253: } else if (argTypes[i] == Short.TYPE) {
254: iprint(7, "(short) ti.peek(" + stackOffset + ")");
255: } else if (argTypes[i] == Integer.TYPE) {
256: iprint(7, "ti.peek(" + stackOffset + ")");
257: } else if (argTypes[i] == Long.TYPE) {
258: stackOffset--;
259: iprint(7, "ti.longPeek(" + stackOffset + ")");
260: } else if (argTypes[i] == Float.TYPE) {
261: iprint(7, "Types.intToFloat( ti.peek(" + stackOffset
262: + "))");
263: } else if (argTypes[i] == Double.TYPE) {
264: stackOffset--;
265: iprint(7, "Types.longToDouble( ti.longPeek("
266: + stackOffset + "))");
267: } else {
268: iprint(7, "ti.peek(" + stackOffset + ")");
269: }
270:
271: if ((++i) < argTypes.length) {
272: pw.println(',');
273: }
274: }
275:
276: pw.print(")");
277: }
278:
279: static void printCaseConst(Method m) {
280: String mname = m.getName();
281: String jniname = Types.getJNIMethodName(mname);
282:
283: String id = jniname;
284:
285: if (id.equals("$clinit")) {
286: id = "<clinit>";
287: } else if (id.equals("$init")) {
288: id = "<init>";
289: }
290:
291: String argSig = Types.getJNIArgSignature(mname);
292:
293: if (argSig != null) {
294: id += argSig;
295: } else {
296: // Ok, no type signature, we have to recreate this from the argTypes
297: id += getSignature(m);
298: }
299:
300: iprint(3, "case ");
301: pw.print(id.hashCode());
302: pw.print(": // ");
303: pw.println(id);
304: }
305:
306: static int printExecCallProlog(Method m) {
307: Class retType = m.getReturnType();
308:
309: if (retType == Void.TYPE) {
310: iprintln(4, "retSize = 0;");
311: iprint(4, "");
312: } else if (retType == Boolean.TYPE) {
313: iprintln(4, "retSize = 1;");
314: iprint(4, "iret = Types.booleanToInt( ");
315:
316: return 1;
317: } else if (retType == Byte.TYPE) {
318: iprintln(4, "retSize = 1;");
319: iprint(4, "iret = (int) ");
320: } else if (retType == Character.TYPE) {
321: iprintln(4, "retSize = 1;");
322: iprint(4, "iret = (int) ");
323: } else if (retType == Short.TYPE) {
324: iprintln(4, "retSize = 1;");
325: iprint(4, "iret = (int) ");
326: } else if (retType == Integer.TYPE) {
327: iprintln(4, "retSize = 1;");
328: iprint(4, "iret = ");
329: } else if (retType == Long.TYPE) {
330: iprintln(4, "retSize = 2;");
331: iprint(4, "lret = ");
332: } else if (retType == Float.TYPE) {
333: iprintln(4, "retSize = 1;");
334: iprint(4, "iret = Types.floatToInt( ");
335:
336: return 1;
337: } else if (retType == Double.TYPE) {
338: iprintln(4, "retSize = 2;");
339: iprint(4, "lret = Types.doubleToLong( ");
340:
341: return 1;
342: } else {
343: iprintln(4, "retSize = 1;");
344: iprint(4, "iret = ");
345: }
346:
347: return 0;
348: }
349:
350: static void printExecute(Class cls) {
351: Method[] mths = cls.getDeclaredMethods();
352:
353: iprint(1, EXECUTE);
354: pw.println(" {");
355:
356: iprintln(2, "int iret = 0;");
357: iprintln(2, "long lret = 0;");
358: iprintln(2, "int retSize = 0;");
359: iprintln(2, "String exception = null;");
360: iprintln(2, "int mid = mi.getUniqueName().hashCode();");
361: pw.println();
362:
363: iprintln(2, "MJIEnv env = ti.getMJIEnv();");
364: iprintln(2,
365: "int rThis = (mi.isStatic()) ? ci.getClassObjectRef() : ti.getCalleeThis(mi);");
366: pw.println();
367:
368: iprintln(2, "env.setCallEnvironment( mi);");
369: pw.println();
370:
371: iprintln(2, "try {");
372:
373: iprintln(3, "switch (mid) {");
374:
375: for (int i = 0; i < mths.length; i++) {
376: Method m = mths[i];
377:
378: if (isMJIExecuteCandidate(m)) {
379: printCaseConst(m);
380:
381: int openFuncs = printExecCallProlog(m);
382: printCall(cls, m);
383:
384: for (int j = 0; j < openFuncs; j++) {
385: pw.print(')');
386: }
387:
388: pw.println(';');
389: iprintln(4, "break;");
390: }
391: }
392:
393: iprintln(3, "default:");
394: iprintln(4,
395: "return ti.createAndThrowException( \"java.lang.UnsatisfiedLinkError\",");
396: iprintln(6,
397: "\"cannot find: \" + ci.getName() + '.' + mi.getName());");
398:
399: iprintln(3, "}");
400:
401: iprintln(2, "} catch (Throwable x) {");
402: iprintln(3, "x.printStackTrace();");
403: iprintln(
404: 3,
405: "return ti.createAndThrowException( \"java.lang.reflect.InvocationTargetException\",");
406: iprintln(5, "ci.getName() + '.' + mi.getName());");
407: iprintln(2, "}");
408:
409: pw.println();
410: iprintln(2, "if ((exception = env.getException()) != null) {");
411: iprintln(3, "return ti.createAndThrowException(exception);");
412: iprintln(2, "}");
413: pw.println();
414: iprintln(2, "if (env.getRepeat()) {");
415: iprintln(3, "return ti.getPC();");
416: iprintln(2, "}");
417: pw.println();
418: iprintln(2, "ti.removeArguments(mi);");
419: pw.println();
420: iprintln(2, "switch (retSize) {");
421: iprintln(2, "case 0: break; // nothing to return");
422: iprintln(2,
423: "case 1: ti.push(iret, mi.isReferenceReturnType()); break;");
424: iprintln(2, "case 2: ti.longPush(lret); break;");
425: iprintln(2, "}");
426: pw.println();
427: iprintln(2, "return ti.getPC().getNext();");
428:
429: iprintln(1, "}");
430: }
431:
432: static void printFooter(Class cls) {
433: pw.println("}");
434: }
435:
436: static void printHeader(Class cls) {
437: pw.print("package ");
438: pw.print(SYS_PKG);
439: pw.println(';');
440: pw.println();
441:
442: String cname = cls.getName();
443: int idx = cname.lastIndexOf('.');
444:
445: if (idx > 0) {
446: cname = cname.substring(idx + 1);
447: }
448:
449: pw.println("import gov.nasa.jpf.JPFVMException;");
450: pw.println("import gov.nasa.jpf.jvm.bytecode.Instruction;");
451: pw.println();
452:
453: pw.print("class ");
454: pw.print(cname);
455: pw.println("$ extends NativePeer {");
456: }
457:
458: static void printIndent(int level) {
459: for (int i = 0; i < level; i++) {
460: pw.print(INDENT);
461: }
462: }
463:
464: static void printIsCond(Class cls, String condPrefix) {
465: Method[] mths = cls.getDeclaredMethods();
466:
467: iprint(1, condPrefix);
468: pw.println(" {");
469:
470: iprintln(2, "boolean ret = false;");
471: iprintln(2, "int mid = mi.getUniqueName().hashCode();");
472: pw.println();
473:
474: iprintln(2, "MJIEnv env = ti.getMJIEnv();");
475: iprintln(2,
476: "int rThis = (mi.isStatic()) ? ci.getClassObjectRef() : ti.getCalleeThis(mi);");
477: pw.println();
478:
479: iprintln(2, "env.setCallEnvironment( mi);");
480: pw.println();
481:
482: iprintln(2, "try {");
483:
484: iprintln(3, "switch (mid) {");
485:
486: for (int i = 0; i < mths.length; i++) {
487: Method m = mths[i];
488:
489: if (((condPrefix == IS_COND_DETERMINISTIC) && isMJIDetermCondCandidate(m))
490: || ((condPrefix == IS_COND_EXECUTABLE) && isMJIExecCondCandidate(m))) {
491: printCaseConst(m);
492:
493: iprint(4, "ret = ");
494: printCall(cls, m);
495: pw.println(';');
496: }
497: }
498:
499: iprintln(3, "default:");
500:
501: if (condPrefix == IS_COND_EXECUTABLE) {
502: iprintln(4,
503: "throw new JPFVMException(\"no isExecutable() condition: \" + mi.getName());");
504: } else {
505: iprintln(
506: 4,
507: "throw new JPFVMException(\"no isDeterministic() condition: \" + mi.getName());");
508: }
509:
510: iprintln(3, "}");
511:
512: iprintln(2, "} catch (Throwable x) {");
513: iprintln(3, "x.printStackTrace();");
514: iprintln(2, "}");
515: pw.println();
516:
517: iprintln(2, "return ret;");
518: iprintln(1, "}");
519: }
520:
521: static void printIsCondDeterministic(Class cls) {
522: printIsCond(cls, IS_COND_DETERMINISTIC);
523: }
524:
525: static void printIsCondExecutable(Class cls) {
526: printIsCond(cls, IS_COND_EXECUTABLE);
527: }
528:
529: static void printNativePeerDispatcher(Class cls) {
530: printHeader(cls);
531: pw.println();
532:
533: printExecute(cls);
534: pw.println();
535: printIsCondDeterministic(cls);
536: pw.println();
537: printIsCondExecutable(cls);
538: pw.println();
539:
540: printFooter(cls);
541: }
542:
543: static boolean readOptions(String[] args) {
544: for (int i = 0; i < args.length; i++) {
545: String arg = args[i];
546:
547: if (arg.charAt(0) == '-') {
548: System.err.println("unknown option: " + arg);
549: showUsage();
550:
551: return false;
552: } else {
553: if (clsName == null) {
554: clsName = arg;
555: }
556: }
557: }
558:
559: return (clsName != null);
560: }
561:
562: static void showUsage() {
563: System.out.println("usage: 'GenPeerDispatcher <className>'");
564: }
565: }
|