view src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java @ 0:61d41facf527

initial v8 import (history reset)
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Fri, 23 Jan 2015 10:14:01 -0800
parents
children db918c531e6d
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.pushRequiredClinits(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.pushRequiredClinits(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.pushRequiredClinits(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.pushRequiredClinits(ti)){
      return null;
    } else {
      return ci;
    }    
  }
  
  @MJI
  public void initialize0____V (MJIEnv env, int clsObjRef){
    ClassInfo ci = env.getReferredClassInfo( clsObjRef);
    ci.pushRequiredClinits(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.pushRequiredClinits(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.isRegistered()){
      ThreadInfo ti = env.getThreadInfo();
      ciEncl.registerClass(ti);
      
      if (ciEncl.initializeClass(ti)){
        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);
  }
}