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