Initial import
[jpf-core.git] / src / peers / gov / nasa / jpf / vm / JPF_java_lang_reflect_Method.java
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java
new file mode 100644 (file)
index 0000000..66b564e
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * 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 gov.nasa.jpf.util.MethodInfoRegistry;
+import gov.nasa.jpf.util.RunListener;
+import gov.nasa.jpf.util.RunRegistry;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+public class JPF_java_lang_reflect_Method extends NativePeer {
+
+  static MethodInfoRegistry registry;
+
+  // class init - this is called automatically from the NativePeer ctor
+  public static boolean init (Config conf) {
+    // this is an example of how to handle cross-initialization between
+    // native peers - this might also get explicitly called by the java.lang.Class
+    // peer, since it creates Method objects. Here we have to make sure
+    // we only reset between JPF runs
+
+    if (registry == null){
+      registry = new MethodInfoRegistry();
+      
+      RunRegistry.getDefaultRegistry().addListener( new RunListener() {
+        @Override
+               public void reset (RunRegistry reg){
+          registry = null;
+        }
+      });
+    }
+    return true;
+  }
+
+  static int createMethodObject (MJIEnv env, ClassInfo ciMth, MethodInfo mi){
+    // note - it is the callers responsibility to ensure Method is properly initialized    
+    int regIdx = registry.registerMethodInfo(mi);
+    int eidx = env.newObject( ciMth);
+    ElementInfo ei = env.getModifiableElementInfo(eidx);
+    
+    ei.setIntField("regIdx", regIdx);
+    ei.setBooleanField("isAccessible", mi.isPublic());
+    
+    return eidx;
+  }
+  
+  // this is NOT an MJI method, but it is used outside this package, so
+  // we have to add 'final'
+  public static final MethodInfo getMethodInfo (MJIEnv env, int objRef){
+    return registry.getMethodInfo(env,objRef, "regIdx");
+  }
+  
+  @MJI
+  public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    MethodInfo mi = getMethodInfo(env, objRef);
+    
+    int nameRef = env.getReferenceField( objRef, "name");
+    if (nameRef == MJIEnv.NULL) {
+      nameRef = env.newString(mi.getName());
+      env.setReferenceField(objRef, "name", nameRef);
+    }
+   
+    return nameRef;
+  }
+
+  @MJI
+  public int getModifiers____I (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    return mi.getModifiers();
+  }
+  
+  static int getParameterTypes( MJIEnv env, MethodInfo mi) {
+    ThreadInfo ti = env.getThreadInfo();
+    String[] argTypeNames = mi.getArgumentTypeNames();
+    int[] ar = new int[argTypeNames.length];
+
+    for (int i = 0; i < argTypeNames.length; i++) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(argTypeNames[i]);
+      if (!ci.isRegistered()) {
+        ci.registerClass(ti);
+      }
+
+      ar[i] = ci.getClassObjectRef();
+    }
+
+    int aRef = env.newObjectArray("Ljava/lang/Class;", argTypeNames.length);
+    for (int i = 0; i < argTypeNames.length; i++) {
+      env.setReferenceArrayElement(aRef, i, ar[i]);
+    }
+
+    return aRef;
+  }
+  
+  @MJI
+  public int getParameterTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    return getParameterTypes(env, getMethodInfo(env, objRef));
+  }
+  
+  int getExceptionTypes(MJIEnv env, MethodInfo mi) {
+    ThreadInfo ti = env.getThreadInfo();
+    String[] exceptionNames = mi.getThrownExceptionClassNames();
+     
+    if (exceptionNames == null) {
+      exceptionNames = new String[0];
+    }
+     
+    int[] ar = new int[exceptionNames.length];
+     
+    for (int i = 0; i < exceptionNames.length; i++) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(exceptionNames[i]);
+      if (!ci.isRegistered()) {
+        ci.registerClass(ti);
+      }
+       
+      ar[i] = ci.getClassObjectRef();
+    }
+     
+    int aRef = env.newObjectArray("Ljava/lang/Class;", exceptionNames.length);
+    for (int i = 0; i < exceptionNames.length; i++) {
+      env.setReferenceArrayElement(aRef, i, ar[i]);
+    }
+     
+    return aRef;
+  }
+  
+  @MJI
+  public int getExceptionTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef) {
+    return getExceptionTypes(env, getMethodInfo(env, objRef));
+  }
+  
+  @MJI
+  public int getReturnType____Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    ThreadInfo ti = env.getThreadInfo();
+
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(mi.getReturnTypeName());
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+    }
+
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);    
+    ClassInfo ci = mi.getClassInfo();
+    // it's got to be registered, otherwise we wouldn't be able to acquire the Method object
+    return ci.getClassObjectRef();
+  }
+    
+  static int createBoxedReturnValueObject (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame) {
+    byte rt = mi.getReturnTypeCode();
+    int ret = MJIEnv.NULL;
+    ElementInfo rei;
+    Object attr = null;
+
+    if (rt == Types.T_DOUBLE) {
+      attr = frame.getLongResultAttr();
+      double v = frame.getDoubleResult();
+      ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Double"));
+      rei = env.getModifiableElementInfo(ret);
+      rei.setDoubleField("value", v);
+    } else if (rt == Types.T_FLOAT) {
+      attr = frame.getResultAttr();
+      float v = frame.getFloatResult();
+      ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Float"));
+      rei = env.getModifiableElementInfo(ret);
+      rei.setFloatField("value", v);
+    } else if (rt == Types.T_LONG) {
+      attr = frame.getLongResultAttr();
+      long v = frame.getLongResult();
+      ret = env.valueOfLong(v);
+    } else if (rt == Types.T_BYTE) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfByte((byte)v);
+    } else if (rt == Types.T_CHAR) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfCharacter((char)v);
+    } else if (rt == Types.T_SHORT) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfShort((short)v);
+    } else if (rt == Types.T_INT) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfInteger(v);
+    } else if (rt == Types.T_BOOLEAN) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult();
+      ret = env.valueOfBoolean((v == 1)? true: false);
+    } else if (mi.isReferenceReturnType()){ 
+      attr = frame.getResultAttr();
+      ret = frame.getReferenceResult();
+    }
+
+    env.setReturnAttribute(attr);
+    return ret;
+  }
+
+  static boolean pushUnboxedArguments (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame, int argIdx, int argsRef) {
+    ElementInfo source;
+    ClassInfo sourceClass;
+    String destTypeNames[];
+    int nArgs, passedCount, sourceRef;
+    byte sourceType, destTypes[];
+
+    destTypes     = mi.getArgumentTypes();
+    destTypeNames = mi.getArgumentTypeNames();
+    nArgs         = destTypeNames.length;
+    
+    // according to the API docs, passing null instead of an empty array is allowed for no args
+    passedCount   = (argsRef != MJIEnv.NULL) ? env.getArrayLength(argsRef) : 0;
+    
+    if (nArgs != passedCount) {
+      env.throwException(IllegalArgumentException.class.getName(), "Wrong number of arguments passed.  Actual = " + passedCount + ".  Expected = " + nArgs);
+      return false;
+    }
+    
+    for (int i = 0; i < nArgs; i++) {
+      sourceRef = env.getReferenceArrayElement(argsRef, i);
+
+      // we have to handle null references explicitly
+      if (sourceRef == MJIEnv.NULL) {
+        if ((destTypes[i] != Types.T_REFERENCE) && (destTypes[i] != Types.T_ARRAY)) {
+          env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Actual = (null).  Expected = " + destTypeNames[i]);
+          return false;
+        } 
+         
+        frame.pushRef(MJIEnv.NULL);
+        continue;
+      }
+
+      source      = env.getElementInfo(sourceRef);
+      sourceClass = source.getClassInfo();   
+      sourceType = getSourceType( sourceClass, destTypes[i], destTypeNames[i]);
+
+      Object attr = env.getElementInfo(argsRef).getFields().getFieldAttr(i);
+      if ((argIdx = pushArg( argIdx, frame, source, sourceType, destTypes[i], attr)) < 0 ){
+        env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Source Class = " + sourceClass.getName() + ".  Dest Class = " + destTypeNames[i]);
+        return false;        
+      }
+    }
+    
+    return true;
+  }
+
+  // this returns the primitive type in case we have to unbox, and otherwise checks reference type compatibility
+  private static byte getSourceType (ClassInfo ciArgVal, byte destType, String destTypeName){
+    switch (destType){
+    // the primitives
+    case Types.T_BOOLEAN:
+    case Types.T_BYTE:
+    case Types.T_CHAR:
+    case Types.T_SHORT:
+    case Types.T_INT:
+    case Types.T_LONG:
+    case Types.T_FLOAT:
+    case Types.T_DOUBLE:
+      return Types.getUnboxedType(ciArgVal.getName());
+      
+    case Types.T_ARRAY:
+    case Types.T_REFERENCE: // check if the source type is assignment compatible with the destType
+      if (ciArgVal.isInstanceOf(destTypeName)){
+        return destType;
+      }
+    }
+    
+    return Types.T_NONE;
+  }
+  
+  // do the proper type conversion - Java is pretty forgiving here and does
+  // not throw exceptions upon value truncation
+  private static int pushArg( int argIdx, DirectCallStackFrame frame, ElementInfo eiArg, byte srcType, byte destType, Object attr){    
+    switch (srcType) {
+    case Types.T_DOUBLE:
+    {
+      double v = eiArg.getDoubleField("value");
+      if (destType == Types.T_DOUBLE){
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_FLOAT: // covers float, double
+    {
+      float v = eiArg.getFloatField("value");
+      switch (destType){
+      case Types.T_FLOAT:
+        return frame.setFloatArgument( argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_LONG:
+    {
+      long v = eiArg.getLongField("value");
+      switch (destType){
+      case Types.T_LONG:
+        return frame.setLongArgument(argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_INT:
+    { 
+      int v = eiArg.getIntField("value");
+      switch (destType){
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_SHORT:
+    { 
+      int v = eiArg.getShortField("value");
+      switch (destType){
+      case Types.T_SHORT:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_BYTE:
+    { 
+      byte v = eiArg.getByteField("value");
+      switch (destType){
+      case Types.T_BYTE:
+      case Types.T_SHORT:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_CHAR:
+    {
+      char v = eiArg.getCharField("value");
+      switch (destType){
+      case Types.T_CHAR:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_BOOLEAN:
+    {
+      boolean v = eiArg.getBooleanField("value");
+      if (destType == Types.T_BOOLEAN){
+        return frame.setArgument( argIdx, v ? 1 : 0, attr);
+      }
+      return -1;
+    }
+    case Types.T_ARRAY:
+    {
+      int ref =  eiArg.getObjectRef();
+      if (destType == Types.T_ARRAY){
+        return frame.setReferenceArgument( argIdx, ref, attr);
+      }
+      return -1;
+    }
+    case Types.T_REFERENCE:
+    {
+      int ref =  eiArg.getObjectRef();
+      if (destType == Types.T_REFERENCE){
+        return frame.setReferenceArgument( argIdx, ref, attr);
+      }
+      return -1;
+    }
+    default:
+      // T_VOID, T_NONE
+      return -1;
+    }
+  }
+
+  @MJI
+  public int invoke__Ljava_lang_Object_2_3Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int mthRef, int objRef, int argsRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    MethodInfo miCallee = getMethodInfo(env, mthRef);
+    ClassInfo calleeClass = miCallee.getClassInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+    
+    if (frame == null){ // first time
+
+      //--- check the instance we are calling on
+      if (!miCallee.isStatic()) {
+        if (objRef == MJIEnv.NULL){
+          env.throwException("java.lang.NullPointerException");
+          return MJIEnv.NULL;
+          
+        } else {
+          ElementInfo eiObj = ti.getElementInfo(objRef);
+          ClassInfo objClass = eiObj.getClassInfo();
+        
+          if (!objClass.isInstanceOf(calleeClass)) {
+            env.throwException(IllegalArgumentException.class.getName(), "Object is not an instance of declaring class.  Actual = " + objClass + ".  Expected = " + calleeClass);
+            return MJIEnv.NULL;
+          }
+        }
+      }
+
+      //--- check accessibility
+      ElementInfo eiMth = ti.getElementInfo(mthRef);
+      if (! (Boolean) eiMth.getFieldValueObject("isAccessible")) {
+        StackFrame caller = ti.getTopFrame().getPrevious();
+        ClassInfo callerClass = caller.getClassInfo();
+
+        if (callerClass != calleeClass) {
+          env.throwException(IllegalAccessException.class.getName(), "Class " + callerClass.getName() +
+                  " can not access a member of class " + calleeClass.getName()
+                  + " with modifiers \"" + Modifier.toString(miCallee.getModifiers()));
+          return MJIEnv.NULL;
+        }
+      }
+      
+      //--- push the direct call
+      frame = miCallee.createDirectCallStackFrame(ti, 0);
+      frame.setReflection();
+      
+      int argOffset = 0;
+      if (!miCallee.isStatic()) {
+        frame.setReferenceArgument( argOffset++, objRef, null);
+      }
+      if (!pushUnboxedArguments( env, miCallee, frame, argOffset, argsRef)) {
+        // we've got a IllegalArgumentException
+        return MJIEnv.NULL;  
+      }
+      ti.pushFrame(frame);
+      
+      
+      //--- check for and push required clinits
+      if (miCallee.isStatic()){
+        calleeClass.initializeClass(ti);
+      }
+      
+      return MJIEnv.NULL; // reexecute
+      
+    } else { // we have returned from the direct call
+      return createBoxedReturnValueObject( env, miCallee, frame);
+    }
+  }
+  
+
+  // this one has to collect annotations upwards in the inheritance chain
+  static int getAnnotations (MJIEnv env, MethodInfo mi){
+    String mname = mi.getName();
+    String msig = mi.genericSignature;
+    ArrayList<AnnotationInfo> aiList = new ArrayList<AnnotationInfo>();
+    
+    // our own annotations
+    ClassInfo ci = mi.getClassInfo();
+    for (AnnotationInfo ai : mi.getAnnotations()) {
+      aiList.add(ai);
+    }
+    
+    // our superclass annotations
+    for (ci = ci.getSuperClass(); ci != null; ci = ci.getSuperClass()){
+      mi = ci.getMethod(mname, msig, false);
+      if (mi != null){
+        for (AnnotationInfo ai: mi.getAnnotations()){
+          aiList.add(ai);
+        }        
+      }
+    }
+
+    try {
+      return env.newAnnotationProxies(aiList.toArray(new AnnotationInfo[aiList.size()]));
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getAnnotations( env, getMethodInfo(env,mthRef));
+  }
+  
+  // the following ones consist of a package default implementation that is shared with
+  // the constructor peer, and a public model method
+  static int getAnnotation (MJIEnv env, MethodInfo mi, int annotationClsRef){
+    ClassInfo aci = env.getReferredClassInfo(annotationClsRef);
+    
+    AnnotationInfo ai = mi.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;
+      }
+    }
+    
+    return MJIEnv.NULL;
+  }  
+
+  @MJI
+  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef, int annotationClsRef) {
+    return getAnnotation(env, getMethodInfo(env,mthRef), annotationClsRef);
+  }
+  
+  static int getDeclaredAnnotations (MJIEnv env, MethodInfo mi){
+    AnnotationInfo[] ai = mi.getAnnotations();
+
+    try {
+      return env.newAnnotationProxies(ai);
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getDeclaredAnnotations( env, getMethodInfo(env,mthRef));
+  }
+  
+  static int getParameterAnnotations (MJIEnv env, MethodInfo mi){
+    AnnotationInfo[][] pa = mi.getParameterAnnotations();
+    // this should always return an array object, even if the method has no arguments
+    
+    try {
+      int paRef = env.newObjectArray("[Ljava/lang/annotation/Annotation;", pa.length);
+      
+      for (int i=0; i<pa.length; i++){
+        int eRef = env.newAnnotationProxies(pa[i]);
+        env.setReferenceArrayElement(paRef, i, eRef);
+      }
+
+      return paRef;
+      
+    } catch (ClinitRequired x){ // be prepared that we might have to initialize respective annotation classes
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getParameterAnnotations_____3_3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getParameterAnnotations( env, getMethodInfo(env,mthRef));
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    StringBuilder sb = new StringBuilder();
+    
+    MethodInfo mi = getMethodInfo(env, objRef);
+
+    sb.append(Modifier.toString(mi.getModifiers()));
+    sb.append(' ');
+
+    sb.append(mi.getReturnTypeName());
+    sb.append(' ');
+
+    sb.append(mi.getClassName());
+    sb.append('.');
+
+    sb.append(mi.getName());
+
+    sb.append('(');
+    
+    String[] at = mi.getArgumentTypeNames();
+    for (int i=0; i<at.length; i++){
+      if (i>0) sb.append(',');
+      sb.append(at[i]);
+    }
+    
+    sb.append(')');
+    
+    int sref = env.newString(sb.toString());
+    return sref;
+  }
+
+  @MJI
+  public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int mthRef){
+    ElementInfo ei = env.getElementInfo(mthRef);
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(JPF_java_lang_Class.METHOD_CLASSNAME);
+
+    if (ei.getClassInfo() == ci){
+      MethodInfo mi1 = getMethodInfo(env, objRef);
+      MethodInfo mi2 = getMethodInfo(env, mthRef);
+      if (mi1.getClassInfo() == mi2.getClassInfo()){
+        if (mi1.getName().equals(mi2.getName())){
+          if (mi1.getReturnType().equals(mi2.getReturnType())){
+            byte[] params1 = mi1.getArgumentTypes();
+            byte[] params2 = mi2.getArgumentTypes();
+            if (params1.length == params2.length){
+              for (int i = 0; i < params1.length; i++){
+                if (params1[i] != params2[i]){
+                  return false;
+                }
+              }
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    return mi.getClassName().hashCode() ^ mi.getName().hashCode();
+  }
+}