Fixing the method getGenericParameterTypes to include parameterized object such as...
[jpf-core.git] / src / peers / gov / nasa / jpf / vm / JPF_java_lang_Class.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 java.io.IOException;
21 import java.io.InputStream;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Set;
25
26 import gov.nasa.jpf.Config;
27 import gov.nasa.jpf.annotation.MJI;
28
29 /**
30  * MJI NativePeer class for java.lang.Class library abstraction
31  */
32 public class JPF_java_lang_Class extends NativePeer {
33   
34   static final String FIELD_CLASSNAME = "java.lang.reflect.Field";
35   static final String METHOD_CLASSNAME = "java.lang.reflect.Method";
36   static final String CONSTRUCTOR_CLASSNAME = "java.lang.reflect.Constructor";
37
38   public static boolean init (Config conf){
39     // we create Method and Constructor objects, so we better make sure these
40     // classes are initialized (they already might be)
41     JPF_java_lang_reflect_Method.init(conf);
42     JPF_java_lang_reflect_Constructor.init(conf);
43     return true;
44   }
45   
46   @MJI
47   public boolean isArray____Z (MJIEnv env, int robj) {
48     ClassInfo ci = env.getReferredClassInfo( robj);
49     return ci.isArray();
50   }
51
52   @MJI
53   public int getComponentType____Ljava_lang_Class_2 (MJIEnv env, int robj) {
54     if (isArray____Z(env, robj)) {
55       ThreadInfo ti = env.getThreadInfo();
56       Instruction insn = ti.getPC();
57       ClassInfo ci = env.getReferredClassInfo( robj).getComponentClassInfo();
58
59     if (ci.initializeClass(ti)){
60         env.repeatInvocation();
61         return MJIEnv.NULL;
62       }
63
64       return ci.getClassObjectRef();
65     }
66
67     return MJIEnv.NULL;
68   }
69
70   @MJI
71   public boolean isInstance__Ljava_lang_Object_2__Z (MJIEnv env, int robj,
72                                                          int r1) {
73     ElementInfo sei = env.getStaticElementInfo(robj);
74     ClassInfo   ci = sei.getClassInfo();
75     ClassInfo   ciOther = env.getClassInfo(r1);
76     return (ciOther.isInstanceOf(ci));
77   }
78
79   @MJI
80   public boolean isInterface____Z (MJIEnv env, int robj){
81     ClassInfo ci = env.getReferredClassInfo( robj);
82     return ci.isInterface();
83   }
84   
85   @MJI
86   public boolean isAssignableFrom__Ljava_lang_Class_2__Z (MJIEnv env, int rcls,
87                                                               int r1) {
88     ElementInfo sei1 = env.getStaticElementInfo(rcls);
89     ClassInfo   ci1 = sei1.getClassInfo();
90
91     ElementInfo sei2 = env.getStaticElementInfo(r1);
92     ClassInfo   ci2 = sei2.getClassInfo();
93
94     return ci2.isInstanceOf( ci1);
95   }
96   
97   @MJI
98   public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){    
99     ClassInfo ci = env.getReferredClassInfo( robj);
100     AnnotationInfo[] ai = ci.getAnnotations();
101
102     try {
103       return env.newAnnotationProxies(ai);
104     } catch (ClinitRequired x){
105       env.handleClinitRequest(x.getRequiredClassInfo());
106       return MJIEnv.NULL;
107     }
108   }
109
110   // TODO: Fix for Groovy's model-checking
111   @MJI
112   public int getTypeParameters_____3Ljava_lang_reflect_TypeVariable_2 (MJIEnv env, int robj){
113     // Get the ClassInfo for this class
114     ClassInfo tci = env.getReferredClassInfo(robj);
115     String[] typeVars = tci.getGenericTypeVariableNames();
116
117     // Return with null if this is not a generic class
118     if (typeVars.length == 0) {
119       int aRef = env.newObjectArray("Ljava/lang/reflect/TypeVariable;", 0);
120       return aRef;
121     }
122     // Now create a TypeVariableImpl object for every type variable name; get the ClassInfo for TypeVariableImpl
123     ClassLoaderInfo cli = env.getSystemClassLoaderInfo();
124     ClassInfo ci = cli.getResolvedClassInfo("sun.reflect.generics.reflectiveObjects.TypeVariableImpl");
125     int[] var = new int[typeVars.length];
126
127     for(int i = 0; i < typeVars.length; i++) {
128       int typeVarRef = env.newObject(ci);
129       ElementInfo ei = env.getModifiableElementInfo(typeVarRef);
130       ei.setReferenceField("genericDeclaration", tci.getClassObjectRef());
131       ei.setReferenceField("name", env.newString(typeVars[i]));
132       var[i] = typeVarRef;
133     }
134     int aRef = env.newObjectArray("Ljava/lang/reflect/TypeVariable;", typeVars.length);
135
136     // Set references for every array element
137     for (int i = 0; i < typeVars.length; i++) {
138       env.setReferenceArrayElement(aRef, i, var[i]);
139     }
140
141     return aRef;
142   }
143   
144   @MJI
145   public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj,
146                                                                                 int annoClsRef){
147     ClassInfo ci = env.getReferredClassInfo( robj);
148     ClassInfo aci = env.getReferredClassInfo(annoClsRef);
149     
150     AnnotationInfo ai = ci.getAnnotation(aci.getName());
151     if (ai != null){
152       ClassInfo aciProxy = aci.getAnnotationProxy();
153       
154       try {
155         return env.newAnnotationProxy(aciProxy, ai);
156       } catch (ClinitRequired x){
157         env.handleClinitRequest(x.getRequiredClassInfo());
158         return MJIEnv.NULL;
159       }
160     } else {
161       return MJIEnv.NULL;
162     }
163   }
164   
165   @MJI
166   public int getPrimitiveClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
167                                                             int rcls, int stringRef) {
168     // we don't really have to check for a valid class name here, since
169     // this is a package default method that just gets called from
170     // the clinit of box classes
171     // note this does NOT return the box class (e.g. java.lang.Integer), which
172     // is a normal, functional class, but a primitive class (e.g. 'int') that
173     // is rather a strange beast (not even Object derived)
174     
175     ClassLoaderInfo scli = env.getSystemClassLoaderInfo(); // this is the one responsible for builtin classes
176     String primClsName = env.getStringObject(stringRef); // always initialized
177     
178     ClassInfo ci = scli.getResolvedClassInfo(primClsName);
179     return ci.getClassObjectRef();
180   }
181
182   @MJI
183   public boolean desiredAssertionStatus____Z (MJIEnv env, int robj) {
184     ClassInfo ci = env.getReferredClassInfo(robj);
185     return ci.desiredAssertionStatus();
186   }
187
188   public static int getClassObject (MJIEnv env, ClassInfo ci){
189     ThreadInfo ti = env.getThreadInfo();
190     Instruction insn = ti.getPC();
191
192     if (ci.initializeClass(ti)){
193       env.repeatInvocation();
194       return MJIEnv.NULL;
195     }
196
197     StaticElementInfo ei = ci.getStaticElementInfo();
198     int ref = ei.getClassObjectRef();
199
200     return ref;
201   }
202   
203   @MJI
204   public int forName__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
205                                                                        int rcls,
206                                                                        int clsNameRef) {
207     if (clsNameRef == MJIEnv.NULL){
208       env.throwException("java.lang.NullPointerException", "no class name provided");
209       return MJIEnv.NULL;
210     }
211     
212     String clsName = env.getStringObject(clsNameRef);
213     
214     if (clsName.isEmpty()){
215       env.throwException("java.lang.ClassNotFoundException", "empty class name");
216       return MJIEnv.NULL;  
217     }
218     
219     ThreadInfo ti = env.getThreadInfo();
220     MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
221     // class of the method that includes the invocation of Class.forName() 
222     ClassInfo cls = mi.getClassInfo();
223
224     String name;
225     // for array type, the component terminal must be resolved
226     if(clsName.charAt(0)=='[') {
227       name = Types.getComponentTerminal(clsName);
228     } else{
229       name = clsName;
230     }
231
232     // make the classloader of the class including the invocation of 
233     // Class.forName() resolve the class with the given name
234     try {
235       cls.resolveReferencedClass(name);
236     } catch(LoadOnJPFRequired lre) {
237       env.repeatInvocation();
238       return MJIEnv.NULL;
239     }
240
241     // The class obtained here is the same as the resolved one, except
242     // if it represents an array type
243     ClassInfo ci = cls.getClassLoaderInfo().getResolvedClassInfo(clsName);
244
245     return getClassObject(env, ci);
246   }
247
248   /**
249    * this is an example of a native method issuing direct calls - otherwise known
250    * as a round trip.
251    * We don't have to deal with class init here anymore, since this is called
252    * via the class object of the class to instantiate
253    */
254   @MJI
255   public int newInstance____Ljava_lang_Object_2 (MJIEnv env, int robj) {
256     ThreadInfo ti = env.getThreadInfo();
257     DirectCallStackFrame frame = ti.getReturnedDirectCall();
258     
259     ClassInfo ci = env.getReferredClassInfo(robj);   // what are we
260     MethodInfo miCtor = ci.getMethod("<init>()V", true); // note there always is one since something needs to call Object()
261
262     if (frame == null){ // first time around
263       if(ci.isAbstract()){ // not allowed to instantiate
264         env.throwException("java.lang.InstantiationException");
265         return MJIEnv.NULL;
266       }
267
268       // <2do> - still need to handle protected
269       if (miCtor.isPrivate()) {
270         env.throwException("java.lang.IllegalAccessException", "cannot access non-public member of class " + ci.getName());
271         return MJIEnv.NULL;
272       }
273
274       int objRef = env.newObjectOfUncheckedClass(ci);  // create the thing
275
276       frame = miCtor.createDirectCallStackFrame(ti, 1);
277       // note that we don't set this as a reflection call since it is supposed to propagate exceptions
278       frame.setReferenceArgument(0, objRef, null);
279       frame.setLocalReferenceVariable(0, objRef);        // (1) store ref for retrieval during re-exec
280       ti.pushFrame(frame);
281       
282       // check if we have to push clinits
283       ci.initializeClass(ti);
284       
285       env.repeatInvocation();
286       return MJIEnv.NULL;
287       
288     } else { // re-execution
289       int objRef = frame.getLocalVariable(0); // that's the object ref we set in (1)
290       return objRef;
291     }      
292   }
293   
294   @MJI
295   public int getSuperclass____Ljava_lang_Class_2 (MJIEnv env, int robj) {
296     ClassInfo ci = env.getReferredClassInfo( robj);
297     ClassInfo sci = ci.getSuperClass();
298     if (sci != null) {
299       return sci.getClassObjectRef();
300     } else {
301       return MJIEnv.NULL;
302     }
303   }
304
305   int getMethod (MJIEnv env, int clsRef, ClassInfo ciMethod, String mname, int argTypesRef,
306                         boolean isRecursiveLookup, boolean publicOnly) {
307
308     ClassInfo ci = env.getReferredClassInfo( clsRef);
309     
310     StringBuilder sb = new StringBuilder(mname);
311     sb.append('(');
312     int nParams = argTypesRef != MJIEnv.NULL ? env.getArrayLength(argTypesRef) : 0;
313     for (int i=0; i<nParams; i++) {
314       int cRef = env.getReferenceArrayElement(argTypesRef, i);
315       ClassInfo cit = env.getReferredClassInfo( cRef);
316       String tname = cit.getName();
317       String tcode = tname;
318       tcode = Types.getTypeSignature(tcode, false);
319       sb.append(tcode);
320     }
321     sb.append(')');
322     String fullMthName = sb.toString();
323
324     MethodInfo mi = ci.getReflectionMethod(fullMthName, isRecursiveLookup);
325     if (mi == null || (publicOnly && !mi.isPublic())) {
326       env.throwException("java.lang.NoSuchMethodException", ci.getName() + '.' + fullMthName);
327       return MJIEnv.NULL;
328       
329     } else {
330       return createMethodObject(env, ciMethod, mi);      
331     }
332   }
333
334   int createMethodObject (MJIEnv env, ClassInfo objectCi, MethodInfo mi) {
335     // NOTE - we rely on Constructor and Method peers being initialized
336     if (mi.isCtor()){
337       return JPF_java_lang_reflect_Constructor.createConstructorObject(env, objectCi, mi);
338     } else {
339       return JPF_java_lang_reflect_Method.createMethodObject(env, objectCi, mi);      
340     }
341   }
342   
343   @MJI
344   public int getDeclaredMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
345                                                                                                      int nameRef, int argTypesRef) {
346     ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
347     if (mci == null) {
348       env.repeatInvocation();
349       return MJIEnv.NULL;
350     }
351     
352     String mname = env.getStringObject(nameRef);
353     return getMethod(env, clsRef, mci, mname, argTypesRef, false, false);
354   }
355
356   @MJI
357   public int getDeclaredConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env,
358                                                                                                int clsRef,
359                                                                                                int argTypesRef){
360     ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
361     if (mci == null) {
362       env.repeatInvocation();
363       return MJIEnv.NULL;
364     }
365     
366     int ctorRef =  getMethod(env,clsRef, mci, "<init>",argTypesRef,false, false);
367     return ctorRef;
368   }
369   
370   @MJI
371   public int getMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
372                                                                                                      int nameRef, int argTypesRef) {
373     ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
374     if (mci == null) {
375       env.repeatInvocation();
376       return MJIEnv.NULL;
377     }
378     
379     String mname = env.getStringObject(nameRef);
380     return getMethod( env, clsRef, mci, mname, argTypesRef, true, true);
381   }
382
383   private void addDeclaredMethodsRec (boolean includeSuperClasses, HashMap<String,MethodInfo>methods, ClassInfo ci){
384     
385     if (includeSuperClasses){ // do NOT include Object methods for interfaces
386       ClassInfo sci = ci.getSuperClass();
387       if (sci != null){
388         addDeclaredMethodsRec( includeSuperClasses, methods,sci);
389       }
390     }
391
392     ClassLoaderInfo cl = ci.getClassLoaderInfo();
393     for (String ifcName : ci.getDirectInterfaceNames()){
394       ClassInfo ici = cl.getResolvedClassInfo(ifcName); // has to be already defined, so no exception
395       addDeclaredMethodsRec( includeSuperClasses, methods,ici);
396     }
397
398     for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
399       // filter out non-public, <clinit> and <init>
400       if (mi.isPublic() && (mi.getName().charAt(0) != '<')) {
401         String mname = mi.getUniqueName();
402
403         if (!(ci.isInterface() && methods.containsKey(mname))){
404           methods.put(mname, mi);
405         }
406       }
407     }
408   }
409
410   @MJI
411   public int getMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
412     ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
413     if (mci == null) {
414       env.repeatInvocation();
415       return MJIEnv.NULL;
416     }
417     
418     ClassInfo ci = env.getReferredClassInfo(objref);
419
420     // collect all the public, non-ctor instance methods
421     if (!ci.isPrimitive()) {
422       HashMap<String,MethodInfo> methods = new HashMap<String,MethodInfo>();
423       addDeclaredMethodsRec( !ci.isInterface(), methods,ci);
424       
425       int n = methods.size();
426       int aref = env.newObjectArray("Ljava/lang/reflect/Method;", n);
427       int i=0;
428
429       for (MethodInfo mi : methods.values()){
430         int mref = createMethodObject(env, mci, mi);
431         env.setReferenceArrayElement(aref,i++,mref);
432       }
433
434       return aref;
435
436     } else {
437       return env.newObjectArray("Ljava/lang/reflect/Method;", 0);
438     }
439   }
440   
441   @MJI
442   public int getDeclaredMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
443     ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
444     if (mci == null) {
445       env.repeatInvocation();
446       return MJIEnv.NULL;
447     }
448     
449     ClassInfo ci = env.getReferredClassInfo(objref);
450     MethodInfo[] methodInfos = ci.getDeclaredMethodInfos();
451     
452     // we have to filter out the ctors and the static init
453     int nMth = methodInfos.length;
454     for (int i=0; i<methodInfos.length; i++){
455       if (methodInfos[i].getName().charAt(0) == '<'){
456         methodInfos[i] = null;
457         nMth--;
458       }
459     }
460     
461     int aref = env.newObjectArray("Ljava/lang/reflect/Method;", nMth);
462     
463     for (int i=0, j=0; i<methodInfos.length; i++) {
464       if (methodInfos[i] != null){
465         int mref = createMethodObject(env, mci, methodInfos[i]);
466         env.setReferenceArrayElement(aref,j++,mref);
467       }
468     }
469     
470     return aref;
471   }
472   
473   int getConstructors (MJIEnv env, int objref, boolean publicOnly){
474     ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
475     if (mci == null) {
476       env.repeatInvocation();
477       return MJIEnv.NULL;
478     }
479     
480     ClassInfo ci = env.getReferredClassInfo(objref);
481     ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
482     
483     // we have to filter out the ctors and the static init
484     for (MethodInfo mi : ci.getDeclaredMethodInfos()){
485       if (mi.getName().equals("<init>")){
486         if (!publicOnly || mi.isPublic()) {
487           ctors.add(mi);
488         }
489       }
490     }
491     
492     int nCtors = ctors.size();
493     int aref = env.newObjectArray("Ljava/lang/reflect/Constructor;", nCtors);
494     
495     for (int i=0; i<nCtors; i++){
496       env.setReferenceArrayElement(aref, i, createMethodObject(env, mci, ctors.get(i)));
497     }
498     
499     return aref;
500   }
501   
502   @MJI
503   public int getConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
504     return getConstructors(env, objref, true);
505   }  
506   
507   @MJI
508   public int getDeclaredConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
509     return getConstructors(env, objref, false);
510   }
511   
512   @MJI
513   public int getConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env, int clsRef,
514                                                                                        int argTypesRef){
515     ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
516     if (mci == null) {
517       env.repeatInvocation();
518       return MJIEnv.NULL;
519     }
520     
521     // <2do> should only return a public ctor 
522     return getMethod(env,clsRef, mci, "<init>",argTypesRef,false,true);
523   }
524   
525   // this is only used for system classes such as java.lang.reflect.Method
526   ClassInfo getInitializedClassInfo (MJIEnv env, String clsName){
527     ThreadInfo ti = env.getThreadInfo();
528     Instruction insn = ti.getPC();
529     ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo( clsName);
530     
531     if (ci.initializeClass(ti)){
532       return null;
533     } else {
534       return ci;
535     }    
536   }
537   
538   @MJI
539   public void initialize0____V (MJIEnv env, int clsObjRef){
540     ClassInfo ci = env.getReferredClassInfo( clsObjRef);
541     ci.initializeClass(ThreadInfo.currentThread);
542   }
543
544   Set<ClassInfo> getInitializedInterfaces (MJIEnv env, ClassInfo ci){
545     ThreadInfo ti = env.getThreadInfo();
546     Instruction insn = ti.getPC();
547
548     Set<ClassInfo> ifcs = ci.getAllInterfaceClassInfos();
549     for (ClassInfo ciIfc : ifcs){
550     if (ciIfc.initializeClass(ti)){
551         return null;
552       } 
553     }
554
555     return ifcs;
556   }
557   
558   static int createFieldObject (MJIEnv env, FieldInfo fi, ClassInfo fci){
559     int regIdx = JPF_java_lang_reflect_Field.registerFieldInfo(fi);
560     
561     int eidx = env.newObject(fci);
562     ElementInfo ei = env.getModifiableElementInfo(eidx);    
563     ei.setIntField("regIdx", regIdx);
564     
565     return eidx;
566   }
567   
568   @MJI
569   public int getDeclaredFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int objRef) {
570     ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
571     if (fci == null) {
572       env.repeatInvocation();
573       return MJIEnv.NULL;
574     }
575
576     ClassInfo ci = env.getReferredClassInfo(objRef);
577     int nInstance = ci.getNumberOfDeclaredInstanceFields();
578     int nStatic = ci.getNumberOfStaticFields();
579     int aref = env.newObjectArray("Ljava/lang/reflect/Field;", nInstance + nStatic);
580     int i, j=0;
581     
582     for (i=0; i<nStatic; i++) {
583       FieldInfo fi = ci.getStaticField(i);
584       env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
585     }    
586     
587     for (i=0; i<nInstance; i++) {
588       FieldInfo fi = ci.getDeclaredInstanceField(i);
589       env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
590     }
591     
592     return aref;
593   }
594   
595   @MJI
596   public int getFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef){
597     ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
598     if (fci == null) {
599       env.repeatInvocation();
600       return MJIEnv.NULL;
601     }
602         
603     ClassInfo ci = env.getReferredClassInfo(clsRef);
604     // interfaces might not be initialized yet, so we have to check first
605     Set<ClassInfo> ifcs = getInitializedInterfaces( env, ci);
606     if (ifcs == null) {
607       env.repeatInvocation();
608       return MJIEnv.NULL;
609     }
610     
611     ArrayList<FieldInfo> fiList = new ArrayList<FieldInfo>();
612     for (; ci != null; ci = ci.getSuperClass()){
613       // the host VM returns them in order of declaration, but the spec says there is no guaranteed order so we keep it simple
614       for (FieldInfo fi : ci.getDeclaredInstanceFields()){
615         if (fi.isPublic()){
616           fiList.add(fi);
617         }
618       }
619       for (FieldInfo fi : ci.getDeclaredStaticFields()){
620         if (fi.isPublic()){
621           fiList.add(fi);
622         }
623       }
624     }
625     
626     for (ClassInfo ciIfc : ifcs){
627       for (FieldInfo fi : ciIfc.getDeclaredStaticFields()){
628         fiList.add(fi); // there are no non-public fields in interfaces
629       }      
630     }
631
632     int aref = env.newObjectArray("Ljava/lang/reflect/Field;", fiList.size());
633     int j=0;
634     for (FieldInfo fi : fiList){
635       env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
636     }
637     
638     return aref;
639   }
640     
641   int getField (MJIEnv env, int clsRef, int nameRef, boolean isRecursiveLookup) {    
642     ClassInfo ci = env.getReferredClassInfo( clsRef);
643     String fname = env.getStringObject(nameRef);
644     FieldInfo fi = null;
645     
646     if (isRecursiveLookup) {
647       fi = ci.getInstanceField(fname);
648       if (fi == null) {
649         fi = ci.getStaticField(fname);
650       }      
651     } else {
652         fi = ci.getDeclaredInstanceField(fname);
653         if (fi == null) {
654           fi = ci.getDeclaredStaticField(fname);
655         }
656     }
657     
658     if (fi == null) {      
659       env.throwException("java.lang.NoSuchFieldException", fname);
660       return MJIEnv.NULL;
661       
662     } else {
663       // don't do a Field clinit before we know there is such a field
664       ClassInfo fci = getInitializedClassInfo( env, FIELD_CLASSNAME);
665       if (fci == null) {
666         env.repeatInvocation();
667         return MJIEnv.NULL;
668       }
669       
670       return createFieldObject( env, fi, fci);
671     }
672   }
673   
674   @MJI
675   public int getDeclaredField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
676     return getField(env,clsRef,nameRef, false);
677   }  
678  
679   @MJI
680   public int getField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
681     return getField(env,clsRef,nameRef, true);    
682   }
683
684   @MJI
685   public int getModifiers____I (MJIEnv env, int clsRef){
686     ClassInfo ci = env.getReferredClassInfo(clsRef);
687     return ci.getModifiers();
688   }
689
690   @MJI
691   public int getEnumConstants_____3Ljava_lang_Object_2 (MJIEnv env, int clsRef){
692     ClassInfo ci = env.getReferredClassInfo(clsRef);
693     
694     if (env.requiresClinitExecution(ci)){
695       env.repeatInvocation();
696       return 0;
697     }
698
699     if (ci.getSuperClass().getName().equals("java.lang.Enum")) {      
700       ArrayList<FieldInfo> list = new ArrayList<FieldInfo>();
701       String cName = ci.getName();
702       
703       for (FieldInfo fi : ci.getDeclaredStaticFields()) {
704         if (fi.isFinal() && cName.equals(fi.getType())){
705           list.add(fi);
706         }
707       }
708       
709       int aRef = env.newObjectArray(cName, list.size());      
710       StaticElementInfo sei = ci.getStaticElementInfo();
711       int i=0;
712       for (FieldInfo fi : list){
713         env.setReferenceArrayElement( aRef, i++, sei.getReferenceField(fi));
714       }
715       return aRef;
716     }
717     
718     return MJIEnv.NULL;
719   }
720     
721   @MJI
722   public int getInterfaces_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
723     ClassInfo ci = env.getReferredClassInfo(clsRef);
724     int aref = MJIEnv.NULL;
725     ThreadInfo ti = env.getThreadInfo();
726     
727     // contrary to the API doc, this only returns the interfaces directly
728     // implemented by this class, not it's bases
729     // <2do> this is not exactly correct, since the interfaces should be ordered
730     Set<ClassInfo> interfaces = ci.getInterfaceClassInfos();
731     aref = env.newObjectArray("Ljava/lang/Class;", interfaces.size());
732
733     int i=0;
734     for (ClassInfo ifc: interfaces){
735       env.setReferenceArrayElement(aref, i++, ifc.getClassObjectRef());
736     }
737     
738     return aref;
739   }
740
741
742   /**
743    * <2do> needs to load from the classfile location, NOT the MJIEnv (native) class
744    *
745    * @author Sebastian Gfeller (sebastian.gfeller@gmail.com)
746    * @author Tihomir Gvero (tihomir.gvero@gmail.com)
747    */
748   @MJI
749   public int getByteArrayFromResourceStream__Ljava_lang_String_2___3B(MJIEnv env, int clsRef, int nameRef) {
750     String name = env.getStringObject(nameRef);
751
752     // <2do> this is not loading from the classfile location! fix it
753     InputStream is = env.getClass().getResourceAsStream(name);
754     if (is == null){
755       return MJIEnv.NULL;
756     }
757     // We assume that the entire input stream can be read at the moment,
758     // although this could break.
759     byte[] content = null;
760     try {
761       content = new byte[is.available()];
762       is.read(content);
763     } catch (IOException e) {
764       throw new RuntimeException(e);
765     }
766     // Now if everything worked, the content should be in the byte buffer.
767     // We put this buffer into the JPF VM.
768     return env.newByteArray(content);
769   }
770
771   @MJI
772   public int getEnclosingClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef) {
773     ClassInfo ciEncl = env.getReferredClassInfo( clsRef).getEnclosingClassInfo();
774     
775     if (ciEncl == null){
776       return MJIEnv.NULL;
777     }
778
779     if (ciEncl.initializeClass(env.getThreadInfo())) {
780       env.repeatInvocation();
781       return 0;
782     }
783
784     return ciEncl.getClassObjectRef();
785   }
786
787   @MJI
788   public int getDeclaredClasses_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
789     ClassInfo ci = env.getReferredClassInfo(clsRef);
790     String[] innerClassNames =  ci.getInnerClasses();
791     int aref = MJIEnv.NULL;
792     ThreadInfo ti = env.getThreadInfo();
793     
794     MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
795     // class of the method that includes the invocation of Class.getDeclaredClasses 
796     ClassInfo cls = mi.getClassInfo();
797
798     // first resolve all the inner classes
799     int length = innerClassNames.length;
800     ClassInfo[] resolvedInnerClass = new ClassInfo[length];
801     for(int i=0; i<length; i++) {
802       try {
803         resolvedInnerClass[i] = cls.resolveReferencedClass(innerClassNames[i]);
804       } catch(LoadOnJPFRequired lre) {
805         env.repeatInvocation();
806         return MJIEnv.NULL;
807       }
808     }
809
810     aref = env.newObjectArray("Ljava/lang/Class;", innerClassNames.length);
811     for (int i=0; i<length; i++){
812       ClassInfo ici = resolvedInnerClass[i];
813       if (!ici.isRegistered()) {
814         ici.registerClass(ti);
815       }
816       env.setReferenceArrayElement(aref, i, ici.getClassObjectRef());
817     }
818     
819     return aref;
820   }
821
822   private String getCanonicalName (ClassInfo ci){
823     if (ci.isArray()){
824       String canonicalName = getCanonicalName(ci.getComponentClassInfo());
825       if (canonicalName != null){
826         return canonicalName + "[]";
827       } else{
828         return null;
829       }
830     }
831     if (isLocalOrAnonymousClass(ci)) {
832       return null;
833     }
834     if (ci.getEnclosingClassInfo() == null){
835       return ci.getName();
836     } else{
837       String enclosingName = getCanonicalName(ci.getEnclosingClassInfo());
838       if (enclosingName == null){ return null; }
839       return enclosingName + "." + ci.getSimpleName();
840     }
841   }
842
843   @MJI
844   public int getCanonicalName____Ljava_lang_String_2 (MJIEnv env, int clsRef){
845     ClassInfo ci = env.getReferredClassInfo(clsRef);
846     return env.newString(getCanonicalName(ci));
847   }
848
849   @MJI
850   public boolean isAnnotation____Z (MJIEnv env, int clsObjRef){
851     ClassInfo ci = env.getReferredClassInfo(clsObjRef);
852     return (ci.getModifiers() & 0x2000) != 0;
853   }
854   
855   @MJI
856   public boolean isAnnotationPresent__Ljava_lang_Class_2__Z (MJIEnv env, int clsObjRef, int annoClsObjRef){
857     ClassInfo ci = env.getReferredClassInfo(clsObjRef);
858     ClassInfo ciAnno = env.getReferredClassInfo(annoClsObjRef);
859     
860     return ci.getAnnotation( ciAnno.getName()) != null;    
861   }
862   
863   @MJI
864   public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){
865     ClassInfo ci = env.getReferredClassInfo(robj);
866
867     try{
868       return env.newAnnotationProxies(ci.getDeclaredAnnotations());
869     } catch (ClinitRequired x){
870       env.handleClinitRequest(x.getRequiredClassInfo());
871       return MJIEnv.NULL;
872     }
873   }
874
875   @MJI
876   public int getEnclosingConstructor____Ljava_lang_reflect_Constructor_2 (MJIEnv env, int robj){
877     ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
878     if (mci == null){
879       env.repeatInvocation();
880       return MJIEnv.NULL;
881     }
882     ClassInfo ci = env.getReferredClassInfo(robj);
883     MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
884
885     if ((enclosingMethod != null) && enclosingMethod.isCtor()){ 
886       return createMethodObject(env, mci, enclosingMethod); 
887     }
888     return MJIEnv.NULL;
889   }
890
891   @MJI
892   public int getEnclosingMethod____Ljava_lang_reflect_Method_2 (MJIEnv env, int robj){
893     ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
894     if (mci == null){
895       env.repeatInvocation();
896       return MJIEnv.NULL;
897     }
898     ClassInfo ci = env.getReferredClassInfo(robj);
899     MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
900
901     if ((enclosingMethod != null) && !enclosingMethod.isCtor()){ 
902       return createMethodObject(env, mci, enclosingMethod); 
903     }
904     return MJIEnv.NULL;
905   }
906
907   @MJI
908   public boolean isAnonymousClass____Z (MJIEnv env, int robj){
909     ClassInfo ci = env.getReferredClassInfo(robj);
910     String cname = null;
911     if (ci.getName().contains("$")){
912       cname = ci.getName().substring(ci.getName().lastIndexOf('$') + 1);
913     }
914     return (cname == null) ? false : cname.matches("\\d+?");
915   }
916
917   @MJI
918   public boolean isEnum____Z (MJIEnv env, int robj){
919     ClassInfo ci = env.getReferredClassInfo(robj);
920     return ci.isEnum();
921   }
922
923   // Similar to getEnclosingClass() except it returns null for the case of
924   // anonymous class.
925   @MJI
926   public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef){
927     ClassInfo ci = env.getReferredClassInfo(clsRef);
928     if (isLocalOrAnonymousClass(ci)){
929       return MJIEnv.NULL;
930     } else{
931       return getEnclosingClass____Ljava_lang_Class_2(env, clsRef);
932     }
933   }
934
935   @MJI
936   public boolean isLocalClass____Z (MJIEnv env, int robj){
937     ClassInfo ci = env.getReferredClassInfo(robj);
938     return isLocalOrAnonymousClass(ci) && !isAnonymousClass____Z(env, robj);
939   }
940
941   private boolean isLocalOrAnonymousClass (ClassInfo ci){
942     return (ci.getEnclosingMethodInfo() != null);
943   }
944
945   @MJI
946   public boolean isMemberClass____Z (MJIEnv env, int robj){
947     ClassInfo ci = env.getReferredClassInfo(robj);
948     return (ci.getEnclosingClassInfo() != null) && !isLocalOrAnonymousClass(ci);
949   }
950
951   /**
952    * Append the package name prefix of the class represented by robj, if the name is not 
953    * absolute. OW, remove leading "/". 
954    */
955   @MJI
956   public int getResolvedName__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int robj, int resRef){
957     String rname = env.getStringObject(resRef);
958     ClassInfo ci = env.getReferredClassInfo(robj);
959     if (rname == null) {
960       return MJIEnv.NULL;
961     }
962     if (!rname.startsWith("/")) {
963       ClassInfo c = ci;
964       while (c.isArray()) {
965           c = c.getComponentClassInfo();
966       }
967       String baseName = c.getName();
968       int index = baseName.lastIndexOf('.');
969       if (index != -1) {
970         rname = baseName.substring(0, index).replace('.', '/')
971             +"/"+rname;
972       }
973     } else {
974         rname = rname.substring(1);
975     }
976
977     return env.newString(rname);
978   }
979 }