public static void LoadFunction(JavaCode code, Mono.Cecil.Cil.Instruction cilInst) { if (cilInst.Operand is MethodReference implMethodRef) { var implMethod = CilMethod.From(implMethodRef); var(declType, declMethod, dlgMethodName) = FindInterfaceType(cilInst); if (dlgMethodName != null) { // if this is an artificial delegate for a functional interface, // then we can't actually instantiate this particular delegate, // because its definition only exists in the DLL created by // DotNetImporter; see BuildDelegate there. we fix the Newobj // instruction to instantiate system.FunctionalInterfaceDelegate. cilInst.Next.Operand = DelegateConstructor(cilInst.Next.Operand); declMethod = new JavaMethodRef(dlgMethodName, implMethod.ReturnType, implMethod.Parameters); } if (IsPrimitive(declMethod.ReturnType)) { // primitive return value is translated to java.lang.Object, // because the delegate target may return a generic type that // is specialized for the primitive type in the delegate. // see also MakeInterface and GenerateInvoke declMethod = new JavaMethodRef( declMethod.Name, JavaType.ObjectType, declMethod.Parameters); } // select the method handle kind for the dynamic call site. // for a non-static invocation, we need to push an object reference. JavaMethodHandle.HandleKind callKind; if (implMethod.DeclType.IsInterface) { callKind = JavaMethodHandle.HandleKind.InvokeInterface; } else { callKind = JavaMethodHandle.HandleKind.InvokeVirtual; } if (cilInst.OpCode.Code == Code.Ldftn) { if (implMethod.IsStatic) { callKind = JavaMethodHandle.HandleKind.InvokeStatic; } else { var implMethodDef = CilMethod.AsDefinition(implMethodRef); bool isVirtual = implMethodDef.IsVirtual; if (isVirtual && implMethodDef.DeclaringType.IsSealed) { isVirtual = false; } if (!isVirtual) { // non-virtual instance method code.NewInstruction(0x59 /* dup */, null, null); code.StackMap.PushStack(JavaType.ObjectType); } else { // virtual method should only be specified in 'ldvirtftn' throw new Exception("virtual method referenced"); } } } // // the method may take generic type parameters, i.e. the System.Type // parameters that are added at the end of the parameter list, by // CilMethod::ImportGenericParameters. but when the delegate is // called, these parameters will not be pushed. therefore we do // the following when assigning such a method to a delegate: // // (1) we select a different implementing method, i.e. the bridge // method generated by CreateCapturingBridgeMethod (see below), // which moves the type arguments to the head of the parameter list. // (2) we generate and push the type arguments at this time, via // calls to GenericUtil.LoadGeneric. // (3) we specify the type arguments as capture parameters of the // call site. (see also JavaCallSite.) this means the generated // proxy will inject these parameters when it calls the bridge // method from step (1), and that bridge method will push these // parameters at the end of the parameter list, when it invokes // the actual target method. // List <JavaFieldRef> TypeArgs = null; JavaMethodRef implMethod2 = implMethod; var implGenericMethod = implMethod.WithGenericParameters; if (implGenericMethod != implMethod) { implMethod2 = new JavaMethodRef( "delegate-bridge-" + implGenericMethod.Name, implMethod.ReturnType, implMethod.Parameters); var count1 = implMethod.Parameters.Count; var count2 = implGenericMethod.Parameters.Count; TypeArgs = implGenericMethod.Parameters.GetRange(count1, count2 - count1); var callMethod = CilMain.GenericStack.EnterMethod(implMethodRef); for (int i = count1; i < count2; i++) { GenericUtil.LoadGeneric(implGenericMethod.Parameters[i].Name, code); } for (int i = count1; i < count2; i++) { code.StackMap.PopStack(CilMain.Where); } } // create a CallSite that implements the method signature // declMethod, in the functional interface declType, in order // to proxy-invoke the method implMethod.DeclType::implMethod. var callSite = new JavaCallSite(declType, declMethod, implMethod.DeclType, implMethod2, TypeArgs, callKind); code.NewInstruction(0xBA /* invokedynamic */, null, callSite); if (callKind != JavaMethodHandle.HandleKind.InvokeStatic) { code.StackMap.PopStack(CilMain.Where); } code.StackMap.PushStack(CilType.From(declType)); } else { throw new InvalidProgramException(); } // // ldftn or ldvirtftn should be followed by newobj, which we can use // to identify the delegate, and therefore, the interface // (JavaType, JavaMethodRef, string) FindInterfaceType( Mono.Cecil.Cil.Instruction cilInst) { cilInst = cilInst.Next; if (cilInst != null && cilInst.OpCode.Code == Code.Newobj && cilInst.Operand is MethodReference constructorRef) { var dlgType = CilType.From(constructorRef.DeclaringType); if (dlgType.IsDelegate) { // // check for an artificial delegate, generated to represent a // java functional interface: (BuildDelegate in DotNetImporter) // // delegate type is marked [java.lang.attr.AsInterface], // and is child of an interface type. // interface type is marked [java.lang.attr.RetainName], // and has one method. // var dlgType0 = CilType.AsDefinition(constructorRef.DeclaringType); if (dlgType0.HasCustomAttribute("AsInterface")) { var ifcType0 = dlgType0.DeclaringType; var ifcType = CilType.From(ifcType0); if (ifcType.IsInterface && ifcType.IsRetainName && ifcType0.HasMethods && ifcType0.Methods.Count == 1) { return(ifcType, null, ifcType0.Methods[0].Name); } } // // otherwise, a normal delegate, which may be generic, or plain. // interface name is DelegateType$interface. // we look for a method named Invoke. // foreach (var method in dlgType0.Methods) { if (method.Name == "Invoke") { return(InterfaceType(dlgType), CilMethod.From(method), null); } } } } throw new InvalidProgramException(); } // // create a method reference to delegate constructor from baselib: // system.MulticastDelegate::.ctor(object, object) // CilMethod DelegateConstructor(object Operand) { var baseType = CilType.AsDefinition(((MethodReference)Operand) .DeclaringType).BaseType; if (baseType.Namespace != "System" || baseType.Name != "MulticastDelegate") { throw CilMain.Where.Exception( $"delegate base type is '{baseType.Name}', " + "but expected 'System.MulticastDelegate'"); } return(CilMethod.CreateDelegateConstructor()); } }