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