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