Fixes default method resolution (#159)
[jpf-core.git] / src / main / gov / nasa / jpf / tool / GenPeer.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 package gov.nasa.jpf.tool;
19
20 import gov.nasa.jpf.vm.Types;
21
22 import java.io.PrintWriter;
23 import java.lang.reflect.Method;
24 import java.lang.reflect.Modifier;
25 import java.util.ArrayList;
26
27
28 /**
29  * tool to automatically generate the framework of a native peer MJI class,
30  * given it's model class. GenPeer collects all the native methods from the 
31  * model class, and creates the corresponding native peer methods
32  */
33 public class GenPeer {
34   static final String SYS_PKG = "gov.nasa.jpf.vm";
35   static final String MJI_ENV = "gov.nasa.jpf.vm.MJIEnv";
36   static final String NATIVEPEER = "gov.nasa.jpf.vm.NativePeer";
37   static final String MJI_ANNOTATION = "gov.nasa.jpf.annotation.MJI";
38   static final String INDENT = "  ";
39   static final String SUPERCLASS = "NativePeer";
40   static final String MJI_ANN = "@MJI";
41   static final String METHOD_PREFIX = "public";
42   static final String ENV_ARG = "MJIEnv env";
43   static final String OBJ_ARG = "int objRef";
44   static final String CLS_ARG = "int clsObjRef";
45   static final String REF_TYPE = "int";
46   static final String NULL = "MJIEnv.NULL";
47
48   static String       clsName;
49   static String[]     mths;
50
51   // our options
52   static boolean isSystemPkg;
53   static boolean allMethods;
54   static boolean mangleNames;
55   static boolean clinit;
56
57   public static void main (String[] args) {
58     if ((args.length == 0) || !readOptions(args)) {
59       showUsage();
60
61       return;
62     }
63
64     PrintWriter pw = new PrintWriter(System.out, true);
65     Class<?>       cls = getClass(clsName);
66
67     if (cls != null) {
68       printNativePeer(cls, pw);
69     }
70   }
71
72   static Class<?> getClass (String cname) {
73     Class<?> clazz = null;
74
75     try {
76       clazz = Class.forName(cname);
77     } catch (ClassNotFoundException cnfx) {
78       System.err.println("target class not found: " + cname);
79     } catch (Throwable x) {
80       x.printStackTrace();
81     }
82
83     return clazz;
84   }
85
86   static boolean isMJICandidate (Method m) {
87     if (allMethods) {
88       return true;
89     }
90
91     if (mths != null) {
92       String name = m.getName();
93
94       for (int i = 0; i < mths.length; i++) {
95         if (name.equals(mths[i])) {
96           return true;
97         }
98       }
99     } else {
100       if ((m.getModifiers() & Modifier.NATIVE) != 0) {
101         return true;
102       }
103     }
104
105     return false;
106   }
107
108   static void getMangledName (Method m) {
109     StringBuilder sb = new StringBuilder(50);
110
111     sb.append(m.getName());
112     sb.append("__");
113   }
114
115   static boolean isPrimitiveType (String t) {
116     return ("int".equals(t) || "long".equals(t) || "boolean".equals(t) || 
117            "void".equals(t) || // not really, but useful for returnTypes
118            "byte".equals(t) || "char".equals(t) || "short".equals(t) || 
119            "float".equals(t) || "double".equals(t));
120   }
121
122   static void printClinit (PrintWriter pw) {
123     pw.print(INDENT);
124     pw.print(METHOD_PREFIX);
125     pw.print(" void $clinit (");
126     pw.print(ENV_ARG);
127     pw.print(", ");
128     pw.print(CLS_ARG);
129     pw.println(") {");
130     pw.print(INDENT);
131     pw.println("}");
132   }
133
134   static void printFooter (Class<?> cls, PrintWriter pw) {
135     pw.println("}");
136   }
137
138   static void printHeader (Class<?> cls, PrintWriter pw) {
139     if (isSystemPkg) {
140       pw.print("package ");
141       pw.print(SYS_PKG);
142       pw.println(';');
143       pw.println();
144     }
145
146     pw.print("import ");
147     pw.print(MJI_ENV);
148     pw.println(";");
149     pw.print("import ");
150     pw.print(NATIVEPEER);
151     pw.println(";");
152     pw.print("import ");
153     pw.print(MJI_ANNOTATION);
154     pw.println(";");
155     pw.println();
156
157     String cname = cls.getName().replace('.', '_');
158
159     pw.print("public class ");
160     pw.print("JPF_");
161     pw.print(cname);
162     pw.print(" extends ");
163     pw.print(SUPERCLASS);
164     pw.println(" {");
165   }
166
167   static void printMethodBody (String rt, String t, PrintWriter pw) {
168     if (!"void".equals(rt)) {
169       pw.print(INDENT);
170       pw.print(INDENT);
171       pw.print(rt);
172
173       if ((rt == REF_TYPE) && (rt != t)) {
174         pw.print(" r");
175         pw.print(t);
176         pw.print(" = ");
177         pw.print(NULL);
178         pw.println(";");
179
180         pw.print(INDENT);
181         pw.print(INDENT);
182         pw.print("return r");
183         pw.print(t);
184         pw.println(";");
185       } else {
186         pw.print(" v = (");
187         pw.print(rt);
188         pw.println(")0;");
189
190         pw.print(INDENT);
191         pw.print(INDENT);
192         pw.println("return v;");
193       }
194     }
195   }
196
197   static void printMethodName (Method m, PrintWriter pw) {
198     String name = null;
199
200     if (mangleNames) {
201       name = Types.getJNIMangledMethodName(m);
202     } else {
203       name = m.getName();
204     }
205
206     pw.print(name);
207   }
208
209   static void printMJIAnnotation(PrintWriter pw) {
210     pw.print(INDENT);
211     pw.println(MJI_ANN);
212   }
213
214   static void printMethodStub (String condPrefix, Method m, PrintWriter pw) {
215     String t = null;
216     String rt;
217
218     printMJIAnnotation(pw);
219
220     pw.print(INDENT);
221     pw.print(METHOD_PREFIX);
222     pw.print(' ');
223
224     if (condPrefix == null) {
225       t = rt = stripType(m.getReturnType().getName());
226
227       if (!isPrimitiveType(rt)) {
228         rt = REF_TYPE;
229       }
230     } else {
231       rt = "boolean";
232     }
233
234     pw.print(rt);
235
236     pw.print(' ');
237
238     if (condPrefix != null) {
239       pw.print(condPrefix);
240     }
241
242     printMethodName(m, pw);
243     pw.print(" (");
244
245     printStdArgs(m, pw);
246     printTargetArgs(m, pw);
247
248     pw.println(") {");
249
250     if (condPrefix == null) {
251       printMethodBody(rt, stripType(null, t), pw);
252     } else {
253       pw.print(INDENT);
254       pw.print(INDENT);
255       pw.println("return true;");
256     }
257
258     pw.print(INDENT);
259     pw.println('}');
260   }
261
262   static void printNativePeer (Class<?> cls, PrintWriter pw) {
263     Method[] mths = cls.getDeclaredMethods();
264
265     printHeader(cls, pw);
266
267     if (clinit) {
268       printClinit(pw);
269     }
270
271     for (int i = 0; i < mths.length; i++) {
272       Method m = mths[i];
273
274       if (isMJICandidate(m)) {
275         pw.println();
276         printMethodStub(null, m, pw);
277       }
278     }
279
280     printFooter(cls, pw);
281   }
282
283   static void printStdArgs (Method m, PrintWriter pw) {
284     pw.print(ENV_ARG);
285     pw.print(", ");
286
287     if ((m.getModifiers() & Modifier.STATIC) != 0) {
288       pw.print(CLS_ARG);
289     } else {
290       pw.print(OBJ_ARG);
291     }
292   }
293
294   static void printTargetArgs (Method m, PrintWriter pw) {
295     Class<?>[] pt = m.getParameterTypes();
296
297     for (int i = 0; i < pt.length; i++) {
298       String  t = stripType(pt[i].getName());
299       boolean isPrim = isPrimitiveType(t);
300
301       pw.print(", ");
302
303       if (isPrim) {
304         pw.print(t);
305         pw.print(" v");
306         pw.print(i);
307       } else {
308         pw.print(REF_TYPE);
309         pw.print(" r");
310         pw.print(stripType(null, t));
311         pw.print(i);
312       }
313     }
314   }
315
316   static String[] readNames (String[] args, int i) {
317     ArrayList<String> a = null;
318
319     for (; (i < args.length) && (args[i].charAt(0) != '-'); i++) {
320       if (a == null) {
321         a = new ArrayList<String>();
322       }
323
324       a.add(args[i]);
325     }
326
327     if (a != null) {
328       String[] names = new String[a.size()];
329       a.toArray(names);
330
331       return names;
332     }
333
334     return null;
335   }
336
337   static boolean readOptions (String[] args) {
338     for (int i = 0; i < args.length; i++) {
339       String arg = args[i];
340
341       if ("-s".equals(arg)) {
342         isSystemPkg = true;
343       } else if ("-m".equals(arg)) {
344         mangleNames = true;
345       } else if ("-a".equals(arg)) {
346         allMethods = true;
347       } else if ("-ci".equals(arg)) {
348         clinit = true;
349       } else if (arg.charAt(0) != '-') {
350         // rather simple
351         if (clsName == null) {
352           clsName = arg;
353         } else {
354           mths = readNames(args, i);
355           i += mths.length;
356         }
357       } else {
358         System.err.println("unknown option: " + arg);
359         showUsage();
360
361         return false;
362       }
363     }
364
365     return (clsName != null);
366   }
367
368   static void showUsage () {
369     System.out.println(
370           "usage:   'GenPeer [<option>..] <className> [<method>..]'");
371     System.out.println("options:  -s  : system peer class (gov.nasa.jpf.vm)");
372     System.out.println("          -ci : create <clinit> MJI method");
373     System.out.println("          -m  : create mangled method names");
374     System.out.println("          -a  : create MJI methods for all target class methods");
375   }
376
377   static String stripType (String s) {
378     return stripType("java.lang", s);
379   }
380
381   static String stripType (String prefix, String s) {
382     int i = s.lastIndexOf('.') + 1;
383     int l = s.length() - 1;
384
385     if (s.charAt(l) == ';') {
386       s = s.substring(0, l);
387     }
388
389     if (prefix == null) {
390       if (i == 0) {
391         return s;
392       } else {
393         return s.substring(i);
394       }
395     } else {
396       if (s.startsWith(prefix) && (prefix.length() + 1 == i)) {
397         return s.substring(i);
398       } else {
399         return s;
400       }
401     }
402   }
403 }