Mercurial > hg > Members > kono > jpf-core
comparison src/main/gov/nasa/jpf/vm/NativePeer.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children | 3517702bd768 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:61d41facf527 |
---|---|
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.vm; | |
19 | |
20 import gov.nasa.jpf.Config; | |
21 import gov.nasa.jpf.JPF; | |
22 import gov.nasa.jpf.JPFException; | |
23 import gov.nasa.jpf.annotation.MJI; | |
24 import gov.nasa.jpf.util.JPFLogger; | |
25 | |
26 import java.lang.reflect.Constructor; | |
27 import java.lang.reflect.InvocationTargetException; | |
28 import java.lang.reflect.Method; | |
29 import java.lang.reflect.Modifier; | |
30 import java.util.HashMap; | |
31 import java.util.Map; | |
32 import java.util.logging.Level; | |
33 | |
34 | |
35 /** | |
36 * native peer classes are part of MJI and contain the code that is | |
37 * executed by the host VM (i.e. outside the state-tracked JPF VM). Each | |
38 * class executed by JPF that has native mehods must have a native peer class | |
39 * (which is looked up and associated at class loadtime) | |
40 */ | |
41 public class NativePeer implements Cloneable { | |
42 | |
43 static final String MODEL_PACKAGE = "<model>"; | |
44 static final String DEFAULT_PACKAGE = "<default>"; | |
45 | |
46 static JPFLogger logger = JPF.getLogger("class"); | |
47 | |
48 static ClassLoader loader; | |
49 static HashMap<ClassInfo, NativePeer> peers; | |
50 static Config config; | |
51 static boolean noOrphanMethods; | |
52 | |
53 static String[] peerPackages; | |
54 | |
55 ClassInfo ci; | |
56 Class<?> peerClass; | |
57 HashMap<String, Method> methods; | |
58 | |
59 | |
60 public static boolean init (Config conf) { | |
61 loader = conf.getClassLoader(); | |
62 peers = new HashMap<ClassInfo, NativePeer>(); | |
63 | |
64 peerPackages = getPeerPackages(conf); | |
65 | |
66 config = conf; | |
67 noOrphanMethods = conf.getBoolean("vm.no_orphan_methods", false); | |
68 | |
69 return true; | |
70 } | |
71 | |
72 static String[] getPeerPackages (Config conf) { | |
73 String[] defPeerPackages = { MODEL_PACKAGE, "gov.nasa.jpf.vm", DEFAULT_PACKAGE }; | |
74 String[] packages = conf.getStringArray("peer_packages", defPeerPackages); | |
75 | |
76 // internalize | |
77 for (int i=0; i<packages.length; i++) { | |
78 if (packages[i].equals(MODEL_PACKAGE)) { | |
79 packages[i] = MODEL_PACKAGE; | |
80 } else if (packages[i].equals(DEFAULT_PACKAGE)) { | |
81 packages[i] = DEFAULT_PACKAGE; | |
82 } | |
83 } | |
84 | |
85 return packages; | |
86 } | |
87 | |
88 static Class<?> locatePeerCls (String clsName) { | |
89 String cn = "JPF_" + clsName.replace('.', '_'); | |
90 | |
91 for (int i=0; i<peerPackages.length; i++) { | |
92 String pcn; | |
93 String pkg = peerPackages[i]; | |
94 | |
95 if (pkg == MODEL_PACKAGE) { | |
96 int j = clsName.lastIndexOf('.'); | |
97 pcn = clsName.substring(0, j+1) + cn; | |
98 } else if (pkg == DEFAULT_PACKAGE) { | |
99 pcn = cn; | |
100 } else { | |
101 pcn = pkg + '.' + cn; | |
102 } | |
103 | |
104 try { | |
105 Class<?> peerCls = loader.loadClass(pcn); | |
106 | |
107 if ((peerCls.getModifiers() & Modifier.PUBLIC) == 0) { | |
108 logger.warning("non-public peer class: ", pcn); | |
109 continue; // pointless to use this one, it would just create IllegalAccessExceptions | |
110 } | |
111 | |
112 logger.info("loaded peer class: ", pcn); | |
113 | |
114 return peerCls; | |
115 } catch (ClassNotFoundException cnfx) { | |
116 // try next one | |
117 } | |
118 } | |
119 | |
120 return null; // nothing found | |
121 } | |
122 | |
123 /** | |
124 * this becomes the factory method to load either a plain (slow) | |
125 * reflection-based peer (a NativePeer object), or some speed optimized | |
126 * derived class object. | |
127 * Watch out - this gets called before the ClassInfo is fully initialized | |
128 * (we shouldn't rely on more than just its name here) | |
129 */ | |
130 static NativePeer getNativePeer (ClassInfo ci) { | |
131 String clsName = ci.getName(); | |
132 NativePeer peer = peers.get(ci); | |
133 Class<?> peerCls = null; | |
134 | |
135 if (peer == null) { | |
136 peerCls = locatePeerCls(clsName); | |
137 | |
138 if (peerCls != null) { | |
139 initializePeerClass( peerCls); | |
140 | |
141 if (logger.isLoggable(Level.INFO)) { | |
142 logger.info("load peer: ", peerCls.getName()); | |
143 } | |
144 | |
145 peer = getInstance(peerCls, NativePeer.class); | |
146 peer.initialize(peerCls, ci, true); | |
147 | |
148 peers.put(ci, peer); | |
149 } | |
150 } | |
151 | |
152 return peer; | |
153 } | |
154 | |
155 public static <T> T getInstance(Class<?> cls, Class<T> type) throws JPFException { | |
156 Class<?>[] argTypes = Config.CONFIG_ARGTYPES; | |
157 Object[] args = config.CONFIG_ARGS; | |
158 | |
159 return getInstance(cls, type, argTypes, args); | |
160 } | |
161 | |
162 public static <T> T getInstance(Class<?> cls, Class<T> type, Class<?>[] argTypes, | |
163 Object[] args) throws JPFException { | |
164 Object o = null; | |
165 Constructor<?> ctor = null; | |
166 | |
167 if (cls == null) { | |
168 return null; | |
169 } | |
170 | |
171 while (o == null) { | |
172 try { | |
173 ctor = cls.getConstructor(argTypes); | |
174 o = ctor.newInstance(args); | |
175 } catch (NoSuchMethodException nmx) { | |
176 | |
177 if ((argTypes.length > 1) || ((argTypes.length == 1) && (argTypes[0] != Config.class))) { | |
178 // fallback 1: try a single Config param | |
179 argTypes = Config.CONFIG_ARGTYPES; | |
180 args = config.CONFIG_ARGS; | |
181 } else if (argTypes.length > 0) { | |
182 // fallback 2: try the default ctor | |
183 argTypes = Config.NO_ARGTYPES; | |
184 args = Config.NO_ARGS; | |
185 | |
186 } else { | |
187 // Ok, there is no suitable ctor, bail out | |
188 throw new JPFException("no suitable ctor found for the peer class " + cls.getName()); | |
189 } | |
190 } catch (IllegalAccessException iacc) { | |
191 throw new JPFException("ctor not accessible: " | |
192 + config.getMethodSignature(ctor)); | |
193 } catch (IllegalArgumentException iarg) { | |
194 throw new JPFException("illegal constructor arguments: " | |
195 + config.getMethodSignature(ctor)); | |
196 } catch (InvocationTargetException ix) { | |
197 Throwable tx = ix.getCause(); | |
198 throw new JPFException("exception " + tx + " occured in " | |
199 + config.getMethodSignature(ctor)); | |
200 } catch (InstantiationException ivt) { | |
201 throw new JPFException("abstract class cannot be instantiated"); | |
202 } catch (ExceptionInInitializerError eie) { | |
203 throw new JPFException("static initialization failed:\n>> " | |
204 + eie.getException(), eie.getException()); | |
205 } | |
206 } | |
207 | |
208 // check type | |
209 if (!cls.isInstance(o)) { | |
210 throw new JPFException("instance not of type: " | |
211 + cls.getName()); | |
212 } | |
213 | |
214 return type.cast(o); // safe according to above | |
215 } | |
216 | |
217 static String getPeerDispatcherClassName (String clsName) { | |
218 return (clsName + '$'); | |
219 } | |
220 | |
221 public Class<?> getPeerClass() { | |
222 return peerClass; | |
223 } | |
224 | |
225 public String getPeerClassName() { | |
226 return peerClass.getName(); | |
227 } | |
228 | |
229 protected void initialize (Class<?> peerClass, ClassInfo ci, boolean cacheMethods) { | |
230 if ((this.ci != null) || (this.peerClass != null)) { | |
231 throw new RuntimeException("cannot re-initialize NativePeer: " + | |
232 peerClass.getName()); | |
233 } | |
234 | |
235 this.ci = ci; | |
236 this.peerClass = peerClass; | |
237 | |
238 loadMethods(cacheMethods); | |
239 } | |
240 | |
241 protected static void initializePeerClass( Class<?> cls) { | |
242 try { | |
243 Method m = cls.getDeclaredMethod("init", Config.class ); | |
244 try { | |
245 m.invoke(null, config); | |
246 } catch (IllegalArgumentException iax){ | |
247 // can't happen - static method | |
248 } catch (IllegalAccessException iacx) { | |
249 throw new RuntimeException("peer initialization method not accessible: " | |
250 + cls.getName()); | |
251 } catch (InvocationTargetException itx){ | |
252 throw new RuntimeException("initialization of peer " + | |
253 cls.getName() + " failed: " + itx.getCause()); | |
254 | |
255 } | |
256 } catch (NoSuchMethodException nsmx){ | |
257 // nothing to do | |
258 } | |
259 } | |
260 | |
261 private static boolean isMJICandidate (Method mth) { | |
262 | |
263 // the native peer should be annotated with @MJI | |
264 if(!mth.isAnnotationPresent(MJI.class)) { | |
265 return false; | |
266 } | |
267 | |
268 // this native peer should be Public | |
269 if(!Modifier.isPublic(mth.getModifiers())) { | |
270 return false; | |
271 } | |
272 | |
273 // native method always have a MJIEnv and int as the first parameters | |
274 Class<?>[] argTypes = mth.getParameterTypes(); | |
275 if ((argTypes.length >= 2) && (argTypes[0] == MJIEnv.class) && (argTypes[1] == int.class) ) { | |
276 return true; | |
277 } else { | |
278 return false; | |
279 } | |
280 } | |
281 | |
282 | |
283 private Method getMethod (MethodInfo mi) { | |
284 return getMethod(null, mi); | |
285 } | |
286 | |
287 private Method getMethod (String prefix, MethodInfo mi) { | |
288 String name = mi.getUniqueName(); | |
289 | |
290 if (prefix != null) { | |
291 name = prefix + name; | |
292 } | |
293 | |
294 return methods.get(name); | |
295 } | |
296 | |
297 /** | |
298 * look at all @MJI annotated methods in the peer and set their | |
299 * corresponding model class MethodInfo attributes | |
300 * <2do> pcm - this is too long, break it down | |
301 */ | |
302 protected void loadMethods (boolean cacheMethods) { | |
303 // since we allow native peer class hierarchies, we have to look at all methods | |
304 //Method[] m = peerClass.getDeclaredMethods(); | |
305 Method[] m = peerClass.getMethods(); | |
306 | |
307 methods = new HashMap<String, Method>(m.length); | |
308 | |
309 Map<String,MethodInfo> methodInfos = ci.getDeclaredMethods(); | |
310 MethodInfo[] mis = null; | |
311 | |
312 for (int i = 0; i < m.length; i++) { | |
313 Method mth = m[i]; | |
314 | |
315 if (isMJICandidate(mth)) { | |
316 // Note that we can't mangle the name automatically, since we loose the | |
317 // object type info (all mapped to int). This has to be handled | |
318 // the same way like with overloaded JNI methods - you have to | |
319 // mangle them manually | |
320 String mn = mth.getName(); | |
321 | |
322 // JNI doesn't allow <clinit> or <init> to be native, but MJI does | |
323 // (you should know what you are doing before you use that, really) | |
324 if (mn.startsWith("$clinit")) { | |
325 mn = "<clinit>"; | |
326 } else if (mn.startsWith("$init")) { | |
327 mn = "<init>" + mn.substring(5); | |
328 } | |
329 | |
330 String mname = Types.getJNIMethodName(mn); | |
331 String sig = Types.getJNISignature(mn); | |
332 | |
333 if (sig != null) { | |
334 mname += sig; | |
335 } | |
336 | |
337 // now try to find a corresponding MethodInfo object and mark it | |
338 // as 'peer-ed' | |
339 // <2do> in case of <clinit>, it wouldn't be strictly required to | |
340 // have a MethodInfo upfront (we could create it). Might be handy | |
341 // for classes where we intercept just a few methods, but need | |
342 // to init before | |
343 MethodInfo mi = methodInfos.get(mname); | |
344 | |
345 if ((mi == null) && (sig == null)) { | |
346 // nothing found, we have to do it the hard way - check if there is | |
347 // a single method with this name (still unsafe, but JNI behavior) | |
348 // Note there's no point in doing that if we do have a signature | |
349 if (mis == null) { // cache it for subsequent lookup | |
350 mis = new MethodInfo[methodInfos.size()]; | |
351 methodInfos.values().toArray(mis); | |
352 } | |
353 | |
354 mi = searchMethod(mname, mis); | |
355 } | |
356 | |
357 if (mi != null) { | |
358 logger.info("load MJI method: ", mname); | |
359 | |
360 NativeMethodInfo miNative = new NativeMethodInfo(mi, mth, this); | |
361 miNative.replace(mi); | |
362 | |
363 } else { | |
364 checkOrphan(mth, mname); | |
365 } | |
366 } | |
367 } | |
368 } | |
369 | |
370 protected void checkOrphan (Method mth, String mname){ | |
371 if (!ignoreOrphan(mth)) { | |
372 // we have an orphan method, i.e. a peer method that does not map into any model method | |
373 // This is usually a signature typo or an out-of-sync peer, but could also be a | |
374 // MJI method in a peer superclass which is bound to a MethodInfo in a model superclass | |
375 | |
376 Class<?> implCls = mth.getDeclaringClass(); | |
377 if (implCls != peerClass) { | |
378 ClassInfo ciSuper = ci.getSuperClass(); | |
379 if (ciSuper != null){ | |
380 MethodInfo mi = ciSuper.getMethod(mname, true); | |
381 if (mi != null){ | |
382 if (mi instanceof NativeMethodInfo){ | |
383 NativeMethodInfo nmi = (NativeMethodInfo)mi; | |
384 if (nmi.getMethod().equals(mth)){ | |
385 return; | |
386 } | |
387 } | |
388 } | |
389 } | |
390 } | |
391 | |
392 String message = "orphan NativePeer method: " + ci.getName() + '.' + mname; | |
393 | |
394 if (noOrphanMethods) { | |
395 throw new JPFException(message); | |
396 } else { | |
397 // issue a warning if we have a NativePeer native method w/o a corresponding | |
398 // method in the model class (this might happen due to compiler optimizations | |
399 // silently skipping empty methods) | |
400 logger.warning(message); | |
401 } | |
402 } | |
403 } | |
404 | |
405 protected boolean ignoreOrphan (Method m){ | |
406 MJI annotation = m.getAnnotation(MJI.class); | |
407 return annotation.noOrphanWarning(); | |
408 } | |
409 | |
410 private static MethodInfo searchMethod (String mname, MethodInfo[] methods) { | |
411 int idx = -1; | |
412 | |
413 for (int j = 0; j < methods.length; j++) { | |
414 if (methods[j].getName().equals(mname)) { | |
415 // if this is actually a overloaded method, and the first one | |
416 // isn't the right choice, we would get an IllegalArgumentException, | |
417 // hence we have to go on and make sure it's not overloaded | |
418 | |
419 if (idx == -1) { | |
420 idx = j; | |
421 } else { | |
422 throw new JPFException("overloaded native method without signature: " + mname); | |
423 } | |
424 } | |
425 } | |
426 | |
427 if (idx >= 0) { | |
428 return methods[idx]; | |
429 } else { | |
430 return null; | |
431 } | |
432 } | |
433 } | |
434 |