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