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