public static CilInterfaceMethod FindMethod(List <CilInterfaceMethod> haystack, CilInterfaceMethod needle) { int n = needle.Parameters.Count; foreach (var current in haystack) { if (current.PlainCompare(needle)) { return(current); } } return(null); }
public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, string methodSuffix, CilType intoType, List <CilInterfaceMethod> classMethods, JavaClass ifcClass) { CilMethod targetMethod = null; //Console.WriteLine("\n***** LOOKING FOR INTERFACE METHOD " + ifcMethod + " (SUFFIX " + methodSuffix + ") AKA " + ifcMethod.Method); foreach (var clsMethod in classMethods) { /*Console.WriteLine("> LOOKING AT " + (clsMethod.Method.IsExplicitImpl ? "EXPLICIT " : "") + "CLASS METHOD " + clsMethod + " AKA " + clsMethod.Method);*/ if (ifcMethod.GenericCompare(clsMethod)) { if (clsMethod.Method.IsExplicitImpl) { // a matching explicit override method is the best match, // and we can immediately stop searching targetMethod = clsMethod.Method; break; } else { // more than one method may match, if a derived type overrides // or hides a method that also exists in a base type. but the // derived (primary) type methods always come first. if (targetMethod == null) { targetMethod = clsMethod.Method; } } } } if (targetMethod == null) { throw CilMain.Where.Exception( $"missing method '{ifcMethod.Method}' " + $"(for interface '{ifcMethod.Method.DeclType}')"); } // Console.WriteLine("INTERFACE METHOD " + ifcMethod.Method + " TARGET " + targetMethod); BuildGenericProxy2(ifcMethod, targetMethod, true, intoType, ifcClass); }
public bool EqualGenericParameters(CilInterfaceMethod other) { if (!EqualGenericParameter(ReturnType, other.ReturnType)) { return(false); } int n = Parameters.Count; if (other.Parameters.Count != n) { return(false); } for (int i = 0; i < n; i++) { if (!EqualGenericParameter(Parameters[i], other.Parameters[i])) { return(false); } } return(true); }
public bool EqualParameters(CilInterfaceMethod other) { if (!ReturnType.Equals(other.ReturnType)) { return(false); } int n = Parameters.Count; if (other.Parameters.Count != n) { return(false); } for (int i = 0; i < n; i++) { if (!Parameters[i].Equals(other.Parameters[i])) { return(false); } } return(true); }
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 bool GenericCompare(CilInterfaceMethod other) => SimpleName == other.SimpleName && EqualGenericParameters(other);
public bool PlainCompare(CilInterfaceMethod other) => SimpleName == other.SimpleName && EqualParameters(other);
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 BuildGenericProxy2(CilInterfaceMethod ifcMethod, CilMethod targetMethod, bool parentField, CilType intoType, JavaClass ifcClass) { // // create proxy method // var targetMethod2 = targetMethod.WithGenericParameters; var ifcMethod2 = ifcMethod.Method.WithGenericParameters; var newMethod = new JavaMethod(ifcClass, targetMethod2); newMethod.Name = ifcMethod2.Name; newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE; var code = newMethod.Code = new JavaCode(); code.Method = newMethod; code.Instructions = new List <JavaCode.Instruction>(); // // push a reference to the parent object // code.NewInstruction(0x19 /* aload */, null, (int)0); if (parentField) { code.NewInstruction(0xB4 /* getfield */, new JavaType(0, 0, ifcClass.Name), new JavaFieldRef(ParentFieldName, intoType)); } // // push all other parameters // int numArgs = newMethod.Parameters.Count; int index = 1; int maxStack = 1; for (int i = 0; i < numArgs; i++) { var ifcArg = ifcMethod2.Parameters[i].Type; code.NewInstruction(ifcArg.LoadOpcode, null, (int)index); index += ifcArg.Category; var clsArg = (CilType)targetMethod2.Parameters[i].Type; if (JavaType.ObjectType.Equals(ifcArg)) { if (!clsArg.IsReference) { var boxedArg = new BoxedType(clsArg, false); code.NewInstruction(0xC0 /* checkcast */, boxedArg, null); boxedArg.GetValue(code); } else if (!JavaType.ObjectType.Equals(clsArg)) { code.NewInstruction(0xC0 /* checkcast */, clsArg, null); } // a parameter in the target method may be a concrete type, // but if it is a generic java.lang.Object in the interface, // then it must be a generic java.lang.Object in the proxy newMethod.Parameters[i] = new JavaFieldRef("", ifcArg); } maxStack += clsArg.Category; } // // invoke proxy target method // code.NewInstruction(0xB6 /* invokevirtual */, intoType, targetMethod2); // // return value from method // var clsRet = (CilType)targetMethod2.ReturnType; var ifcRet = ifcMethod2.ReturnType; if (JavaType.ObjectType.Equals(ifcRet)) { if (!clsRet.IsReference) { var boxedArg = new BoxedType(clsRet, false); boxedArg.BoxValue(code); } // the return value in the target method may be a concrete type, // but if it is a generic java.lang.Object in the interface, // then it must also be a generic java.lang.Object in the proxy newMethod.ReturnType = ifcRet; code.NewInstruction(ifcRet.ReturnOpcode, null, null); } else { code.NewInstruction(clsRet.ReturnOpcode, null, null); } code.MaxLocals = index; code.MaxStack = maxStack; ifcClass.Methods.Add(newMethod); }
public static JavaMethod BuildPlainProxy(CilInterfaceMethod ifcMethod, CilType intoType, List <CilInterfaceMethod> classMethods) { CilMethod targetMethod = null; foreach (var clsMethod in classMethods) { if (clsMethod.Method.IsExplicitImpl) { // no need for a proxy if we already have an override method, // which has the same name as the proxy: interface$method if (ifcMethod.Method.Name == clsMethod.Method.Name) { return(null); } } else if (ifcMethod.PlainCompare(clsMethod)) { // more than one method may match, if a derived type overrides // or hides a method that also exists in a base type. but the // derived (primary) type methods always come first. if (targetMethod == null) { targetMethod = clsMethod.Method; } } } if (targetMethod == null) { throw CilMain.Where.Exception( $"missing method '{ifcMethod.Method}' " + $"(for interface '{ifcMethod.Method.DeclType}')"); } if (targetMethod.IsRetainName) { // method retains is name, so it doesn't require a proxy bridge return(null); } // // create proxy method // var newMethod = new JavaMethod(null, targetMethod); newMethod.Name = ifcMethod.Method.Name; newMethod.Flags = JavaAccessFlags.ACC_PUBLIC | JavaAccessFlags.ACC_BRIDGE; var code = newMethod.Code = new JavaCode(); code.Method = newMethod; code.Instructions = new List <JavaCode.Instruction>(); // // push 'this' and all other parameters // code.NewInstruction(0x19 /* aload */, null, (int)0); int numArgs = newMethod.Parameters.Count; int index = 1; for (int i = 0; i < numArgs; i++) { var arg = targetMethod.Parameters[i].Type; code.NewInstruction(arg.LoadOpcode, null, (int)index); index += arg.Category; } // // invoke proxy target method and return // code.NewInstruction(0xB6 /* invokevirtual */, intoType, targetMethod); code.NewInstruction(targetMethod.ReturnType.ReturnOpcode, null, null); code.MaxLocals = code.MaxStack = index; return(newMethod); }
public static List <JavaClass> BuildProxyMethods(List <CilInterface> allInterfaces, TypeDefinition fromType, CilType intoType, JavaClass theClass) { // // process only if the class (or interface) has any methods or super interfaces // var classMethods = theClass.Methods; if (classMethods.Count == 0) { return(null); } bool isInterface = intoType.IsInterface; if ((!isInterface) && theClass.Interfaces == null) { return(null); } var theMethods = CilInterfaceMethod.CollectAll(fromType); // // if any interfaces are marked [RetainName], make sure that // all corresponding methods are also marked [RetainName] // CheckRetainNameMethods(theMethods, allInterfaces, intoType); // // if this is an abstract class but forced to an interface via [AddInterface] // decoration, then we need to remove all constructors generated for the class // if (intoType.IsInterface) { if (!fromType.IsInterface) { for (int i = classMethods.Count; i-- > 0;) { if (classMethods[i].Name == "<init>") { classMethods.RemoveAt(i); } } } if (intoType.HasGenericParameters) { // the RuntimeType constructor in baselib uses IGenericEntity // marker interface to identify generic classes. note that // real generic types implement IGenericObject -> IGenericEntity. theClass.AddInterface("system.IGenericEntity"); } return(null); } // // for each implemented interface, build proxy methods // List <JavaClass> output = null; int ifcNumber = 0; foreach (var ifc in allInterfaces) { if ((!ifc.DirectReference) && ifc.SuperImplements) { // we don't have to build proxy for an interface if it is // implemented by a super type and not by our primary type continue; } if (ifc.GenericTypes == null) { foreach (var ifcMethod in ifc.Methods) { // build proxy methods: interface$method -> method var newMethod = BuildPlainProxy(ifcMethod, intoType, theMethods); if (newMethod != null) { newMethod.Class = theClass; theClass.Methods.Add(newMethod); } } } else { var ifcClass = CreateInnerClass(theClass, intoType, ++ifcNumber); ifcClass.AddInterface(ifc.InterfaceType.JavaName); if (output == null) { output = new List <JavaClass>(); CreateInterfaceArrayField(theClass); } output.Add(ifcClass); // if the class implements a generic interface for multiple types, // then we need a method suffix to differentiate between the methods. // see also: CilMethod::InsertMethodNamePrefix string methodSuffix = ""; foreach (var genericType in ifc.GenericTypes) { methodSuffix += "--" + CilMethod.GenericParameterSuffixName(genericType); } foreach (var ifcMethod in ifc.Methods) { // build proxy classes: proxy sub-class -> this class BuildGenericProxy(ifcMethod, methodSuffix, intoType, theMethods, ifcClass); } } } return(output); JavaClass CreateInnerClass(JavaClass parentClass, CilType parentType, int ifcNumber) { // generic interfaces are implemented as proxy sub-classes which // call methods on the parent class object. we need to define // an inner class. this class has one instance field which is a // reference to the parent class. the constructor takes this // reference as a parameter and initializes the instance field. var newClass = CilMain.CreateInnerClass(parentClass, parentClass.Name + "$$generic" + ifcNumber.ToString()); var fld = new JavaField(); fld.Name = ParentFieldName; fld.Type = parentType; fld.Class = newClass; fld.Flags = JavaAccessFlags.ACC_PRIVATE; newClass.Fields.Add(fld); var code = CilMain.CreateHelperMethod(newClass, new JavaMethodRef("<init>", JavaType.VoidType, JavaType.ObjectType), 2, 2); code.Method.Flags &= ~JavaAccessFlags.ACC_BRIDGE; // invalid for constructor code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0xB7 /* invokespecial */, JavaType.ObjectType, new JavaMethodRef("<init>", JavaType.VoidType)); code.NewInstruction(0x19 /* aload */, null, (int)0); code.NewInstruction(0x19 /* aload */, null, (int)1); code.NewInstruction(0xC0 /* checkcast */, parentType, null); code.NewInstruction(0xB5 /* putfield */, new JavaType(0, 0, newClass.Name), new JavaFieldRef(ParentFieldName, parentType)); code.NewInstruction(JavaType.VoidType.ReturnOpcode, null, null); return(newClass); } void CreateInterfaceArrayField(JavaClass parentClass) { // the parent class has a helper array field that is used to track // the proxy objects generated for implemented generic interfaces. // see also: InitInterfaceArrayField, below. var fld = new JavaField(); fld.Name = InterfaceArrayField.Name; fld.Type = InterfaceArrayField.Type; fld.Class = parentClass; fld.Flags = JavaAccessFlags.ACC_PRIVATE; if (parentClass.Fields == null) { parentClass.Fields = new List <JavaField>(1); } parentClass.Fields.Add(fld); } }
// // if any interfaces are marked [RetainName], make sure that // all corresponding methods are also marked [RetainName] // static void CheckRetainNameMethods(List <CilInterfaceMethod> theMethods, List <CilInterface> theInterfaces, CilType checkedType) { List <CilInterface> retainNameInterfaces = null; foreach (var myInterface in theInterfaces) { if (myInterface.InterfaceType.IsRetainName) { if (retainNameInterfaces == null) { retainNameInterfaces = new List <CilInterface>(); } retainNameInterfaces.Add(myInterface); } } if (retainNameInterfaces == null) { return; } foreach (var myMethod in theMethods) { // if the method was not declared on the type itself, skip it. if (myMethod.Method.DeclType != checkedType) { continue; } // methods may be marked with [RetainName] to avoid shadow renaming // (via CilMethod::MethodIsShadowing), as well as to match methods // from [RetainName] interfaces. so if the method is marked so, // then we can just skip it. if (myMethod.Method.IsRetainName) { continue; } // if the method is an explicit method implementation, then it cannot // implement a method from a [RetainName] interface (checked in // CilMethod::InsertMethodNamePrefix) and cannot be marked [RetainName] // (checked in CilMethod::SetMethodType). if (myMethod.Method.IsExplicitImpl) { continue; } // if the method is not marked [RetainName], then make sure it is not // overriding an interface method marked [RetainName] var(foundInterface, foundMethod) = FindMethod(retainNameInterfaces, myMethod); if (foundMethod != null) { var interfaceName = foundInterface.InterfaceType.JavaName; throw CilMain.Where.Exception( $"method '{myMethod}' (for interface '{interfaceName}') " + $"should be decorated with [java.attr.RetainName]"); } } (CilInterface, CilInterfaceMethod) FindMethod(List <CilInterface> haystack, CilInterfaceMethod needle) { foreach (var ifc in haystack) { var mth = CilInterfaceMethod.FindMethod(ifc.Methods, needle); if (mth != null) { return(ifc, mth); } } return(null, null); } }
public static void BuildGenericProxy(CilInterfaceMethod ifcMethod, /*string methodSuffix,*/ CilType intoType, List <CilInterfaceMethod> classMethods, JavaClass ifcClass) { CilMethod targetMethod = null; //Console.WriteLine("\n***** LOOKING FOR INTERFACE METHOD " + ifcMethod + " (SUFFIX " + methodSuffix + ") AKA " + ifcMethod.Method); foreach (var clsMethod in classMethods) { /*Console.WriteLine("> LOOKING AT " + (clsMethod.Method.IsExplicitImpl ? "EXPLICIT " : "") + "CLASS METHOD " + clsMethod + " AKA " + clsMethod.Method);*/ if (ifcMethod.GenericCompare(clsMethod)) { if (clsMethod.Method.IsExplicitImpl) { // a matching explicit override method is the best match, // and we can immediately stop searching targetMethod = clsMethod.Method; break; } else { // more than one method may match, if a derived type overrides // or hides a method that also exists in a base type. but the // derived (primary) type methods always come first if (targetMethod == null) { targetMethod = clsMethod.Method; } // if a second method matches, and the set of generic types // in its signature exactly matches the interface method we // are looking for, then prefer this method. when a class // implements same-name methods from multiple interfaces, // this is needed to pick the right method. // see also ResolvedGenericTypes in CilInterfaceMethod. else if (clsMethod.ResolvedGenericTypes.Length != 0 && clsMethod.ResolvedGenericTypes == ifcMethod.ResolvedGenericTypes) { targetMethod = clsMethod.Method; } } } } if (targetMethod == null) { throw CilMain.Where.Exception( $"missing method '{ifcMethod.Method}' " + $"(for interface '{ifcMethod.Method.DeclType}')"); } // Console.WriteLine("INTERFACE METHOD " + ifcMethod.Method + " TARGET " + targetMethod); BuildGenericProxy2(ifcMethod, targetMethod, true, intoType, ifcClass); }