view src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java @ 23:db918c531e6d

streamlined class init, which was a mixed case of registerClass()/initializeClass() and pushRequiredClinits(). Now it is a single initializeClass(ti) method which combines the previous initializeClass(), pushRequiredClinits() and pushClinit() methods. The reason for combining these is the forthcoming replacement of separately locked clinits from different DirectCallStackFrames with a single synthetic frame that calls clinits from nested synchronized blocks. This is required to model hotspot, which does cause deadlocks with concurrent init of classes that cause subclass init during their clinit executions.
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Wed, 15 Apr 2015 22:40:21 -0700
parents 61d41facf527
children
line wrap: on
line source

/*
 * Copyright (C) 2014, United States Government, as represented by the
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 *
 * The Java Pathfinder core (jpf-core) platform is licensed under the
 * Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 *        http://www.apache.org/licenses/LICENSE-2.0. 
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package gov.nasa.jpf.vm;

import gov.nasa.jpf.Config;
import gov.nasa.jpf.annotation.MJI;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;


/**
 * MJI NativePeer class for java.lang.Class library abstraction
 */
public class JPF_java_lang_Class extends NativePeer {
  
  static final String FIELD_CLASSNAME = "java.lang.reflect.Field";
  static final String METHOD_CLASSNAME = "java.lang.reflect.Method";
  static final String CONSTRUCTOR_CLASSNAME = "java.lang.reflect.Constructor";
  
  public static boolean init (Config conf){
    // we create Method and Constructor objects, so we better make sure these
    // classes are initialized (they already might be)
    JPF_java_lang_reflect_Method.init(conf);
    JPF_java_lang_reflect_Constructor.init(conf);
    return true;
  }
  
  @MJI
  public boolean isArray____Z (MJIEnv env, int robj) {
    ClassInfo ci = env.getReferredClassInfo( robj);
    return ci.isArray();
  }

  @MJI
  public int getComponentType____Ljava_lang_Class_2 (MJIEnv env, int robj) {
    if (isArray____Z(env, robj)) {
      ThreadInfo ti = env.getThreadInfo();
      Instruction insn = ti.getPC();
      ClassInfo ci = env.getReferredClassInfo( robj).getComponentClassInfo();

    if (ci.initializeClass(ti)){
        env.repeatInvocation();
        return MJIEnv.NULL;
      }

      return ci.getClassObjectRef();
    }

    return MJIEnv.NULL;
  }

  @MJI
  public boolean isInstance__Ljava_lang_Object_2__Z (MJIEnv env, int robj,
                                                         int r1) {
    ElementInfo sei = env.getStaticElementInfo(robj);
    ClassInfo   ci = sei.getClassInfo();
    ClassInfo   ciOther = env.getClassInfo(r1);
    return (ciOther.isInstanceOf(ci));
  }

  @MJI
  public boolean isInterface____Z (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo( robj);
    return ci.isInterface();
  }
  
  @MJI
  public boolean isAssignableFrom__Ljava_lang_Class_2__Z (MJIEnv env, int rcls,
                                                              int r1) {
    ElementInfo sei1 = env.getStaticElementInfo(rcls);
    ClassInfo   ci1 = sei1.getClassInfo();

    ElementInfo sei2 = env.getStaticElementInfo(r1);
    ClassInfo   ci2 = sei2.getClassInfo();

    return ci2.isInstanceOf( ci1);
  }
  
  @MJI
  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){    
    ClassInfo ci = env.getReferredClassInfo( robj);
    AnnotationInfo[] ai = ci.getAnnotations();

    try {
      return env.newAnnotationProxies(ai);
    } catch (ClinitRequired x){
      env.handleClinitRequest(x.getRequiredClassInfo());
      return MJIEnv.NULL;
    }
  }
  
  @MJI
  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj,
                                                                                int annoClsRef){
    ClassInfo ci = env.getReferredClassInfo( robj);
    ClassInfo aci = env.getReferredClassInfo(annoClsRef);
    
    AnnotationInfo ai = ci.getAnnotation(aci.getName());
    if (ai != null){
      ClassInfo aciProxy = aci.getAnnotationProxy();
      
      try {
        return env.newAnnotationProxy(aciProxy, ai);
      } catch (ClinitRequired x){
        env.handleClinitRequest(x.getRequiredClassInfo());
        return MJIEnv.NULL;
      }
    } else {
      return MJIEnv.NULL;
    }
  }
  
  @MJI
  public int getPrimitiveClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
                                                            int rcls, int stringRef) {
    // we don't really have to check for a valid class name here, since
    // this is a package default method that just gets called from
    // the clinit of box classes
    // note this does NOT return the box class (e.g. java.lang.Integer), which
    // is a normal, functional class, but a primitive class (e.g. 'int') that
    // is rather a strange beast (not even Object derived)
    
    ClassLoaderInfo scli = env.getSystemClassLoaderInfo(); // this is the one responsible for builtin classes
    String primClsName = env.getStringObject(stringRef); // always initialized
    
    ClassInfo ci = scli.getResolvedClassInfo(primClsName);
    return ci.getClassObjectRef();
  }

  @MJI
  public boolean desiredAssertionStatus____Z (MJIEnv env, int robj) {
    ClassInfo ci = env.getReferredClassInfo(robj);
    return ci.desiredAssertionStatus();
  }

  public static int getClassObject (MJIEnv env, ClassInfo ci){
    ThreadInfo ti = env.getThreadInfo();
    Instruction insn = ti.getPC();

    if (ci.initializeClass(ti)){
      env.repeatInvocation();
      return MJIEnv.NULL;
    }

    StaticElementInfo ei = ci.getStaticElementInfo();
    int ref = ei.getClassObjectRef();

    return ref;
  }
  
  @MJI
  public int forName__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
                                                                       int rcls,
                                                                       int clsNameRef) {
    if (clsNameRef == MJIEnv.NULL){
      env.throwException("java.lang.NullPointerException", "no class name provided");
      return MJIEnv.NULL;
    }
    
    String clsName = env.getStringObject(clsNameRef);
    
    if (clsName.isEmpty()){
      env.throwException("java.lang.ClassNotFoundException", "empty class name");
      return MJIEnv.NULL;  
    }
    
    ThreadInfo ti = env.getThreadInfo();
    MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
    // class of the method that includes the invocation of Class.forName() 
    ClassInfo cls = mi.getClassInfo();

    String name;
    // for array type, the component terminal must be resolved
    if(clsName.charAt(0)=='[') {
      name = Types.getComponentTerminal(clsName);
    } else{
      name = clsName;
    }

    // make the classloader of the class including the invocation of 
    // Class.forName() resolve the class with the given name
    try {
      cls.resolveReferencedClass(name);
    } catch(LoadOnJPFRequired lre) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }

    // The class obtained here is the same as the resolved one, except
    // if it represents an array type
    ClassInfo ci = cls.getClassLoaderInfo().getResolvedClassInfo(clsName);

    return getClassObject(env, ci);
  }

  /**
   * this is an example of a native method issuing direct calls - otherwise known
   * as a round trip.
   * We don't have to deal with class init here anymore, since this is called
   * via the class object of the class to instantiate
   */
  @MJI
  public int newInstance____Ljava_lang_Object_2 (MJIEnv env, int robj) {
    ThreadInfo ti = env.getThreadInfo();
    DirectCallStackFrame frame = ti.getReturnedDirectCall();
    
    ClassInfo ci = env.getReferredClassInfo(robj);   // what are we
    MethodInfo miCtor = ci.getMethod("<init>()V", true); // note there always is one since something needs to call Object()

    if (frame == null){ // first time around
      if(ci.isAbstract()){ // not allowed to instantiate
        env.throwException("java.lang.InstantiationException");
        return MJIEnv.NULL;
      }

      // <2do> - still need to handle protected
      if (miCtor.isPrivate()) {
        env.throwException("java.lang.IllegalAccessException", "cannot access non-public member of class " + ci.getName());
        return MJIEnv.NULL;
      }

      int objRef = env.newObjectOfUncheckedClass(ci);  // create the thing

      frame = miCtor.createDirectCallStackFrame(ti, 1);
      // note that we don't set this as a reflection call since it is supposed to propagate exceptions
      frame.setReferenceArgument(0, objRef, null);
      frame.setLocalReferenceVariable(0, objRef);        // (1) store ref for retrieval during re-exec
      ti.pushFrame(frame);
      
      // check if we have to push clinits
      ci.initializeClass(ti);
      
      env.repeatInvocation();
      return MJIEnv.NULL;
      
    } else { // re-execution
      int objRef = frame.getLocalVariable(0); // that's the object ref we set in (1)
      return objRef;
    }      
  }
  
  @MJI
  public int getSuperclass____Ljava_lang_Class_2 (MJIEnv env, int robj) {
    ClassInfo ci = env.getReferredClassInfo( robj);
    ClassInfo sci = ci.getSuperClass();
    if (sci != null) {
      return sci.getClassObjectRef();
    } else {
      return MJIEnv.NULL;
    }
  }

  int getMethod (MJIEnv env, int clsRef, ClassInfo ciMethod, String mname, int argTypesRef,
                        boolean isRecursiveLookup, boolean publicOnly) {

    ClassInfo ci = env.getReferredClassInfo( clsRef);
    
    StringBuilder sb = new StringBuilder(mname);
    sb.append('(');
    int nParams = argTypesRef != MJIEnv.NULL ? env.getArrayLength(argTypesRef) : 0;
    for (int i=0; i<nParams; i++) {
      int cRef = env.getReferenceArrayElement(argTypesRef, i);
      ClassInfo cit = env.getReferredClassInfo( cRef);
      String tname = cit.getName();
      String tcode = tname;
      tcode = Types.getTypeSignature(tcode, false);
      sb.append(tcode);
    }
    sb.append(')');
    String fullMthName = sb.toString();

    MethodInfo mi = ci.getReflectionMethod(fullMthName, isRecursiveLookup);
    if (mi == null || (publicOnly && !mi.isPublic())) {
      env.throwException("java.lang.NoSuchMethodException", ci.getName() + '.' + fullMthName);
      return MJIEnv.NULL;
      
    } else {
      return createMethodObject(env, ciMethod, mi);      
    }
  }

  int createMethodObject (MJIEnv env, ClassInfo objectCi, MethodInfo mi) {
    // NOTE - we rely on Constructor and Method peers being initialized
    if (mi.isCtor()){
      return JPF_java_lang_reflect_Constructor.createConstructorObject(env, objectCi, mi);
    } else {
      return JPF_java_lang_reflect_Method.createMethodObject(env, objectCi, mi);      
    }
  }
  
  @MJI
  public int getDeclaredMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
                                                                                                     int nameRef, int argTypesRef) {
    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    String mname = env.getStringObject(nameRef);
    return getMethod(env, clsRef, mci, mname, argTypesRef, false, false);
  }

  @MJI
  public int getDeclaredConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env,
                                                                                               int clsRef,
                                                                                               int argTypesRef){
    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    int ctorRef =  getMethod(env,clsRef, mci, "<init>",argTypesRef,false, false);
    return ctorRef;
  }
  
  @MJI
  public int getMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
                                                                                                     int nameRef, int argTypesRef) {
    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    String mname = env.getStringObject(nameRef);
    return getMethod( env, clsRef, mci, mname, argTypesRef, true, true);
  }

  private void addDeclaredMethodsRec (boolean includeSuperClasses, HashMap<String,MethodInfo>methods, ClassInfo ci){
    
    if (includeSuperClasses){ // do NOT include Object methods for interfaces
      ClassInfo sci = ci.getSuperClass();
      if (sci != null){
        addDeclaredMethodsRec( includeSuperClasses, methods,sci);
      }
    }

    ClassLoaderInfo cl = ci.getClassLoaderInfo();
    for (String ifcName : ci.getDirectInterfaceNames()){
      ClassInfo ici = cl.getResolvedClassInfo(ifcName); // has to be already defined, so no exception
      addDeclaredMethodsRec( includeSuperClasses, methods,ici);
    }

    for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
      // filter out non-public, <clinit> and <init>
      if (mi.isPublic() && (mi.getName().charAt(0) != '<')) {
        String mname = mi.getUniqueName();

        if (!(ci.isInterface() && methods.containsKey(mname))){
          methods.put(mname, mi);
        }
      }
    }
  }

  @MJI
  public int getMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    ClassInfo ci = env.getReferredClassInfo(objref);

    // collect all the public, non-ctor instance methods
    if (!ci.isPrimitive()) {
      HashMap<String,MethodInfo> methods = new HashMap<String,MethodInfo>();
      addDeclaredMethodsRec( !ci.isInterface(), methods,ci);
      
      int n = methods.size();
      int aref = env.newObjectArray("Ljava/lang/reflect/Method;", n);
      int i=0;

      for (MethodInfo mi : methods.values()){
        int mref = createMethodObject(env, mci, mi);
        env.setReferenceArrayElement(aref,i++,mref);
      }

      return aref;

    } else {
      return env.newObjectArray("Ljava/lang/reflect/Method;", 0);
    }
  }
  
  @MJI
  public int getDeclaredMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    ClassInfo ci = env.getReferredClassInfo(objref);
    MethodInfo[] methodInfos = ci.getDeclaredMethodInfos();
    
    // we have to filter out the ctors and the static init
    int nMth = methodInfos.length;
    for (int i=0; i<methodInfos.length; i++){
      if (methodInfos[i].getName().charAt(0) == '<'){
        methodInfos[i] = null;
        nMth--;
      }
    }
    
    int aref = env.newObjectArray("Ljava/lang/reflect/Method;", nMth);
    
    for (int i=0, j=0; i<methodInfos.length; i++) {
      if (methodInfos[i] != null){
        int mref = createMethodObject(env, mci, methodInfos[i]);
        env.setReferenceArrayElement(aref,j++,mref);
      }
    }
    
    return aref;
  }
  
  int getConstructors (MJIEnv env, int objref, boolean publicOnly){
    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    ClassInfo ci = env.getReferredClassInfo(objref);
    ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
    
    // we have to filter out the ctors and the static init
    for (MethodInfo mi : ci.getDeclaredMethodInfos()){
      if (mi.getName().equals("<init>")){
        if (!publicOnly || mi.isPublic()) {
          ctors.add(mi);
        }
      }
    }
    
    int nCtors = ctors.size();
    int aref = env.newObjectArray("Ljava/lang/reflect/Constructor;", nCtors);
    
    for (int i=0; i<nCtors; i++){
      env.setReferenceArrayElement(aref, i, createMethodObject(env, mci, ctors.get(i)));
    }
    
    return aref;
  }
  
  @MJI
  public int getConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
    return getConstructors(env, objref, true);
  }  
  
  @MJI
  public int getDeclaredConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
    return getConstructors(env, objref, false);
  }
  
  @MJI
  public int getConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env, int clsRef,
                                                                                       int argTypesRef){
    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
    if (mci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    // <2do> should only return a public ctor 
    return getMethod(env,clsRef, mci, "<init>",argTypesRef,false,true);
  }
  
  // this is only used for system classes such as java.lang.reflect.Method
  ClassInfo getInitializedClassInfo (MJIEnv env, String clsName){
    ThreadInfo ti = env.getThreadInfo();
    Instruction insn = ti.getPC();
    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo( clsName);
    
    if (ci.initializeClass(ti)){
      return null;
    } else {
      return ci;
    }    
  }
  
  @MJI
  public void initialize0____V (MJIEnv env, int clsObjRef){
    ClassInfo ci = env.getReferredClassInfo( clsObjRef);
    ci.initializeClass(ThreadInfo.currentThread);
  }

  Set<ClassInfo> getInitializedInterfaces (MJIEnv env, ClassInfo ci){
    ThreadInfo ti = env.getThreadInfo();
    Instruction insn = ti.getPC();

    Set<ClassInfo> ifcs = ci.getAllInterfaceClassInfos();
    for (ClassInfo ciIfc : ifcs){
    if (ciIfc.initializeClass(ti)){
        return null;
      } 
    }

    return ifcs;
  }
  
  static int createFieldObject (MJIEnv env, FieldInfo fi, ClassInfo fci){
    int regIdx = JPF_java_lang_reflect_Field.registerFieldInfo(fi);
    
    int eidx = env.newObject(fci);
    ElementInfo ei = env.getModifiableElementInfo(eidx);    
    ei.setIntField("regIdx", regIdx);
    
    return eidx;
  }
  
  @MJI
  public int getDeclaredFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int objRef) {
    ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
    if (fci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }

    ClassInfo ci = env.getReferredClassInfo(objRef);
    int nInstance = ci.getNumberOfDeclaredInstanceFields();
    int nStatic = ci.getNumberOfStaticFields();
    int aref = env.newObjectArray("Ljava/lang/reflect/Field;", nInstance + nStatic);
    int i, j=0;
    
    for (i=0; i<nStatic; i++) {
      FieldInfo fi = ci.getStaticField(i);
      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
    }    
    
    for (i=0; i<nInstance; i++) {
      FieldInfo fi = ci.getDeclaredInstanceField(i);
      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
    }
    
    return aref;
  }
  
  @MJI
  public int getFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef){
    ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
    if (fci == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
        
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    // interfaces might not be initialized yet, so we have to check first
    Set<ClassInfo> ifcs = getInitializedInterfaces( env, ci);
    if (ifcs == null) {
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    
    ArrayList<FieldInfo> fiList = new ArrayList<FieldInfo>();
    for (; ci != null; ci = ci.getSuperClass()){
      // the host VM returns them in order of declaration, but the spec says there is no guaranteed order so we keep it simple
      for (FieldInfo fi : ci.getDeclaredInstanceFields()){
        if (fi.isPublic()){
          fiList.add(fi);
        }
      }
      for (FieldInfo fi : ci.getDeclaredStaticFields()){
        if (fi.isPublic()){
          fiList.add(fi);
        }
      }
    }
    
    for (ClassInfo ciIfc : ifcs){
      for (FieldInfo fi : ciIfc.getDeclaredStaticFields()){
        fiList.add(fi); // there are no non-public fields in interfaces
      }      
    }

    int aref = env.newObjectArray("Ljava/lang/reflect/Field;", fiList.size());
    int j=0;
    for (FieldInfo fi : fiList){
      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
    }
    
    return aref;
  }
    
  int getField (MJIEnv env, int clsRef, int nameRef, boolean isRecursiveLookup) {    
    ClassInfo ci = env.getReferredClassInfo( clsRef);
    String fname = env.getStringObject(nameRef);
    FieldInfo fi = null;
    
    if (isRecursiveLookup) {
      fi = ci.getInstanceField(fname);
      if (fi == null) {
        fi = ci.getStaticField(fname);
      }      
    } else {
        fi = ci.getDeclaredInstanceField(fname);
        if (fi == null) {
          fi = ci.getDeclaredStaticField(fname);
        }
    }
    
    if (fi == null) {      
      env.throwException("java.lang.NoSuchFieldException", fname);
      return MJIEnv.NULL;
      
    } else {
      // don't do a Field clinit before we know there is such a field
      ClassInfo fci = getInitializedClassInfo( env, FIELD_CLASSNAME);
      if (fci == null) {
        env.repeatInvocation();
        return MJIEnv.NULL;
      }
      
      return createFieldObject( env, fi, fci);
    }
  }
  
  @MJI
  public int getDeclaredField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
    return getField(env,clsRef,nameRef, false);
  }  
 
  @MJI
  public int getField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
    return getField(env,clsRef,nameRef, true);    
  }

  @MJI
  public int getModifiers____I (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    return ci.getModifiers();
  }

  @MJI
  public int getEnumConstants_____3Ljava_lang_Object_2 (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    
    if (env.requiresClinitExecution(ci)){
      env.repeatInvocation();
      return 0;
    }

    if (ci.getSuperClass().getName().equals("java.lang.Enum")) {      
      ArrayList<FieldInfo> list = new ArrayList<FieldInfo>();
      String cName = ci.getName();
      
      for (FieldInfo fi : ci.getDeclaredStaticFields()) {
        if (fi.isFinal() && cName.equals(fi.getType())){
          list.add(fi);
        }
      }
      
      int aRef = env.newObjectArray(cName, list.size());      
      StaticElementInfo sei = ci.getStaticElementInfo();
      int i=0;
      for (FieldInfo fi : list){
        env.setReferenceArrayElement( aRef, i++, sei.getReferenceField(fi));
      }
      return aRef;
    }
    
    return MJIEnv.NULL;
  }
    
  @MJI
  public int getInterfaces_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    int aref = MJIEnv.NULL;
    ThreadInfo ti = env.getThreadInfo();
    
    // contrary to the API doc, this only returns the interfaces directly
    // implemented by this class, not it's bases
    // <2do> this is not exactly correct, since the interfaces should be ordered
    Set<ClassInfo> interfaces = ci.getInterfaceClassInfos();
    aref = env.newObjectArray("Ljava/lang/Class;", interfaces.size());

    int i=0;
    for (ClassInfo ifc: interfaces){
      env.setReferenceArrayElement(aref, i++, ifc.getClassObjectRef());
    }
    
    return aref;
  }


  /**
   * <2do> needs to load from the classfile location, NOT the MJIEnv (native) class
   *
   * @author Sebastian Gfeller (sebastian.gfeller@gmail.com)
   * @author Tihomir Gvero (tihomir.gvero@gmail.com)
   */
  @MJI
  public int getByteArrayFromResourceStream__Ljava_lang_String_2___3B(MJIEnv env, int clsRef, int nameRef) {
    String name = env.getStringObject(nameRef);

    // <2do> this is not loading from the classfile location! fix it
    InputStream is = env.getClass().getResourceAsStream(name);
    if (is == null){
      return MJIEnv.NULL;
    }
    // We assume that the entire input stream can be read at the moment,
    // although this could break.
    byte[] content = null;
    try {
      content = new byte[is.available()];
      is.read(content);
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
    // Now if everything worked, the content should be in the byte buffer.
    // We put this buffer into the JPF VM.
    return env.newByteArray(content);
  }

  @MJI
  public int getEnclosingClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef) {
    ClassInfo ciEncl = env.getReferredClassInfo( clsRef).getEnclosingClassInfo();
    
    if (ciEncl == null){
      return MJIEnv.NULL;
    }

    if (ciEncl.initializeClass(env.getThreadInfo())) {
      env.repeatInvocation();
      return 0;
    }

    return ciEncl.getClassObjectRef();
  }

  @MJI
  public int getDeclaredClasses_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    String[] innerClassNames =  ci.getInnerClasses();
    int aref = MJIEnv.NULL;
    ThreadInfo ti = env.getThreadInfo();
    
    MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
    // class of the method that includes the invocation of Class.getDeclaredClasses 
    ClassInfo cls = mi.getClassInfo();

    // first resolve all the inner classes
    int length = innerClassNames.length;
    ClassInfo[] resolvedInnerClass = new ClassInfo[length];
    for(int i=0; i<length; i++) {
      try {
        resolvedInnerClass[i] = cls.resolveReferencedClass(innerClassNames[i]);
      } catch(LoadOnJPFRequired lre) {
        env.repeatInvocation();
        return MJIEnv.NULL;
      }
    }

    aref = env.newObjectArray("Ljava/lang/Class;", innerClassNames.length);
    for (int i=0; i<length; i++){
      ClassInfo ici = resolvedInnerClass[i];
      if (!ici.isRegistered()) {
        ici.registerClass(ti);
      }
      env.setReferenceArrayElement(aref, i, ici.getClassObjectRef());
    }
    
    return aref;
  }

  private String getCanonicalName (ClassInfo ci){
    if (ci.isArray()){
      String canonicalName = getCanonicalName(ci.getComponentClassInfo());
      if (canonicalName != null){
        return canonicalName + "[]";
      } else{
        return null;
      }
    }
    if (isLocalOrAnonymousClass(ci)) {
      return null;
    }
    if (ci.getEnclosingClassInfo() == null){
      return ci.getName();
    } else{
      String enclosingName = getCanonicalName(ci.getEnclosingClassInfo());
      if (enclosingName == null){ return null; }
      return enclosingName + "." + ci.getSimpleName();
    }
  }

  @MJI
  public int getCanonicalName____Ljava_lang_String_2 (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    return env.newString(getCanonicalName(ci));
  }

  @MJI
  public boolean isAnnotation____Z (MJIEnv env, int clsObjRef){
    ClassInfo ci = env.getReferredClassInfo(clsObjRef);
    return (ci.getModifiers() & 0x2000) != 0;
  }
  
  @MJI
  public boolean isAnnotationPresent__Ljava_lang_Class_2__Z (MJIEnv env, int clsObjRef, int annoClsObjRef){
    ClassInfo ci = env.getReferredClassInfo(clsObjRef);
    ClassInfo ciAnno = env.getReferredClassInfo(annoClsObjRef);
    
    return ci.getAnnotation( ciAnno.getName()) != null;    
  }
  
  @MJI
  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo(robj);

    try{
      return env.newAnnotationProxies(ci.getDeclaredAnnotations());
    } catch (ClinitRequired x){
      env.handleClinitRequest(x.getRequiredClassInfo());
      return MJIEnv.NULL;
    }
  }

  @MJI
  public int getEnclosingConstructor____Ljava_lang_reflect_Constructor_2 (MJIEnv env, int robj){
    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
    if (mci == null){
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    ClassInfo ci = env.getReferredClassInfo(robj);
    MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();

    if ((enclosingMethod != null) && enclosingMethod.isCtor()){ 
      return createMethodObject(env, mci, enclosingMethod); 
    }
    return MJIEnv.NULL;
  }

  @MJI
  public int getEnclosingMethod____Ljava_lang_reflect_Method_2 (MJIEnv env, int robj){
    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
    if (mci == null){
      env.repeatInvocation();
      return MJIEnv.NULL;
    }
    ClassInfo ci = env.getReferredClassInfo(robj);
    MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();

    if ((enclosingMethod != null) && !enclosingMethod.isCtor()){ 
      return createMethodObject(env, mci, enclosingMethod); 
    }
    return MJIEnv.NULL;
  }

  @MJI
  public boolean isAnonymousClass____Z (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo(robj);
    String cname = null;
    if (ci.getName().contains("$")){
      cname = ci.getName().substring(ci.getName().lastIndexOf('$') + 1);
    }
    return (cname == null) ? false : cname.matches("\\d+?");
  }

  @MJI
  public boolean isEnum____Z (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo(robj);
    return ci.isEnum();
  }

  // Similar to getEnclosingClass() except it returns null for the case of
  // anonymous class.
  @MJI
  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef){
    ClassInfo ci = env.getReferredClassInfo(clsRef);
    if (isLocalOrAnonymousClass(ci)){
      return MJIEnv.NULL;
    } else{
      return getEnclosingClass____Ljava_lang_Class_2(env, clsRef);
    }
  }

  @MJI
  public boolean isLocalClass____Z (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo(robj);
    return isLocalOrAnonymousClass(ci) && !isAnonymousClass____Z(env, robj);
  }

  private boolean isLocalOrAnonymousClass (ClassInfo ci){
    return (ci.getEnclosingMethodInfo() != null);
  }

  @MJI
  public boolean isMemberClass____Z (MJIEnv env, int robj){
    ClassInfo ci = env.getReferredClassInfo(robj);
    return (ci.getEnclosingClassInfo() != null) && !isLocalOrAnonymousClass(ci);
  }

  /**
   * Append the package name prefix of the class represented by robj, if the name is not 
   * absolute. OW, remove leading "/". 
   */
  @MJI
  public int getResolvedName__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int robj, int resRef){
    String rname = env.getStringObject(resRef);
    ClassInfo ci = env.getReferredClassInfo(robj);
    if (rname == null) {
      return MJIEnv.NULL;
    }
    if (!rname.startsWith("/")) {
      ClassInfo c = ci;
      while (c.isArray()) {
          c = c.getComponentClassInfo();
      }
      String baseName = c.getName();
      int index = baseName.lastIndexOf('.');
      if (index != -1) {
        rname = baseName.substring(0, index).replace('.', '/')
            +"/"+rname;
      }
    } else {
        rname = rname.substring(1);
    }

    return env.newString(rname);
  }
}