public static string GenericParameterFullName(GenericParameter genericParameter) { string name = genericParameter.Name; string prefix; if (genericParameter.Type == GenericParameterType.Method) { prefix = "M!" + genericParameter.DeclaringMethod.FullName; if (name.StartsWith("!!")) { name = CilMethod.AsDefinition(genericParameter.DeclaringMethod) .GenericParameters[genericParameter.Position].Name; } else if (name.StartsWith("!")) { throw new ArgumentException(); } } else { prefix = "T!" + genericParameter.DeclaringType.FullName; if (name.StartsWith("!")) { name = CilType.AsDefinition(genericParameter.DeclaringType) .GenericParameters[genericParameter.Position].Name; } } return(prefix + "<" + name + ">"); }
static int GenericParameterPosition(MethodDefinition defMethod, int parameterIndex, GenericParameter parameter) { // when a method has parameters with a generic type, the // method name gets a suffix like -generic-$n, where n is // the index of the generic parameter in the generic type. // e.g., method "int Mth(TY)" in class CA<TX,TY> would be // named "Mth-generic-$1" because TY has generic index 1. // // however when the method overrides a base class method, // the n should be the index of the generic parameter in // the base type. e.g., "override int Mth(UZ)" in class // CB<UX,UY,UZ> : CA<UY,UZ> translated to "Mth-generic-$2" // (as UZ has generic index 2 in CB) would be incorrect, // because it breaks the overload/override chain. // // the code below identifies that CB.Mth is an override // of a generic base type method, and that CB.UZ maps to // CA.TY in the base Mth(), and prefers the index of CA.TY // (i.e. 1) over CB.UZ (i.e. 2), to correctly translate // the override as "Mth-generic-$1". if (parameter.Type == GenericParameterType.Type && defMethod != null && defMethod.IsVirtual && (!defMethod.IsNewSlot)) { var thisClass = defMethod.DeclaringType; if (thisClass.BaseType is GenericInstanceType baseClass) { var baseGenericArgs = baseClass.GenericArguments; foreach (var baseMethod in CilType.AsDefinition(baseClass).Methods) { if (baseMethod.IsVirtual && CompareMethods(defMethod, baseMethod)) { // we found a base class method that matches // the overriding method, so take the position // (i.e. the generic parameter index within // generic type) from the base method parameter if (baseMethod.Parameters[parameterIndex].ParameterType .GetElementType() is GenericParameter baseParameter && baseGenericArgs.Count > baseParameter.Position && baseGenericArgs[baseParameter.Position] == parameter) { return(baseParameter.Position); } } } } } // in any other case, for example if this is not an // overriding method, or if the base type is not generic, // then select the position within the current type return(parameter.Position); }
public static List <CilInterfaceMethod> CollectAll(TypeDefinition fromType) { var map = new Dictionary <string, List <CilInterfaceMethod> >(); Process(fromType, map); var newList = new List <CilInterfaceMethod>(); foreach (var oldList in map.Values) { newList.AddRange(oldList); } return(newList); void Process(TypeDefinition fromType, Dictionary <string, List <CilInterfaceMethod> > map) { foreach (var fromMethod in fromType.Methods) { if ((fromMethod.IsPublic || fromMethod.HasOverrides) && !(fromMethod.IsStatic || fromMethod.IsConstructor)) { if (fromType.IsInterface && fromMethod.HasBody) { // skip default interface methods, they are not needed // in the context of resolving interface implementations continue; } if (fromMethod.HasCustomAttribute("Discard")) { continue; // skip if decorated with [java.attr.Discard] } var genericMark = CilMain.GenericStack.Mark(); var inputMethod = CilMain.GenericStack.EnterMethod(fromMethod); var outputMethod = new CilInterfaceMethod(inputMethod); if (map.TryGetValue(inputMethod.Name, out var list)) { bool dup = false; foreach (var oldMethod in list) { if (oldMethod.EqualParameters(outputMethod)) { dup = true; break; } } if (!dup) { list.Add(outputMethod); } } else { var list2 = new List <CilInterfaceMethod>(); list2.Add(outputMethod); map.Add(inputMethod.Name, list2); } CilMain.GenericStack.Release(genericMark); } } var fromBaseTypeRef = fromType.BaseType; if (fromBaseTypeRef != null && !fromType.IsInterface) { var fromBaseTypeDef = CilType.AsDefinition(fromBaseTypeRef); var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterType(fromBaseTypeDef); Process(fromBaseTypeDef, map); CilMain.GenericStack.Release(genericMark); } } }
public static List <CilInterface> CollectAll(TypeDefinition fromType) { var list = new List <CilInterface>(); if (fromType.HasInterfaces) { Process(fromType, list, true, false); } return(list); void Process(TypeDefinition fromType, List <CilInterface> list, bool directReference, bool markSuperImplements) { if (fromType.HasInterfaces) { foreach (var ifcIterator in fromType.Interfaces) { var fromInterfaceRef = ifcIterator.InterfaceType; var fromInterfaceDef = CilType.AsDefinition(fromInterfaceRef); var genericMark = CilMain.GenericStack.Mark(); var interfaceType = CilMain.GenericStack.EnterType(fromInterfaceRef); var myInterface = interfaceType.HasGenericParameters ? ImportGenericInterface(interfaceType, list) : ImportPlainInterface(interfaceType, list); if (!myInterface.Inserted) { // interface not inserted yet, which means it was just created myInterface.Methods = CilInterfaceMethod.CollectAll(fromInterfaceDef); myInterface.LoadType(fromInterfaceRef); myInterface.DirectReference = directReference; myInterface.SuperImplements = markSuperImplements; myInterface.Inserted = true; list.Add(myInterface); Process(fromInterfaceDef, list, false, markSuperImplements); } else if (markSuperImplements) { // an interface that was found for the initial type, // was also found in a base type, mark it so myInterface.SuperImplements = true; } CilMain.GenericStack.Release(genericMark); } } // // scan base types to detect interfaces implemented there // var fromBaseTypeRef = fromType.BaseType; if (fromBaseTypeRef != null && !fromType.IsInterface) { var fromBaseTypeDef = CilType.AsDefinition(fromBaseTypeRef); var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterType(fromBaseTypeDef); Process(fromBaseTypeDef, list, false, true); CilMain.GenericStack.Release(genericMark); } } }
public static List <CilInterfaceMethod> CollectAll(TypeDefinition fromType) { var list = new List <CilInterfaceMethod>(); Process(fromType, list); return(list); void Process(TypeDefinition fromType, List <CilInterfaceMethod> list) { foreach (var fromMethod in fromType.Methods) { if ((fromMethod.IsPublic || fromMethod.HasOverrides) && !(fromMethod.IsStatic || fromMethod.IsConstructor)) { if (fromType.IsInterface && fromMethod.HasBody) { // skip default interface methods, they are not needed // in the context of resolving interface implementations continue; } var genericMark = CilMain.GenericStack.Mark(); var inputMethod = CilMain.GenericStack.EnterMethod(fromMethod); var outputMethod = new CilInterfaceMethod(inputMethod); bool dup = false; foreach (var oldMethod in list) { if (oldMethod.Method.Name == outputMethod.Method.Name && oldMethod.EqualParameters(outputMethod)) { dup = true; break; } } if (!dup) { list.Add(outputMethod); } CilMain.GenericStack.Release(genericMark); } } var fromBaseTypeRef = fromType.BaseType; if (fromBaseTypeRef != null && !fromType.IsInterface) { var fromBaseTypeDef = CilType.AsDefinition(fromBaseTypeRef); var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterType(fromBaseTypeDef); Process(fromBaseTypeDef, list); CilMain.GenericStack.Release(genericMark); } } }
public static void BuildOverloadProxy(TypeDefinition fromType, MethodDefinition fromMethod, CilMethod targetMethod, JavaClass intoClass) { // create a proxy bridge to forward invocations of a virtual method from // the base class, to an implementation in a derived class. this is needed // where the base virtual method has generic parameters or return type, // for example: virtual T SomeMethod(T arg) will be translated as: // java.lang.Object SomeMethod(-generic-$)(java.lang.Object arg) // and in a derived class that specializes T as String: // java.lang.String SomeMethod(java.lang.String arg) // so the derived class must also include a proxy bridge method with the // same name as in the base class, and forward the call. //Console.WriteLine($"CHECK OVERRIDE {fromMethod}"); int targetMethodCount = targetMethod.Parameters.Count; for (;;) { var baseType = fromType.BaseType; if (baseType == null) { break; } fromType = CilType.AsDefinition(baseType); if (!baseType.IsGenericInstance) { continue; } foreach (var fromMethod2 in fromType.Methods) { //Console.WriteLine($"\tCOMPARE {fromMethod2} {fromMethod2.IsVirtual} {fromMethod2.Name == fromMethod.Name} {targetMethodCount == fromMethod2.Parameters.Count}"); if (fromMethod2.IsVirtual && fromMethod2.Name == fromMethod.Name && targetMethodCount == fromMethod2.Parameters.Count) { if (CilMethod.CompareMethods(fromMethod, fromMethod2)) { //Console.WriteLine(">>>>>>>>>>>>> WARNING SAME: " + fromMethod.ToString() + " AND " + fromType); continue; } var genericMark = CilMain.GenericStack.Mark(); CilMain.GenericStack.EnterType(baseType); var baseMethod = new CilInterfaceMethod( CilMain.GenericStack.EnterMethod(fromMethod2)); CilMain.GenericStack.Release(genericMark); //Console.WriteLine($"\twould compare with {fromMethod2} in class {baseType}"); //Console.WriteLine($"\t\t{baseMethod}"); if (targetMethodCount != baseMethod.Parameters.Count) { continue; } if (!IsGenericOrEqual(targetMethod.ReturnType, baseMethod.ReturnType)) { continue; } bool sameParameters = true; for (int i = 0; i < targetMethodCount; i++) { if (!IsGenericOrEqual(targetMethod.Parameters[i].Type, baseMethod.Parameters[i])) { bool equalsAfterUnboxing = ( targetMethod.Parameters[i].Type is BoxedType boxedType && boxedType.UnboxedType.Equals(baseMethod.Parameters[i])); if (!equalsAfterUnboxing) { //Console.WriteLine($"\tMISMATCH {targetMethod.Parameters[i].Type} vs {baseMethod.Parameters[i]}/{baseMethod.Parameters[i].IsGenericParameter}"); sameParameters = false; break; } } } if (sameParameters) { //Console.WriteLine($"proxying {targetMethod} in class {targetMethod.DeclType}"); BuildGenericProxy2(baseMethod, targetMethod, false, targetMethod.DeclType, intoClass); return; } } } }
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()); } }