Mercurial > hg > Members > kono > jpf-core
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java Fri Jan 23 10:14:01 2015 -0800 @@ -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.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(); + } +}