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