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