From 6bce58f1bc9d0dd74d089c6dd9ed7bb293de2614 Mon Sep 17 00:00:00 2001 From: jfrijters Date: Thu, 21 Aug 2008 06:40:22 +0000 Subject: [PATCH] Changed JNI reflection to be based on Java reflection (where possible). --- runtime/JniInterface.cs | 116 ++++++++++++++-------------------- runtime/MemberWrapper.cs | 131 ++++++++++++++++++++++++++++++++++++++- runtime/TypeWrapper.cs | 8 ++- runtime/openjdk.cs | 8 +-- runtime/vm.cs | 13 ++++ 5 files changed, 202 insertions(+), 74 deletions(-) diff --git a/runtime/JniInterface.cs b/runtime/JniInterface.cs index f3bf221e..859b0fae 100644 --- a/runtime/JniInterface.cs +++ b/runtime/JniInterface.cs @@ -1405,7 +1405,8 @@ namespace IKVM.Runtime try { wrapper.Finish(); - exception = (Exception)mw.InvokeJNI(null, new object[] { StringFromOEM(msg) }, false, null); + java.lang.reflect.Constructor cons = (java.lang.reflect.Constructor)mw.ToMethodOrConstructor(false); + exception = (Exception)cons.newInstance(new object[] { StringFromOEM(msg) }, (ikvm.@internal.CallerID)JVM.CreateCallerID(pEnv->currentMethod)); rc = JNI_OK; } catch(RetargetableJavaException x) @@ -1635,28 +1636,28 @@ namespace IKVM.Runtime switch(sig[i]) { case 'Z': - argarray[i] = args[i].z != JNI_FALSE; + argarray[i] = java.lang.Boolean.valueOf(args[i].z != JNI_FALSE); break; case 'B': - argarray[i] = args[i].b; + argarray[i] = java.lang.Byte.valueOf((byte)args[i].b); break; case 'C': - argarray[i] = (char)args[i].c; + argarray[i] = java.lang.Character.valueOf((char)args[i].c); break; case 'S': - argarray[i] = args[i].s; + argarray[i] = java.lang.Short.valueOf(args[i].s); break; case 'I': - argarray[i] = args[i].i; + argarray[i] = java.lang.Integer.valueOf(args[i].i); break; case 'J': - argarray[i] = args[i].j; + argarray[i] = java.lang.Long.valueOf(args[i].j); break; case 'F': - argarray[i] = args[i].f; + argarray[i] = java.lang.Float.valueOf(args[i].f); break; case 'D': - argarray[i] = args[i].d; + argarray[i] = java.lang.Double.valueOf(args[i].d); break; case 'L': argarray[i] = pEnv->UnwrapRef(args[i].l); @@ -2003,120 +2004,99 @@ namespace IKVM.Runtime return FindFieldID(pEnv, clazz, name, sig, false); } - private static void SetFieldValue(jfieldID cookie, object obj, object val) + private static sun.reflect.FieldAccessor GetFieldAccessor(jfieldID cookie) { - try - { - FieldWrapper.FromCookie(cookie).SetValue(obj, val); - } - catch - { - Debug.Assert(false); - throw; - } - } - - private static object GetFieldValue(jfieldID cookie, object obj) - { - try - { - return FieldWrapper.FromCookie(cookie).GetValue(obj); - } - catch - { - Debug.Assert(false); - throw; - } + return (sun.reflect.FieldAccessor)FieldWrapper.FromCookie(cookie).GetFieldAccessorJNI(); } internal static jobject GetObjectField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return pEnv->MakeLocalRef(GetFieldValue(fieldID, pEnv->UnwrapRef(obj))); + return pEnv->MakeLocalRef(GetFieldAccessor(fieldID).get(pEnv->UnwrapRef(obj))); } internal static jboolean GetBooleanField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return ((bool)GetFieldValue(fieldID, pEnv->UnwrapRef(obj))) ? JNI_TRUE : JNI_FALSE; + return GetFieldAccessor(fieldID).getBoolean(pEnv->UnwrapRef(obj)) ? JNI_TRUE : JNI_FALSE; } internal static jbyte GetByteField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jbyte)(byte)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jbyte)GetFieldAccessor(fieldID).getByte(pEnv->UnwrapRef(obj)); } internal static jchar GetCharField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jchar)(char)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jchar)GetFieldAccessor(fieldID).getChar(pEnv->UnwrapRef(obj)); } internal static jshort GetShortField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jshort)(short)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jshort)GetFieldAccessor(fieldID).getShort(pEnv->UnwrapRef(obj)); } internal static jint GetIntField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jint)(int)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jint)GetFieldAccessor(fieldID).getInt(pEnv->UnwrapRef(obj)); } internal static jlong GetLongField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jlong)(long)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jlong)GetFieldAccessor(fieldID).getLong(pEnv->UnwrapRef(obj)); } internal static jfloat GetFloatField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jfloat)(float)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jfloat)GetFieldAccessor(fieldID).getFloat(pEnv->UnwrapRef(obj)); } internal static jdouble GetDoubleField(JNIEnv* pEnv, jobject obj, jfieldID fieldID) { - return (jdouble)(double)GetFieldValue(fieldID, pEnv->UnwrapRef(obj)); + return (jdouble)GetFieldAccessor(fieldID).getDouble(pEnv->UnwrapRef(obj)); } internal static void SetObjectField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jobject val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), pEnv->UnwrapRef(val)); + GetFieldAccessor(fieldID).set(pEnv->UnwrapRef(obj), pEnv->UnwrapRef(val)); } internal static void SetBooleanField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jboolean val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), val != JNI_FALSE); + GetFieldAccessor(fieldID).setBoolean(pEnv->UnwrapRef(obj), val != JNI_FALSE); } internal static void SetByteField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jbyte val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (byte)val); + GetFieldAccessor(fieldID).setByte(pEnv->UnwrapRef(obj), (byte)val); } internal static void SetCharField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jchar val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (char)val); + GetFieldAccessor(fieldID).setChar(pEnv->UnwrapRef(obj), (char)val); } internal static void SetShortField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jshort val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (short)val); + GetFieldAccessor(fieldID).setShort(pEnv->UnwrapRef(obj), (short)val); } internal static void SetIntField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jint val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (int)val); + GetFieldAccessor(fieldID).setInt(pEnv->UnwrapRef(obj), (int)val); } internal static void SetLongField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jlong val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (long)val); + GetFieldAccessor(fieldID).setLong(pEnv->UnwrapRef(obj), (long)val); } internal static void SetFloatField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jfloat val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (float)val); + GetFieldAccessor(fieldID).setFloat(pEnv->UnwrapRef(obj), (float)val); } internal static void SetDoubleField(JNIEnv* pEnv, jobject obj, jfieldID fieldID, jdouble val) { - SetFieldValue(fieldID, pEnv->UnwrapRef(obj), (double)val); + GetFieldAccessor(fieldID).setDouble(pEnv->UnwrapRef(obj), (double)val); } internal static jmethodID GetStaticMethodID(JNIEnv* pEnv, jclass clazz, byte* name, byte* sig) @@ -2221,92 +2201,92 @@ namespace IKVM.Runtime internal static jobject GetStaticObjectField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return pEnv->MakeLocalRef(GetFieldValue(fieldID, null)); + return pEnv->MakeLocalRef(GetFieldAccessor(fieldID).get(null)); } internal static jboolean GetStaticBooleanField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return ((bool)GetFieldValue(fieldID, null)) ? JNI_TRUE : JNI_FALSE; + return GetFieldAccessor(fieldID).getBoolean(null) ? JNI_TRUE : JNI_FALSE; } internal static jbyte GetStaticByteField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jbyte)(byte)GetFieldValue(fieldID, null); + return (jbyte)GetFieldAccessor(fieldID).getByte(null); } internal static jchar GetStaticCharField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jchar)(char)GetFieldValue(fieldID, null); + return (jchar)GetFieldAccessor(fieldID).getChar(null); } internal static jshort GetStaticShortField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jshort)(short)GetFieldValue(fieldID, null); + return (jshort)GetFieldAccessor(fieldID).getShort(null); } internal static jint GetStaticIntField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jint)(int)GetFieldValue(fieldID, null); + return (jint)GetFieldAccessor(fieldID).getInt(null); } internal static jlong GetStaticLongField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jlong)(long)GetFieldValue(fieldID, null); + return (jlong)GetFieldAccessor(fieldID).getLong(null); } internal static jfloat GetStaticFloatField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jfloat)(float)GetFieldValue(fieldID, null); + return (jfloat)GetFieldAccessor(fieldID).getFloat(null); } internal static jdouble GetStaticDoubleField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID) { - return (jdouble)(double)GetFieldValue(fieldID, null); + return (jdouble)GetFieldAccessor(fieldID).getDouble(null); } internal static void SetStaticObjectField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jobject val) { - SetFieldValue(fieldID, null, pEnv->UnwrapRef(val)); + GetFieldAccessor(fieldID).set(null, pEnv->UnwrapRef(val)); } internal static void SetStaticBooleanField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jboolean val) { - SetFieldValue(fieldID, null, val != JNI_FALSE); + GetFieldAccessor(fieldID).setBoolean(null, val != JNI_FALSE); } internal static void SetStaticByteField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jbyte val) { - SetFieldValue(fieldID, null, (byte)val); + GetFieldAccessor(fieldID).setByte(null, (byte)val); } internal static void SetStaticCharField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jchar val) { - SetFieldValue(fieldID, null, (char)val); + GetFieldAccessor(fieldID).setChar(null, (char)val); } internal static void SetStaticShortField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jshort val) { - SetFieldValue(fieldID, null, (short)val); + GetFieldAccessor(fieldID).setShort(null, (short)val); } internal static void SetStaticIntField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jint val) { - SetFieldValue(fieldID, null, (int)val); + GetFieldAccessor(fieldID).setInt(null, (int)val); } internal static void SetStaticLongField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jlong val) { - SetFieldValue(fieldID, null, (long)val); + GetFieldAccessor(fieldID).setLong(null, (long)val); } internal static void SetStaticFloatField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jfloat val) { - SetFieldValue(fieldID, null, (float)val); + GetFieldAccessor(fieldID).setFloat(null, (float)val); } internal static void SetStaticDoubleField(JNIEnv* pEnv, jclass clazz, jfieldID fieldID, jdouble val) { - SetFieldValue(fieldID, null, (double)val); + GetFieldAccessor(fieldID).setDouble(null, (double)val); } internal static jstring NewString(JNIEnv* pEnv, jchar* unicode, int len) diff --git a/runtime/MemberWrapper.cs b/runtime/MemberWrapper.cs index fe66e458..e73dafec 100644 --- a/runtime/MemberWrapper.cs +++ b/runtime/MemberWrapper.cs @@ -29,6 +29,7 @@ using System.Reflection.Emit; #endif using System.Diagnostics; using IKVM.Attributes; +using System.Threading; namespace IKVM.Internal { @@ -278,6 +279,7 @@ namespace IKVM.Internal abstract class MethodWrapper : MemberWrapper { #if !STATIC_COMPILER && !FIRST_PASS + private static Dictionary invokenonvirtualCache; private volatile object reflectionMethod; #endif internal static readonly MethodWrapper[] EmptyArray = new MethodWrapper[0]; @@ -704,9 +706,122 @@ namespace IKVM.Internal #if FIRST_PASS return null; #else - return Invoke(obj, args, nonVirtual, ikvm.@internal.CallerID.create(callerID)); + if (ReferenceEquals(Name, StringConstants.INIT)) + { + java.lang.reflect.Constructor cons = (java.lang.reflect.Constructor)ToMethodOrConstructor(false); + if (obj == null) + { + sun.reflect.ConstructorAccessor acc = cons.getConstructorAccessor(); + if (acc == null) + { + acc = reflectionFactory.newConstructorAccessor(cons); + cons.setConstructorAccessor(acc); + } + return acc.newInstance(args); + } + else if (method is MethodInfo) + { + Debug.Assert(method.IsStatic); + // we're dealing with a constructor on a remapped type, if obj is supplied, it means + // that we should call the constructor on an already existing instance, but that isn't + // possible with remapped types + // the type of this exception is a bit random (note that this can only happen through JNI reflection) + throw new java.lang.IncompatibleClassChangeError(string.Format("Remapped type {0} doesn't support constructor invocation on an existing instance", DeclaringType.Name)); + } + else if (!method.DeclaringType.IsInstanceOfType(obj)) + { + // we're trying to initialize an existing instance of a remapped type + throw new NotSupportedException("Unable to partially construct object of type " + obj.GetType().FullName + " to type " + method.DeclaringType.FullName); + } + else + { + try + { + InvokeArgsProcessor proc = new InvokeArgsProcessor(this, method, obj, UnboxArgs(args), ikvm.@internal.CallerID.create(callerID)); + object o = method.Invoke(proc.GetObj(), proc.GetArgs()); + TypeWrapper retType = this.ReturnType; + if (!retType.IsUnloadable && retType.IsGhost) + { + o = retType.GhostRefField.GetValue(o); + } + return o; + } + catch (ArgumentException x1) + { + throw new java.lang.IllegalArgumentException(x1.Message); + } + catch (TargetInvocationException x) + { + throw new java.lang.reflect.InvocationTargetException(ikvm.runtime.Util.mapException(x.InnerException)); + } + } + } + else if (nonVirtual + && !this.IsStatic + && !this.IsPrivate + && !this.IsAbstract + && !this.IsFinal + && !this.DeclaringType.IsFinal) + { + if (this.DeclaringType.IsRemapped) + { + ResolveMethod(); + return InvokeImpl(method, obj, UnboxArgs(args), true, ikvm.@internal.CallerID.create(callerID)); + } + else + { + if (invokenonvirtualCache == null) + { + Interlocked.CompareExchange(ref invokenonvirtualCache, new Dictionary(), null); + } + sun.reflect.MethodAccessor acc; + lock (invokenonvirtualCache) + { + if (!invokenonvirtualCache.TryGetValue(this, out acc)) + { + acc = new IKVM.NativeCode.sun.reflect.ReflectionFactory.FastMethodAccessorImpl((java.lang.reflect.Method)ToMethodOrConstructor(false), true); + invokenonvirtualCache.Add(this, acc); + } + } + object val = acc.invoke(obj, args, ikvm.@internal.CallerID.create(callerID)); + if (this.ReturnType.IsPrimitive && this.ReturnType != PrimitiveTypeWrapper.VOID) + { + val = JVM.Unbox(val); + } + return val; + } + } + else + { + java.lang.reflect.Method method = (java.lang.reflect.Method)ToMethodOrConstructor(false); + sun.reflect.MethodAccessor acc = method.getMethodAccessor(); + if (acc == null) + { + acc = reflectionFactory.newMethodAccessor(method); + method.setMethodAccessor(acc); + } + object val = acc.invoke(obj, args, ikvm.@internal.CallerID.create(callerID)); + if (this.ReturnType.IsPrimitive && this.ReturnType != PrimitiveTypeWrapper.VOID) + { + val = JVM.Unbox(val); + } + return val; + } #endif } + + private object[] UnboxArgs(object[] args) + { + TypeWrapper[] paramTypes = GetParameters(); + for (int i = 0; i < paramTypes.Length; i++) + { + if (paramTypes[i].IsPrimitive) + { + args[i] = JVM.Unbox(args[i]); + } + } + return args; + } #endif // !STATIC_COMPILER #if !STATIC_COMPILER && !FIRST_PASS @@ -1183,6 +1298,7 @@ namespace IKVM.Internal #if !STATIC_COMPILER && !FIRST_PASS private static readonly FieldInfo slotField = typeof(java.lang.reflect.Field).GetField("slot", BindingFlags.Instance | BindingFlags.NonPublic); private volatile object reflectionField; + private sun.reflect.FieldAccessor jniAccessor; #endif internal static readonly FieldWrapper[] EmptyArray = new FieldWrapper[0]; private FieldInfo field; @@ -1414,6 +1530,19 @@ namespace IKVM.Internal #endif } } + + internal object GetFieldAccessorJNI() + { +#if FIRST_PASS + return null; +#else + if (jniAccessor == null) + { + jniAccessor = reflectionFactory.newFieldAccessor((java.lang.reflect.Field)ToField(false), true); + } + return jniAccessor; +#endif + } #endif // !STATIC_COMPILER internal virtual object GetValue(object obj) diff --git a/runtime/TypeWrapper.cs b/runtime/TypeWrapper.cs index b5af2f95..3c53f978 100644 --- a/runtime/TypeWrapper.cs +++ b/runtime/TypeWrapper.cs @@ -11315,7 +11315,13 @@ namespace IKVM.Internal protected override void EmitSetImpl(CodeEmitter ilgen) { - throw new InvalidOperationException(); + // NOTE even though the field is final, JNI reflection can still be used to set its value! + LocalBuilder temp = ilgen.AllocTempLocal(underlyingType); + ilgen.Emit(OpCodes.Stloc, temp); + ilgen.Emit(OpCodes.Unbox, underlyingType); + ilgen.Emit(OpCodes.Ldloc, temp); + ilgen.Emit(OpCodes.Stobj, underlyingType); + ilgen.ReleaseTempLocal(temp); } #endif diff --git a/runtime/openjdk.cs b/runtime/openjdk.cs index 00351ecf..03e9a814 100644 --- a/runtime/openjdk.cs +++ b/runtime/openjdk.cs @@ -5859,7 +5859,7 @@ namespace IKVM.NativeCode.sun.reflect } } - private sealed class FastMethodAccessorImpl : srMethodAccessor + internal sealed class FastMethodAccessorImpl : srMethodAccessor { private static readonly MethodInfo valueOfByte; private static readonly MethodInfo valueOfBoolean; @@ -5927,7 +5927,7 @@ namespace IKVM.NativeCode.sun.reflect } } - internal FastMethodAccessorImpl(jlrMethod method) + internal FastMethodAccessorImpl(jlrMethod method, bool nonvirtual) { MethodWrapper mw = MethodWrapper.FromMethodOrConstructor(method); mw.DeclaringType.Finish(); @@ -6018,7 +6018,7 @@ namespace IKVM.NativeCode.sun.reflect { ilgen.Emit(OpCodes.Ldarg_2); } - if (mw.IsStatic) + if (mw.IsStatic || nonvirtual) { mw.EmitCall(ilgen); } @@ -7672,7 +7672,7 @@ namespace IKVM.NativeCode.sun.reflect TypeWrapper tw = TypeWrapper.FromClass(m.getDeclaringClass()); if (!mw.IsDynamicOnly && !tw.IsRemapped) { - return new FastMethodAccessorImpl(m); + return new FastMethodAccessorImpl(m, false); } } return new MethodAccessorImpl(m); diff --git a/runtime/vm.cs b/runtime/vm.cs index 2943a9f3..94e992de 100644 --- a/runtime/vm.cs +++ b/runtime/vm.cs @@ -379,6 +379,19 @@ namespace IKVM.Internal } #endif + +#if !STATIC_COMPILER + // helper for JNI (which doesn't have access to core library internals) + internal static object CreateCallerID(RuntimeMethodHandle method) + { +#if FIRST_PASS + return null; +#else + return ikvm.@internal.CallerID.create(MethodBase.GetMethodFromHandle(method)); +#endif + } +#endif + #if !STATIC_COMPILER // helper for JNI (which doesn't have access to core library internals) internal static object NewDirectByteBuffer(long address, int capacity)