Mercurial > hg > Members > kono > jpf-core
view src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.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 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.pushRequiredClinits(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(); } }