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