0fcc83077183e0db0cbeaf21215274a5a5d60ae7
[jpf-core.git] / src / peers / gov / nasa / jpf / vm / JPF_java_lang_reflect_Method.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.vm;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.annotation.MJI;
22 import gov.nasa.jpf.util.MethodInfoRegistry;
23 import gov.nasa.jpf.util.RunListener;
24 import gov.nasa.jpf.util.RunRegistry;
25
26 import java.lang.reflect.Modifier;
27 import java.util.ArrayList;
28
29 public class JPF_java_lang_reflect_Method extends NativePeer {
30
31   static MethodInfoRegistry registry;
32
33   // class init - this is called automatically from the NativePeer ctor
34   public static boolean init (Config conf) {
35     // this is an example of how to handle cross-initialization between
36     // native peers - this might also get explicitly called by the java.lang.Class
37     // peer, since it creates Method objects. Here we have to make sure
38     // we only reset between JPF runs
39
40     if (registry == null){
41       registry = new MethodInfoRegistry();
42       
43       RunRegistry.getDefaultRegistry().addListener( new RunListener() {
44         @Override
45                 public void reset (RunRegistry reg){
46           registry = null;
47         }
48       });
49     }
50     return true;
51   }
52
53   static int createMethodObject (MJIEnv env, ClassInfo ciMth, MethodInfo mi){
54     // note - it is the callers responsibility to ensure Method is properly initialized    
55     int regIdx = registry.registerMethodInfo(mi);
56     int eidx = env.newObject( ciMth);
57     ElementInfo ei = env.getModifiableElementInfo(eidx);
58     
59     ei.setIntField("regIdx", regIdx);
60     ei.setBooleanField("isAccessible", mi.isPublic());
61     
62     return eidx;
63   }
64   
65   // this is NOT an MJI method, but it is used outside this package, so
66   // we have to add 'final'
67   public static final MethodInfo getMethodInfo (MJIEnv env, int objRef){
68     return registry.getMethodInfo(env,objRef, "regIdx");
69   }
70   
71   @MJI
72   public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef) {
73     MethodInfo mi = getMethodInfo(env, objRef);
74     
75     int nameRef = env.getReferenceField( objRef, "name");
76     if (nameRef == MJIEnv.NULL) {
77       nameRef = env.newString(mi.getName());
78       env.setReferenceField(objRef, "name", nameRef);
79     }
80    
81     return nameRef;
82   }
83
84   @MJI
85   public int getModifiers____I (MJIEnv env, int objRef){
86     MethodInfo mi = getMethodInfo(env, objRef);
87     return mi.getModifiers();
88   }
89
90   static int getParameterTypes( MJIEnv env, MethodInfo mi) {
91     ThreadInfo ti = env.getThreadInfo();
92     String[] argTypeNames = mi.getArgumentTypeNames();
93     int[] ar = new int[argTypeNames.length];
94
95     for (int i = 0; i < argTypeNames.length; i++) {
96       ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(argTypeNames[i]);
97       if (!ci.isRegistered()) {
98         ci.registerClass(ti);
99       }
100
101       ar[i] = ci.getClassObjectRef();
102     }
103
104     int aRef = env.newObjectArray("Ljava/lang/Class;", argTypeNames.length);
105     for (int i = 0; i < argTypeNames.length; i++) {
106       env.setReferenceArrayElement(aRef, i, ar[i]);
107     }
108
109     return aRef;
110   }
111
112   @MJI
113   public int getParameterTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef){
114     return getParameterTypes(env, getMethodInfo(env, objRef));
115   }
116
117   // TODO: Fix for Groovy's model-checking
118   private static int getParameterizedTypeImplObj(String signature, MJIEnv env) {
119
120     ThreadInfo ti = env.getThreadInfo();
121     ClassLoaderInfo cli = env.getSystemClassLoaderInfo();
122     ClassInfo ci = cli.getResolvedClassInfo("sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl");
123     // Create a new object of type ParameterizedTypeImpl
124     int paramTypeRef = env.newObject(ci);
125     ElementInfo ei = env.getModifiableElementInfo(paramTypeRef);
126     // Get field information and fill out the fields
127     // rawType field
128     String className = Types.getGenericClassName(signature);
129     ClassInfo gci = cli.getResolvedClassInfo(className);
130     if (!gci.isRegistered()) {
131         gci.registerClass(ti);
132     }
133     ei.setReferenceField("rawType", gci.getClassObjectRef());
134
135     // actualTypeArguments
136     String[] parameterizedTypes = Types.getParameterizedTypesFromArgumentTypeNames(signature);
137     int[] types = new int[parameterizedTypes.length];
138     for(int j = 0; j < parameterizedTypes.length; j++) {
139       ClassInfo pci = cli.getResolvedClassInfo(parameterizedTypes[j]);
140       if (!pci.isRegistered()) {
141         pci.registerClass(ti);
142       }
143       types[j] = pci.getClassObjectRef();
144     }
145     int aRef = env.newObjectArray("Ljava/lang/reflect/Type;", parameterizedTypes.length);
146     // Set references for every array element
147     for (int j = 0; j < parameterizedTypes.length; j++) {
148       env.setReferenceArrayElement(aRef, j, types[j]);
149     }
150     ei.setReferenceField("actualTypeArguments", aRef);
151
152     // ownerType
153     String ownerType = Types.getOwnerClassName(signature);
154     if (ownerType != null) {
155       ClassInfo oci = ClassLoaderInfo.getCurrentResolvedClassInfo(ownerType);
156       if (!oci.isRegistered()) {
157         oci.registerClass(ti);
158       }
159       ei.setReferenceField("ownerType", oci.getClassObjectRef());
160     } else {
161       ei.setReferenceField("ownerType", MJIEnv.NULL);
162     }
163
164     return paramTypeRef;
165   }
166
167   static int getGenericParameterTypes( MJIEnv env, int objRef, MethodInfo mi) {
168     ThreadInfo ti = env.getThreadInfo();
169     String[] argTypeNames = mi.getArgumentGenericTypeNames();
170     int[] ar = new int[argTypeNames.length];
171
172     for (int i = 0; i < argTypeNames.length; i++) {
173       // Change this into just the generic class type if it is a generic class
174       if (Types.isGenericSignature(argTypeNames[i])) {
175         ar[i] = getParameterizedTypeImplObj(argTypeNames[i], env);
176       } else {
177         ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(argTypeNames[i]);
178         if (!ci.isRegistered()) {
179           ci.registerClass(ti);
180         }
181         ar[i] = ci.getClassObjectRef();
182       }
183     }
184     int aRef = env.newObjectArray("Ljava/lang/reflect/Type;", argTypeNames.length);
185     for (int i = 0; i < argTypeNames.length; i++) {
186       env.setReferenceArrayElement(aRef, i, ar[i]);
187     }
188
189     return aRef;
190   }
191
192   @MJI
193   public int getGenericParameterTypes_____3Ljava_lang_reflect_Type_2 (MJIEnv env, int objRef){
194     return getGenericParameterTypes(env, objRef, getMethodInfo(env, objRef));
195   }
196
197   @MJI
198   public int getGenericReturnType____Ljava_lang_reflect_Type_2 (MJIEnv env, int objRef){
199     MethodInfo mi = getMethodInfo(env, objRef);
200     ThreadInfo ti = env.getThreadInfo();
201
202     String returnTypeName = mi.getGenericReturnTypeName();
203     int retRef;
204     if (Types.isGenericSignature(returnTypeName)) {
205         retRef = getParameterizedTypeImplObj(returnTypeName, env);
206     } else {
207       ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(returnTypeName);
208       if (!ci.isRegistered()) {
209         ci.registerClass(ti);
210       }
211       retRef = ci.getClassObjectRef();
212     }
213
214     return retRef;
215   }
216   // TODO: Fix for Groovy's model-checking
217   // TODO: We have been able to only register the generic class and not yet the parameterized types
218
219   int getExceptionTypes(MJIEnv env, MethodInfo mi) {
220     ThreadInfo ti = env.getThreadInfo();
221     String[] exceptionNames = mi.getThrownExceptionClassNames();
222      
223     if (exceptionNames == null) {
224       exceptionNames = new String[0];
225     }
226      
227     int[] ar = new int[exceptionNames.length];
228      
229     for (int i = 0; i < exceptionNames.length; i++) {
230       ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(exceptionNames[i]);
231       if (!ci.isRegistered()) {
232         ci.registerClass(ti);
233       }
234        
235       ar[i] = ci.getClassObjectRef();
236     }
237      
238     int aRef = env.newObjectArray("Ljava/lang/Class;", exceptionNames.length);
239     for (int i = 0; i < exceptionNames.length; i++) {
240       env.setReferenceArrayElement(aRef, i, ar[i]);
241     }
242      
243     return aRef;
244   }
245   
246   @MJI
247   public int getExceptionTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef) {
248     return getExceptionTypes(env, getMethodInfo(env, objRef));
249   }
250   
251   @MJI
252   public int getDefaultValue____Ljava_lang_Object_2(MJIEnv env, int objRef) {
253     MethodInfo mi = getMethodInfo(env, objRef);
254     ClassInfo ci = mi.getClassInfo();
255     if(!ci.isInterface() || ci.getDirectInterfaceNames() == null || ci.getDirectInterfaceNames().length != 1 || !ci.getDirectInterfaceNames()[0].equals("java.lang.annotation.Annotation")) {
256       return MJIEnv.NULL;
257     }
258     String annotationName = ci.getName();
259     AnnotationInfo ai = ci.getClassLoaderInfo().getResolvedAnnotationInfo(annotationName);
260     Object o = ai.getValue(mi.getName());
261     if(o == null) {
262       return MJIEnv.NULL;
263     }
264     try {
265       return env.liftNativeAnnotationValue(Types.getTypeName(mi.getReturnType()), o);
266     } catch(ClinitRequired e) {
267       env.handleClinitRequest(e.getRequiredClassInfo());
268       return -1;
269     }
270   }
271   
272   @MJI
273   public int getReturnType____Ljava_lang_Class_2 (MJIEnv env, int objRef){
274     MethodInfo mi = getMethodInfo(env, objRef);
275     ThreadInfo ti = env.getThreadInfo();
276
277     ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(mi.getReturnTypeName());
278     if (!ci.isRegistered()) {
279       ci.registerClass(ti);
280     }
281
282     return ci.getClassObjectRef();
283   }
284   
285   @MJI
286   public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objRef){
287     MethodInfo mi = getMethodInfo(env, objRef);    
288     ClassInfo ci = mi.getClassInfo();
289     // it's got to be registered, otherwise we wouldn't be able to acquire the Method object
290     return ci.getClassObjectRef();
291   }
292     
293   static int createBoxedReturnValueObject (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame) {
294     byte rt = mi.getReturnTypeCode();
295     int ret = MJIEnv.NULL;
296     ElementInfo rei;
297     Object attr = null;
298
299     if (rt == Types.T_DOUBLE) {
300       attr = frame.getLongResultAttr();
301       double v = frame.getDoubleResult();
302       ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Double"));
303       rei = env.getModifiableElementInfo(ret);
304       rei.setDoubleField("value", v);
305     } else if (rt == Types.T_FLOAT) {
306       attr = frame.getResultAttr();
307       float v = frame.getFloatResult();
308       ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Float"));
309       rei = env.getModifiableElementInfo(ret);
310       rei.setFloatField("value", v);
311     } else if (rt == Types.T_LONG) {
312       attr = frame.getLongResultAttr();
313       long v = frame.getLongResult();
314       ret = env.valueOfLong(v);
315     } else if (rt == Types.T_BYTE) {
316       attr = frame.getResultAttr();
317       int v = frame.getResult(); 
318       ret = env.valueOfByte((byte)v);
319     } else if (rt == Types.T_CHAR) {
320       attr = frame.getResultAttr();
321       int v = frame.getResult(); 
322       ret = env.valueOfCharacter((char)v);
323     } else if (rt == Types.T_SHORT) {
324       attr = frame.getResultAttr();
325       int v = frame.getResult(); 
326       ret = env.valueOfShort((short)v);
327     } else if (rt == Types.T_INT) {
328       attr = frame.getResultAttr();
329       int v = frame.getResult(); 
330       ret = env.valueOfInteger(v);
331     } else if (rt == Types.T_BOOLEAN) {
332       attr = frame.getResultAttr();
333       int v = frame.getResult();
334       ret = env.valueOfBoolean((v == 1)? true: false);
335     } else if (mi.isReferenceReturnType()){ 
336       attr = frame.getResultAttr();
337       ret = frame.getReferenceResult();
338     }
339
340     env.setReturnAttribute(attr);
341     return ret;
342   }
343
344   static boolean pushUnboxedArguments (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame, int argIdx, int argsRef) {
345     ElementInfo source;
346     ClassInfo sourceClass;
347     String destTypeNames[];
348     int nArgs, passedCount, sourceRef;
349     byte sourceType, destTypes[];
350
351     destTypes     = mi.getArgumentTypes();
352     destTypeNames = mi.getArgumentTypeNames();
353     nArgs         = destTypeNames.length;
354     
355     // according to the API docs, passing null instead of an empty array is allowed for no args
356     passedCount   = (argsRef != MJIEnv.NULL) ? env.getArrayLength(argsRef) : 0;
357     
358     if (nArgs != passedCount) {
359       env.throwException(IllegalArgumentException.class.getName(), "Wrong number of arguments passed.  Actual = " + passedCount + ".  Expected = " + nArgs);
360       return false;
361     }
362     
363     for (int i = 0; i < nArgs; i++) {
364       sourceRef = env.getReferenceArrayElement(argsRef, i);
365
366       // we have to handle null references explicitly
367       if (sourceRef == MJIEnv.NULL) {
368         if ((destTypes[i] != Types.T_REFERENCE) && (destTypes[i] != Types.T_ARRAY)) {
369           env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Actual = (null).  Expected = " + destTypeNames[i]);
370           return false;
371         } 
372          
373         frame.pushRef(MJIEnv.NULL);
374         continue;
375       }
376
377       source      = env.getElementInfo(sourceRef);
378       sourceClass = source.getClassInfo();   
379       sourceType = getSourceType( sourceClass, destTypes[i], destTypeNames[i]);
380
381       Object attr = env.getElementInfo(argsRef).getFields().getFieldAttr(i);
382       if ((argIdx = pushArg( argIdx, frame, source, sourceType, destTypes[i], attr)) < 0 ){
383         env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Source Class = " + sourceClass.getName() + ".  Dest Class = " + destTypeNames[i]);
384         return false;        
385       }
386     }
387     
388     return true;
389   }
390
391   // this returns the primitive type in case we have to unbox, and otherwise checks reference type compatibility
392   private static byte getSourceType (ClassInfo ciArgVal, byte destType, String destTypeName){
393     switch (destType){
394     // the primitives
395     case Types.T_BOOLEAN:
396     case Types.T_BYTE:
397     case Types.T_CHAR:
398     case Types.T_SHORT:
399     case Types.T_INT:
400     case Types.T_LONG:
401     case Types.T_FLOAT:
402     case Types.T_DOUBLE:
403       return Types.getUnboxedType(ciArgVal.getName());
404       
405     case Types.T_ARRAY:
406     case Types.T_REFERENCE: // check if the source type is assignment compatible with the destType
407       if (ciArgVal.isInstanceOf(destTypeName)){
408         return destType;
409       }
410     }
411     
412     return Types.T_NONE;
413   }
414   
415   // do the proper type conversion - Java is pretty forgiving here and does
416   // not throw exceptions upon value truncation
417   private static int pushArg( int argIdx, DirectCallStackFrame frame, ElementInfo eiArg, byte srcType, byte destType, Object attr){    
418     switch (srcType) {
419     case Types.T_DOUBLE:
420     {
421       double v = eiArg.getDoubleField("value");
422       if (destType == Types.T_DOUBLE){
423         return frame.setDoubleArgument( argIdx, v, attr);
424       }
425       return -1;
426     }
427     case Types.T_FLOAT: // covers float, double
428     {
429       float v = eiArg.getFloatField("value");
430       switch (destType){
431       case Types.T_FLOAT:
432         return frame.setFloatArgument( argIdx, v, attr);
433       case Types.T_DOUBLE:
434         return frame.setDoubleArgument( argIdx, v, attr);
435       }
436       return -1;
437     }
438     case Types.T_LONG:
439     {
440       long v = eiArg.getLongField("value");
441       switch (destType){
442       case Types.T_LONG:
443         return frame.setLongArgument(argIdx, v, attr);
444       case Types.T_FLOAT:
445         return frame.setFloatArgument(argIdx, v, attr);
446       case Types.T_DOUBLE:
447         return frame.setDoubleArgument( argIdx, v, attr);
448       }
449       return -1;
450     }
451     case Types.T_INT:
452     { 
453       int v = eiArg.getIntField("value");
454       switch (destType){
455       case Types.T_INT:
456         return frame.setArgument( argIdx, v, attr);
457       case Types.T_LONG:
458         return frame.setLongArgument( argIdx, v, attr);
459       case Types.T_FLOAT:
460         return frame.setFloatArgument(argIdx, v, attr);
461       case Types.T_DOUBLE:
462         return frame.setDoubleArgument( argIdx, v, attr);
463       }
464       return -1;
465     }
466     case Types.T_SHORT:
467     { 
468       int v = eiArg.getShortField("value");
469       switch (destType){
470       case Types.T_SHORT:
471       case Types.T_INT:
472         return frame.setArgument( argIdx, v, attr);
473       case Types.T_LONG:
474         return frame.setLongArgument( argIdx, v, attr);
475       case Types.T_FLOAT:
476         return frame.setFloatArgument(argIdx, v, attr);
477       case Types.T_DOUBLE:
478         return frame.setDoubleArgument( argIdx, v, attr);
479       }
480       return -1;
481     }
482     case Types.T_BYTE:
483     { 
484       byte v = eiArg.getByteField("value");
485       switch (destType){
486       case Types.T_BYTE:
487       case Types.T_SHORT:
488       case Types.T_INT:
489         return frame.setArgument( argIdx, v, attr);
490       case Types.T_LONG:
491         return frame.setLongArgument( argIdx, v, attr);
492       case Types.T_FLOAT:
493         return frame.setFloatArgument(argIdx, v, attr);
494       case Types.T_DOUBLE:
495         return frame.setDoubleArgument( argIdx, v, attr);
496       }
497       return -1;
498     }
499     case Types.T_CHAR:
500     {
501       char v = eiArg.getCharField("value");
502       switch (destType){
503       case Types.T_CHAR:
504       case Types.T_INT:
505         return frame.setArgument( argIdx, v, attr);
506       case Types.T_LONG:
507         return frame.setLongArgument( argIdx, v, attr);
508       case Types.T_FLOAT:
509         return frame.setFloatArgument(argIdx, v, attr);
510       case Types.T_DOUBLE:
511         return frame.setDoubleArgument( argIdx, v, attr);
512       }
513       return -1;
514     }
515     case Types.T_BOOLEAN:
516     {
517       boolean v = eiArg.getBooleanField("value");
518       if (destType == Types.T_BOOLEAN){
519         return frame.setArgument( argIdx, v ? 1 : 0, attr);
520       }
521       return -1;
522     }
523     case Types.T_ARRAY:
524     {
525       int ref =  eiArg.getObjectRef();
526       if (destType == Types.T_ARRAY){
527         return frame.setReferenceArgument( argIdx, ref, attr);
528       }
529       return -1;
530     }
531     case Types.T_REFERENCE:
532     {
533       int ref =  eiArg.getObjectRef();
534       if (destType == Types.T_REFERENCE){
535         return frame.setReferenceArgument( argIdx, ref, attr);
536       }
537       return -1;
538     }
539     default:
540       // T_VOID, T_NONE
541       return -1;
542     }
543   }
544
545   @MJI
546   public int invoke__Ljava_lang_Object_2_3Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int mthRef, int objRef, int argsRef) {
547     ThreadInfo ti = env.getThreadInfo();
548     MethodInfo miCallee = getMethodInfo(env, mthRef);
549     ClassInfo calleeClass = miCallee.getClassInfo();
550     DirectCallStackFrame frame = ti.getReturnedDirectCall();
551     
552     if (frame == null){ // first time
553
554       //--- check the instance we are calling on
555       if (!miCallee.isStatic()) {
556         if (objRef == MJIEnv.NULL){
557           env.throwException("java.lang.NullPointerException");
558           return MJIEnv.NULL;
559           
560         } else {
561           ElementInfo eiObj = ti.getElementInfo(objRef);
562           ClassInfo objClass = eiObj.getClassInfo();
563         
564           if (!objClass.isInstanceOf(calleeClass)) {
565             env.throwException(IllegalArgumentException.class.getName(), "Object is not an instance of declaring class.  Actual = " + objClass + ".  Expected = " + calleeClass);
566             return MJIEnv.NULL;
567           }
568         }
569       }
570
571       //--- check accessibility
572       ElementInfo eiMth = ti.getElementInfo(mthRef);
573       if (! (Boolean) eiMth.getFieldValueObject("isAccessible")) {
574         StackFrame caller = ti.getTopFrame().getPrevious();
575         ClassInfo callerClass = caller.getClassInfo();
576
577         if (callerClass != calleeClass) {
578           env.throwException(IllegalAccessException.class.getName(), "Class " + callerClass.getName() +
579                   " can not access a member of class " + calleeClass.getName()
580                   + " with modifiers \"" + Modifier.toString(miCallee.getModifiers()));
581           return MJIEnv.NULL;
582         }
583       }
584       
585       //--- push the direct call
586       frame = miCallee.createDirectCallStackFrame(ti, 0);
587       frame.setReflection();
588       
589       int argOffset = 0;
590       if (!miCallee.isStatic()) {
591         frame.setReferenceArgument( argOffset++, objRef, null);
592       }
593       if (!pushUnboxedArguments( env, miCallee, frame, argOffset, argsRef)) {
594         // we've got a IllegalArgumentException
595         return MJIEnv.NULL;  
596       }
597       ti.pushFrame(frame);
598       
599       
600       //--- check for and push required clinits
601       if (miCallee.isStatic()){
602         calleeClass.initializeClass(ti);
603       }
604       
605       return MJIEnv.NULL; // reexecute
606       
607     } else { // we have returned from the direct call
608       return createBoxedReturnValueObject( env, miCallee, frame);
609     }
610   }
611   
612
613   // this one has to collect annotations upwards in the inheritance chain
614   static int getAnnotations (MJIEnv env, MethodInfo mi){
615     String mname = mi.getName();
616     String msig = mi.genericSignature;
617     ArrayList<AnnotationInfo> aiList = new ArrayList<AnnotationInfo>();
618     
619     // our own annotations
620     ClassInfo ci = mi.getClassInfo();
621     for (AnnotationInfo ai : mi.getAnnotations()) {
622       aiList.add(ai);
623     }
624     
625     // our superclass annotations
626     for (ci = ci.getSuperClass(); ci != null; ci = ci.getSuperClass()){
627       mi = ci.getMethod(mname, msig, false);
628       if (mi != null){
629         for (AnnotationInfo ai: mi.getAnnotations()){
630           aiList.add(ai);
631         }        
632       }
633     }
634
635     try {
636       return env.newAnnotationProxies(aiList.toArray(new AnnotationInfo[aiList.size()]));
637     } catch (ClinitRequired x){
638       env.handleClinitRequest(x.getRequiredClassInfo());
639       return MJIEnv.NULL;
640     }    
641   }
642
643   @MJI
644   public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
645     return getAnnotations( env, getMethodInfo(env,mthRef));
646   }
647   
648   // the following ones consist of a package default implementation that is shared with
649   // the constructor peer, and a public model method
650   static int getAnnotation (MJIEnv env, MethodInfo mi, int annotationClsRef){
651     ClassInfo aci = env.getReferredClassInfo(annotationClsRef);
652     
653     AnnotationInfo ai = mi.getAnnotation(aci.getName());
654     if (ai != null){
655       ClassInfo aciProxy = aci.getAnnotationProxy();
656       try {
657         return env.newAnnotationProxy(aciProxy, ai);
658       } catch (ClinitRequired x){
659         env.handleClinitRequest(x.getRequiredClassInfo());
660         return MJIEnv.NULL;
661       }
662     }
663     
664     return MJIEnv.NULL;
665   }  
666
667   @MJI
668   public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef, int annotationClsRef) {
669     return getAnnotation(env, getMethodInfo(env,mthRef), annotationClsRef);
670   }
671   
672   static int getDeclaredAnnotations (MJIEnv env, MethodInfo mi){
673     AnnotationInfo[] ai = mi.getAnnotations();
674
675     try {
676       return env.newAnnotationProxies(ai);
677     } catch (ClinitRequired x){
678       env.handleClinitRequest(x.getRequiredClassInfo());
679       return MJIEnv.NULL;
680     }    
681   }
682
683   @MJI
684   public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
685     return getDeclaredAnnotations( env, getMethodInfo(env,mthRef));
686   }
687   
688   static int getParameterAnnotations (MJIEnv env, MethodInfo mi){
689     AnnotationInfo[][] pa = mi.getParameterAnnotations();
690     // this should always return an array object, even if the method has no arguments
691     
692     try {
693       int paRef = env.newObjectArray("[Ljava/lang/annotation/Annotation;", pa.length);
694       
695       for (int i=0; i<pa.length; i++){
696         int eRef = env.newAnnotationProxies(pa[i]);
697         env.setReferenceArrayElement(paRef, i, eRef);
698       }
699
700       return paRef;
701       
702     } catch (ClinitRequired x){ // be prepared that we might have to initialize respective annotation classes
703       env.handleClinitRequest(x.getRequiredClassInfo());
704       return MJIEnv.NULL;
705     }    
706   }
707
708   @MJI
709   public int getParameterAnnotations_____3_3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
710     return getParameterAnnotations( env, getMethodInfo(env,mthRef));
711   }
712
713   @MJI
714   public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
715     StringBuilder sb = new StringBuilder();
716     
717     MethodInfo mi = getMethodInfo(env, objRef);
718
719     sb.append(Modifier.toString(mi.getModifiers()));
720     sb.append(' ');
721
722     sb.append(mi.getReturnTypeName());
723     sb.append(' ');
724
725     sb.append(mi.getClassName());
726     sb.append('.');
727
728     sb.append(mi.getName());
729
730     sb.append('(');
731     
732     String[] at = mi.getArgumentTypeNames();
733     for (int i=0; i<at.length; i++){
734       if (i>0) sb.append(',');
735       sb.append(at[i]);
736     }
737     
738     sb.append(')');
739     
740     int sref = env.newString(sb.toString());
741     return sref;
742   }
743
744   @MJI
745   public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int mthRef){
746     ElementInfo ei = env.getElementInfo(mthRef);
747     ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(JPF_java_lang_Class.METHOD_CLASSNAME);
748
749     if (ei.getClassInfo() == ci){
750       MethodInfo mi1 = getMethodInfo(env, objRef);
751       MethodInfo mi2 = getMethodInfo(env, mthRef);
752       if (mi1.getClassInfo() == mi2.getClassInfo()){
753         if (mi1.getName().equals(mi2.getName())){
754           if (mi1.getReturnType().equals(mi2.getReturnType())){
755             byte[] params1 = mi1.getArgumentTypes();
756             byte[] params2 = mi2.getArgumentTypes();
757             if (params1.length == params2.length){
758               for (int i = 0; i < params1.length; i++){
759                 if (params1[i] != params2[i]){
760                   return false;
761                 }
762               }
763               return true;
764             }
765           }
766         }
767       }
768     }
769     return false;
770   }
771
772   @MJI
773   public int hashCode____I (MJIEnv env, int objRef){
774     MethodInfo mi = getMethodInfo(env, objRef);
775     return mi.getClassName().hashCode() ^ mi.getName().hashCode();
776   }
777 }