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