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