diff options
author | jfrijters <jfrijters> | 2013-02-24 19:29:06 +0400 |
---|---|---|
committer | jfrijters <jfrijters> | 2013-02-24 19:29:06 +0400 |
commit | f57522835372ad726e3ce3b6b58efbb155af154c (patch) | |
tree | 053c97c1c49ce6950f946a38f86dd2849689db00 | |
parent | b1b5fee0342725a1fe7cc28f138292448922916a (diff) |
Reimplemented dynamic binding on top of MethodHandles. This avoids having to instantiate a java.lang.reflect.Method which might fail if it declares a checked exception that is not loadable. It also has the potential of being faster, but no perf work has been done yet.
-rw-r--r-- | runtime/ByteCodeHelper.cs | 149 | ||||
-rw-r--r-- | runtime/compiler.cs | 181 |
2 files changed, 143 insertions, 187 deletions
diff --git a/runtime/ByteCodeHelper.cs b/runtime/ByteCodeHelper.cs index c6f50770..64769463 100644 --- a/runtime/ByteCodeHelper.cs +++ b/runtime/ByteCodeHelper.cs @@ -342,123 +342,6 @@ namespace IKVM.Runtime return wrapper.IsInstance(obj); } - private static MethodWrapper GetMethodWrapper(object thisObj, string clazz, string name, string sig, bool isStatic, ikvm.@internal.CallerID callerId) - { - TypeWrapper caller = TypeWrapper.FromClass(callerId.getCallerClass()); - TypeWrapper wrapper = LoadTypeWrapper(clazz, callerId); - MethodWrapper mw = wrapper.GetMethodWrapper(name, sig, name != StringConstants.INIT); - if(mw == null) - { - throw new java.lang.NoSuchMethodError(clazz + "." + name + sig); - } - // TODO check loader constraints - if(mw.IsStatic != isStatic) - { - throw new java.lang.IncompatibleClassChangeError(clazz + "." + name); - } - TypeWrapper objType = null; - if(thisObj != null) - { - if(!wrapper.IsInstance(thisObj)) - { - throw new java.lang.IncompatibleClassChangeError(clazz + "." + name); - } - objType = ClassLoaderWrapper.GetWrapperFromType(thisObj.GetType()); - } - if(mw.IsAccessibleFrom(wrapper, caller, objType)) - { - return mw; - } - throw new java.lang.IllegalAccessError(clazz + "." + name + sig); - } - - [DebuggerStepThroughAttribute] - public static object DynamicInvokeSpecialNew(string clazz, string name, string sig, object[] args, ikvm.@internal.CallerID callerID) - { -#if FIRST_PASS - return null; -#else - Profiler.Count("DynamicInvokeSpecialNew"); - MethodWrapper mw = GetMethodWrapper(null, clazz, name, sig, false, callerID); - if (mw.DeclaringType.IsAbstract) - { - throw new java.lang.InstantiationError(mw.DeclaringType.Name); - } - try - { - java.lang.reflect.Constructor cons = (java.lang.reflect.Constructor)mw.ToMethodOrConstructor(false); - return cons.newInstance(BoxArgs(mw, args), callerID); - } - catch (java.lang.reflect.InvocationTargetException x) - { - throw x.getCause(); - } -#endif - } - - [DebuggerStepThroughAttribute] - public static object DynamicInvokestatic(string clazz, string name, string sig, object[] args, ikvm.@internal.CallerID callerID) - { -#if FIRST_PASS - return null; -#else - Profiler.Count("DynamicInvokestatic"); - MethodWrapper mw = GetMethodWrapper(null, clazz, name, sig, true, callerID); - java.lang.reflect.Method m = (java.lang.reflect.Method)mw.ToMethodOrConstructor(false); - try - { - object val = m.invoke(null, BoxArgs(mw, args), callerID); - if (mw.ReturnType.IsPrimitive && mw.ReturnType != PrimitiveTypeWrapper.VOID) - { - val = JVM.Unbox(val); - } - return val; - } - catch (java.lang.reflect.InvocationTargetException x) - { - throw x.getCause(); - } -#endif - } - - [DebuggerStepThroughAttribute] - public static object DynamicInvokevirtual(object obj, string clazz, string name, string sig, object[] args, ikvm.@internal.CallerID callerID) - { -#if FIRST_PASS - return null; -#else - Profiler.Count("DynamicInvokevirtual"); - MethodWrapper mw = GetMethodWrapper(obj, clazz, name, sig, false, callerID); - java.lang.reflect.Method m = (java.lang.reflect.Method)mw.ToMethodOrConstructor(false); - try - { - object val = m.invoke(obj, BoxArgs(mw, args), callerID); - if (mw.ReturnType.IsPrimitive && mw.ReturnType != PrimitiveTypeWrapper.VOID) - { - val = JVM.Unbox(val); - } - return val; - } - catch (java.lang.reflect.InvocationTargetException x) - { - throw x.getCause(); - } -#endif - } - - private static object[] BoxArgs(MethodWrapper mw, object[] args) - { - TypeWrapper[] paramTypes = mw.GetParameters(); - for (int i = 0; i < paramTypes.Length; i++) - { - if (paramTypes[i].IsPrimitive) - { - args[i] = JVM.Box(args[i]); - } - } - return args; - } - [DebuggerStepThrough] public static java.lang.invoke.MethodType DynamicLoadMethodType(ref java.lang.invoke.MethodType cache, string sig, ikvm.@internal.CallerID callerID) { @@ -566,6 +449,38 @@ namespace IKVM.Runtime } [DebuggerStepThrough] + public static T DynamicBinderMemberLookup<T>(int kind, string clazz, string name, string sig, ikvm.@internal.CallerID callerID) + where T : class /* delegate */ + { +#if FIRST_PASS + return null; +#else + try + { + java.lang.invoke.MethodHandle mh = DynamicLoadMethodHandleImpl(kind, clazz, name, sig, callerID); + return mh.vmtarget as T + ?? (T)mh.asType(MethodHandleUtil.GetDelegateMethodType(typeof(T))).vmtarget; + } + catch (java.lang.IncompatibleClassChangeError x) + { + if (x.getCause() is java.lang.NoSuchMethodException) + { + throw new java.lang.NoSuchMethodError(x.getCause().Message); + } + if (x.getCause() is java.lang.NoSuchFieldException) + { + throw new java.lang.NoSuchFieldError(x.getCause().Message); + } + if (x.getCause() is java.lang.IllegalAccessException) + { + throw new java.lang.IllegalAccessError(x.getCause().Message); + } + throw; + } +#endif + } + + [DebuggerStepThrough] public static Delegate DynamicCreateDelegate(object obj, Type delegateType, string name, string sig) { TypeWrapper tw = TypeWrapper.FromClass(ikvm.runtime.Util.getClassFromObject(obj)); diff --git a/runtime/compiler.cs b/runtime/compiler.cs index 7a61aede..8b8a30e4 100644 --- a/runtime/compiler.cs +++ b/runtime/compiler.cs @@ -62,9 +62,6 @@ static class ByteCodeHelperMethods internal static readonly MethodInfo DynamicClassLiteral; internal static readonly MethodInfo DynamicGetfield; internal static readonly MethodInfo DynamicGetstatic; - internal static readonly MethodInfo DynamicInvokeSpecialNew; - internal static readonly MethodInfo DynamicInvokestatic; - internal static readonly MethodInfo DynamicInvokevirtual; internal static readonly MethodInfo DynamicMultianewarray; internal static readonly MethodInfo DynamicNewarray; internal static readonly MethodInfo DynamicNewCheckOnly; @@ -73,6 +70,7 @@ static class ByteCodeHelperMethods internal static readonly MethodInfo DynamicCreateDelegate; internal static readonly MethodInfo DynamicLoadMethodType; internal static readonly MethodInfo DynamicLoadMethodHandle; + internal static readonly MethodInfo DynamicBinderMemberLookup; internal static readonly MethodInfo VerboseCastFailure; internal static readonly MethodInfo SkipFinalizer; internal static readonly MethodInfo DynamicInstanceOf; @@ -113,9 +111,6 @@ static class ByteCodeHelperMethods DynamicClassLiteral = typeofByteCodeHelper.GetMethod("DynamicClassLiteral"); DynamicGetfield = typeofByteCodeHelper.GetMethod("DynamicGetfield"); DynamicGetstatic = typeofByteCodeHelper.GetMethod("DynamicGetstatic"); - DynamicInvokeSpecialNew = typeofByteCodeHelper.GetMethod("DynamicInvokeSpecialNew"); - DynamicInvokestatic = typeofByteCodeHelper.GetMethod("DynamicInvokestatic"); - DynamicInvokevirtual = typeofByteCodeHelper.GetMethod("DynamicInvokevirtual"); DynamicMultianewarray = typeofByteCodeHelper.GetMethod("DynamicMultianewarray"); DynamicNewarray = typeofByteCodeHelper.GetMethod("DynamicNewarray"); DynamicNewCheckOnly = typeofByteCodeHelper.GetMethod("DynamicNewCheckOnly"); @@ -124,6 +119,7 @@ static class ByteCodeHelperMethods DynamicCreateDelegate = typeofByteCodeHelper.GetMethod("DynamicCreateDelegate"); DynamicLoadMethodType = typeofByteCodeHelper.GetMethod("DynamicLoadMethodType"); DynamicLoadMethodHandle = typeofByteCodeHelper.GetMethod("DynamicLoadMethodHandle"); + DynamicBinderMemberLookup = typeofByteCodeHelper.GetMethod("DynamicBinderMemberLookup"); VerboseCastFailure = typeofByteCodeHelper.GetMethod("VerboseCastFailure"); SkipFinalizer = typeofByteCodeHelper.GetMethod("SkipFinalizer"); DynamicInstanceOf = typeofByteCodeHelper.GetMethod("DynamicInstanceOf"); @@ -1547,8 +1543,7 @@ sealed class Compiler case NormalizedByteCode.__dynamic_invokestatic: case NormalizedByteCode.__invokestatic: { - ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(instr.Arg1); - MethodWrapper method = GetMethodCallEmitter(cpi, instr.NormalizedOpCode); + MethodWrapper method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); if(method.IsIntrinsic && method.EmitIntrinsic(new EmitIntrinsicContext(method, context, ilGenerator, ma, i, mw, classFile, code, flags))) { break; @@ -1575,7 +1570,7 @@ sealed class Compiler case NormalizedByteCode.__methodhandle_invokeexact: { bool isinvokespecial = instr.NormalizedOpCode == NormalizedByteCode.__invokespecial || instr.NormalizedOpCode == NormalizedByteCode.__dynamic_invokespecial; - MethodWrapper method = GetMethodCallEmitter(classFile.GetMethodref(instr.Arg1), instr.NormalizedOpCode); + MethodWrapper method = GetMethodCallEmitter(instr.NormalizedOpCode, instr.Arg1); int argcount = method.GetParameters().Length; TypeWrapper type = ma.GetRawStackTypeWrapper(i, argcount); TypeWrapper thisType = SigTypeToClassName(type, method.DeclaringType); @@ -1589,6 +1584,7 @@ sealed class Compiler nonleaf = true; + // HACK this code is duplicated in java.lang.invoke.cs if(method.IsProtected && (method.DeclaringType == java_lang_Object || method.DeclaringType == java_lang_Throwable)) { // HACK we may need to redirect finalize or clone from java.lang.Object/Throwable @@ -3098,7 +3094,7 @@ sealed class Compiler ClassFile.ConstantPoolItemMI cpiMI; if (mw == null && (cpiMI = mh.MemberConstantPoolItem as ClassFile.ConstantPoolItemMI) != null) { - mw = new DynamicMethodWrapper(compiler.context, cpiMI, Modifiers.Public | Modifiers.Static); + mw = new DynamicBinder().Get(compiler.context, ClassFile.RefKind.invokeStatic, cpiMI); } if (mw == null || !mw.IsStatic) { @@ -3556,19 +3552,6 @@ sealed class Compiler private void CastInterfaceArgs(TypeWrapper declaringType, TypeWrapper[] args, int instructionIndex, bool instanceMethod) { bool needsCast = false; - bool dynamic; - switch(m.Instructions[instructionIndex].NormalizedOpCode) - { - case NormalizedByteCode.__dynamic_invokeinterface: - case NormalizedByteCode.__dynamic_invokestatic: - case NormalizedByteCode.__dynamic_invokevirtual: - dynamic = true; - break; - default: - dynamic = false; - break; - } - int firstCastArg = -1; if(!needsCast) @@ -3648,13 +3631,24 @@ sealed class Compiler dh.Store(i); } } + if(instanceMethod && args[0].IsUnloadable && !declaringType.IsUnloadable) + { + if(declaringType.IsInterface) + { + ilGenerator.EmitAssertType(declaringType.TypeAsTBD); + } + else + { + ilGenerator.Emit(OpCodes.Castclass, declaringType.TypeAsSignatureType); + } + } for(int i = firstCastArg; i < args.Length; i++) { if(i != firstCastArg) { dh.Load(i); } - if(!args[i].IsUnloadable && args[i].IsGhost && !dynamic) + if(!args[i].IsUnloadable && args[i].IsGhost) { if(i == 0 && instanceMethod && !declaringType.IsInterface) { @@ -3681,7 +3675,7 @@ sealed class Compiler } else { - if(!args[i].IsUnloadable && !dynamic) + if(!args[i].IsUnloadable) { if(args[i].IsNonPrimitiveValueType) { @@ -3864,66 +3858,98 @@ sealed class Compiler } } - private sealed class DynamicMethodWrapper : MethodWrapper + private sealed class DynamicBinder { - private readonly DynamicTypeWrapper.FinishContext context; - private readonly ClassFile.ConstantPoolItemMI cpi; + private MethodWrapper mw; - internal DynamicMethodWrapper(DynamicTypeWrapper.FinishContext context, ClassFile.ConstantPoolItemMI cpi, Modifiers modifiers) - : base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), modifiers, MemberFlags.None) + internal MethodWrapper Get(DynamicTypeWrapper.FinishContext context, ClassFile.RefKind kind, ClassFile.ConstantPoolItemMI cpi) { - this.context = context; - this.cpi = cpi; + return mw ?? (mw = new DynamicBinderMethodWrapper(cpi, Emit(context, kind, cpi), kind)); } - internal override void EmitCall(CodeEmitter ilgen) + private static MethodInfo Emit(DynamicTypeWrapper.FinishContext context, ClassFile.RefKind kind, ClassFile.ConstantPoolItemMI cpi) { - Emit(ByteCodeHelperMethods.DynamicInvokestatic, ilgen, cpi.GetRetType()); + TypeWrapper ret; + TypeWrapper[] args; + if (kind == ClassFile.RefKind.invokeStatic) + { + ret = cpi.GetRetType(); + args = cpi.GetArgTypes(); + } + else if (kind == ClassFile.RefKind.newInvokeSpecial) + { + ret = cpi.GetClassType(); + args = cpi.GetArgTypes(); + } + else + { + ret = cpi.GetRetType(); + args = new TypeWrapper[cpi.GetArgTypes().Length + 1]; + Array.Copy(cpi.GetArgTypes(), 0, args, 1, args.Length - 1); + args[0] = cpi.GetClassType(); + } + Type delegateType = MethodHandleUtil.CreateDelegateType(args, ret); + FieldBuilder fb = context.DefineMethodHandleInvokeCacheField(delegateType); + Type[] types = new Type[args.Length]; + for (int i = 0; i < types.Length; i++) + { + types[i] = args[i].TypeAsSignatureType; + } + MethodBuilder mb = context.DefineMethodHandleDispatchStub(ret.TypeAsSignatureType, types); + CodeEmitter ilgen = CodeEmitter.Create(mb); + ilgen.Emit(OpCodes.Ldsfld, fb); + CodeEmitterLabel label = ilgen.DefineLabel(); + ilgen.EmitBrtrue(label); + ilgen.EmitLdc_I4((int)kind); + ilgen.Emit(OpCodes.Ldstr, cpi.Class); + ilgen.Emit(OpCodes.Ldstr, cpi.Name); + ilgen.Emit(OpCodes.Ldstr, cpi.Signature); + context.EmitCallerID(ilgen); + ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicBinderMemberLookup.MakeGenericMethod(delegateType)); + ilgen.Emit(OpCodes.Volatile); + ilgen.Emit(OpCodes.Stsfld, fb); + ilgen.MarkLabel(label); + ilgen.Emit(OpCodes.Ldsfld, fb); + for (int i = 0; i < args.Length; i++) + { + ilgen.EmitLdarg(i); + } + MethodHandleUtil.EmitCallDelegateInvokeMethod(ilgen, delegateType); + ilgen.Emit(OpCodes.Ret); + ilgen.DoEmit(); + return mb; } - internal override void EmitCallvirt(CodeEmitter ilgen) + private sealed class DynamicBinderMethodWrapper : MethodWrapper { - Emit(ByteCodeHelperMethods.DynamicInvokevirtual, ilgen, cpi.GetRetType()); - } + private readonly MethodInfo method; - internal override void EmitNewobj(CodeEmitter ilgen) - { - Emit(ByteCodeHelperMethods.DynamicInvokeSpecialNew, ilgen, cpi.GetClassType()); - } + internal DynamicBinderMethodWrapper(ClassFile.ConstantPoolItemMI cpi, MethodInfo method, ClassFile.RefKind kind) + : base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), kind == ClassFile.RefKind.invokeStatic ? Modifiers.Public | Modifiers.Static : Modifiers.Public, MemberFlags.None) + { + this.method = method; + } - private void Emit(MethodInfo helperMethod, CodeEmitter ilGenerator, TypeWrapper retTypeWrapper) - { - Profiler.Count("EmitDynamicInvokeEmitter"); - TypeWrapper[] args = cpi.GetArgTypes(); - CodeEmitterLocal argarray = ilGenerator.DeclareLocal(JVM.Import(typeof(object[]))); - CodeEmitterLocal val = ilGenerator.DeclareLocal(Types.Object); - ilGenerator.EmitLdc_I4(args.Length); - ilGenerator.Emit(OpCodes.Newarr, Types.Object); - ilGenerator.Emit(OpCodes.Stloc, argarray); - for(int i = args.Length - 1; i >= 0; i--) + internal override void EmitCall(CodeEmitter ilgen) { - if(args[i].IsPrimitive) - { - ilGenerator.Emit(OpCodes.Box, args[i].TypeAsTBD); - } - ilGenerator.Emit(OpCodes.Stloc, val); - ilGenerator.Emit(OpCodes.Ldloc, argarray); - ilGenerator.EmitLdc_I4(i); - ilGenerator.Emit(OpCodes.Ldloc, val); - ilGenerator.Emit(OpCodes.Stelem_Ref); + ilgen.Emit(OpCodes.Call, method); + } + + internal override void EmitCallvirt(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, method); + } + + internal override void EmitNewobj(CodeEmitter ilgen) + { + ilgen.Emit(OpCodes.Call, method); } - ilGenerator.Emit(OpCodes.Ldstr, cpi.Class); - ilGenerator.Emit(OpCodes.Ldstr, cpi.Name); - ilGenerator.Emit(OpCodes.Ldstr, cpi.Signature); - ilGenerator.Emit(OpCodes.Ldloc, argarray); - context.EmitCallerID(ilGenerator); - ilGenerator.Emit(OpCodes.Call, helperMethod); - EmitReturnTypeConversion(ilGenerator, retTypeWrapper); } } - private MethodWrapper GetMethodCallEmitter(ClassFile.ConstantPoolItemMI cpi, NormalizedByteCode invoke) + private MethodWrapper GetMethodCallEmitter(NormalizedByteCode invoke, int constantPoolIndex) { + ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(constantPoolIndex); #if STATIC_COMPILER if(replacedMethodWrappers != null) { @@ -3954,10 +3980,13 @@ sealed class Compiler mw = cpi.GetMethod(); break; case NormalizedByteCode.__dynamic_invokeinterface: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeInterface, cpi); case NormalizedByteCode.__dynamic_invokestatic: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeStatic, cpi); case NormalizedByteCode.__dynamic_invokevirtual: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeVirtual, cpi); case NormalizedByteCode.__dynamic_invokespecial: - return new DynamicMethodWrapper(context, cpi, Modifiers.Public); + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.newInvokeSpecial, cpi); case NormalizedByteCode.__methodhandle_invoke: return new MethodHandleMethodWrapper(context, clazz, cpi, false); case NormalizedByteCode.__methodhandle_invokeexact: @@ -3967,7 +3996,19 @@ sealed class Compiler } if(mw.IsDynamicOnly) { - return new DynamicMethodWrapper(context, cpi, mw.Modifiers); + switch (invoke) + { + case NormalizedByteCode.__invokespecial: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeSpecial, cpi); + case NormalizedByteCode.__invokeinterface: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeInterface, cpi); + case NormalizedByteCode.__invokestatic: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeStatic, cpi); + case NormalizedByteCode.__invokevirtual: + return context.GetValue<DynamicBinder>(constantPoolIndex).Get(context, ClassFile.RefKind.invokeVirtual, cpi); + default: + throw new InvalidOperationException(); + } } return mw; } |