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