/// <summary> /// Update the return type (if needed) /// </summary> private void FixReturnType(TargetFramework target) { var declaringType = method.DeclaringType; if ((declaringType == null) || method.IsSignConverted || (!method.NeedsOverrideKeyword)) { return; } var returnType = method.ReturnType; if ((returnType is NetGenericParameter) && ((NetGenericParameter)returnType).Owner == method) { return; } var baseMethod = method.Overrides.First(x => !x.DeclaringType.IsInterface); var expectedRetType = GenericParameters.Resolve(baseMethod.ReturnType, declaringType, target.TypeNameMap); var retType = GenericParameters.Resolve(method.ReturnType, declaringType, target.TypeNameMap); if (!retType.AreSame(expectedRetType)) { if (baseMethod.IsAbstract) { method.ReturnType = expectedRetType; } else { method.RequireNewSlot(); } } }
/// <summary> /// Add abstract method (that do not yet exist) for all interface methods. /// </summary> private void AddAbstractInterfaceMethods(NetTypeDefinition typeDef, TargetFramework target) { if ((typeDef.IsInterface) || (this is IInterfaceConstantsTypeBuilder)) { return; } var typeMethods = typeDef.Methods.ToList(); foreach (var iterator in typeDef.Interfaces) { // Collect all interface methods var iDef = iterator; var iMethods = iDef.GetElementType().Methods.Select(x => Tuple.Create(iDef, x)).ToList(); iMethods.AddRange(iDef.GetElementType().GetBaseTypes(true).Where(x => x.GetElementType().IsInterface).SelectMany(x => x.GetElementType().Methods.Select(m => Tuple.Create(x, m)))); foreach (var pair in iMethods) { var iMethod = pair.Item2; if (typeMethods.Any(x => x.IsExactDuplicate(iMethod))) { continue; } // Add abstract method var method = new NetMethodDefinition(iMethod.Name, null, typeDef, target, iMethod.SignConvertMode, "TypeBuilder.AddAbstractInterfaceMethods"); method.AccessFlags = iMethod.AccessFlags; method.Attributes = iMethod.Attributes; method.IsAbstract = false; method.IsVirtual = !typeDef.IsSealed; method.GenericParameters.AddRange(iMethod.GenericParameters.Select(x => new NetGenericParameter(GetMethodGenericParameterName(typeDef, x.Name), x.Name, method))); method.ReturnType = GenericParameters.Resolve(iMethod.ReturnType, typeDef, target.TypeNameMap); method.Parameters.AddRange(iMethod.Parameters.Select(x => new NetParameterDefinition(x.Name, GenericParameters.Resolve(x.ParameterType, typeDef, target.TypeNameMap), x.IsParams))); // Check if there are duplicates now that the generic parameters are resolved. if (typeMethods.Any(x => x.IsExactDuplicate(method))) { continue; } // Should we ignore this one? if (IgnoreInterfaceMethod(method, iMethod)) { continue; } // Test if the method is a duplicate of an existing method, but with different return type var differentAccessMask = !method.HasSameVisibility(iMethod); if (differentAccessMask || typeMethods.Any(x => x.IsDuplicate(method)) || ImplementAsExplicitInterfaceMethod(method, iMethod) || typeDef.NestedTypes.Any(x => x.Name == method.Name)) { // Method with different return type exists. // Make it an explicit interface method method.SetExplicitImplementation(iMethod, pair.Item1); method.IsAbstract = false; method.IsVirtual = false; } else { // Check for override //method.IsOverride = isOverride; if (method.IsOverride && method.Overrides.All(x => !x.IsVirtual)) { // No need to override anything continue; } } // Create DexImport attribute var typeCa = iMethod.DeclaringType.CustomAttributes.First(x => x.AttributeType == null); var methodCa = iMethod.CustomAttributes.First(x => x.AttributeType == null); var ca = new NetCustomAttribute(null, typeCa.ConstructorArguments[0], methodCa.ConstructorArguments[0], methodCa.ConstructorArguments[1]); ca.CopyPropertiesFrom(methodCa); method.CustomAttributes.Add(ca); typeDef.Methods.Add(method); typeMethods.Add(method); // Avoid name conflicts var nested = typeDef.NestedTypes.FirstOrDefault(x => x.Name == method.Name); if (nested != null) { nested.Name = "Java" + nested.Name; } } } }
/// <summary> /// Remove all properties that override / implement a property that does not exist. /// /// Will also fix property visibility. /// </summary> private void FixOverridenProperties(MethodRenamer methodRenamer, TargetFramework target) { if (!typeDef.Properties.Any()) { return; } // handle overiding properties var allBaseTypes = typeDef.GetBaseTypes(true); var allBaseProperties = allBaseTypes.SelectMany(x => x.GetElementType().Properties).ToList(); var overridingProperties = typeDef.Properties.Where(x => IsAnyOverride(x) && !DoNotFixOverridenProperty(x)).ToList(); for (int i = 0; i < overridingProperties.Count; ++i) { var prop = overridingProperties[i]; NetPropertyDefinition matchingBaseProp = null; if (prop.Getter != null) { // Note: this logic might need to be also applied for the "lone" setter logic below. foreach (var baseProp in allBaseProperties.Where(p => p.Name == prop.Name)) { var basePropType = GenericParameters.Resolve(baseProp.PropertyType, prop.DeclaringType, target.TypeNameMap); if (!prop.PropertyType.AreSame(basePropType)) { continue; } matchingBaseProp = baseProp; break; } // Check for existing property in base class if (matchingBaseProp == null) { // implement as normal method. RemoveProperty(prop); continue; } // Check for base property with setter if (prop.Setter != null) { if (matchingBaseProp.Setter == null) { // Remove setter prop.Setter.Property = null; prop.Setter = null; continue; } } } else { // this is a "lone" setter. for boolean setters, the name might have changed. // try to match all base properties, update the name if neccessary. foreach (var baseProp in allBaseProperties.Where(g => g.Getter != null && g.Setter != null)) { if (FindSetter(baseProp.Getter, new[] { prop.Setter }, true) != null) { if (baseProp.Setter.IsVirtual) { if (matchingBaseProp == null || prop.Name == baseProp.Name) { matchingBaseProp = baseProp; } } } } if (matchingBaseProp == null) { // remove the property alltogether prop.Setter.Property = null; typeDef.Properties.Remove(prop); continue; } prop.Name = matchingBaseProp.Name; } // We must implement the property, since we inherited it. // Fix up any clashes. // clashes with explicit implementations should not occur. if ((prop.Getter ?? prop.Setter).InterfaceType != null) { continue; } var propName = prop.Name; if (propName == typeDef.Name) { if (!matchingBaseProp.DeclaringType.IsInterface) { Console.Error.WriteLine("Warning: Inherited property {0}::{1} clashes with type name. Skipping generation of property and methods.", typeDef.FullName, propName); typeDef.Properties.Remove(prop); typeDef.Methods.Remove(prop.Getter); typeDef.Methods.Remove(prop.Setter); continue; } // make this an explicit interface implementtion. // TODO: We might also want to keep a renamed property in this case, // too allow access to the property from the class. // Also, the explicit implementation does not need a "JavaImport" attribute. Console.Error.WriteLine("Warning: Inherited property {0}::{1} clashes with type name. Generating explicit implementation.", typeDef.FullName, propName); if (prop.Getter != null) { if (matchingBaseProp.Getter != null) { prop.Getter.SetExplicitImplementation(matchingBaseProp.Getter, matchingBaseProp.DeclaringType); } else { prop.Getter = null; } } if (prop.Setter != null) { if (matchingBaseProp.Setter != null) { prop.Setter.SetExplicitImplementation(matchingBaseProp.Setter, matchingBaseProp.DeclaringType); } else { prop.Setter = null; } } continue; } if (typeDef.NestedTypes.Any(x => x.Name == propName)) { Console.Error.WriteLine("Warning: Inherited property {0}::{1} clashes with nested type. Renaming nested type, but consider renaming the property instead.", typeDef.FullName, propName); typeDef.NestedTypes.Where(x => x.Name == propName).ForEach(t => t.Name += "FixMe"); } foreach (var clash in typeDef.Properties.Where(x => x != prop && x.Name == propName).ToList()) { if (clash.Parameters.Count != prop.Parameters.Count) { continue; } if ((clash.Getter ?? clash.Setter).InterfaceType != null) { continue; } if (prop.PropertyType.AreSame(clash.PropertyType) && prop.MainMethod.CreateReason == "TypeBuilder.AddAbstractInterfaceMethods") { // it appears that the method does have an implementation! typeDef.Properties.Remove(prop); typeDef.Methods.Remove(prop.Getter); typeDef.Methods.Remove(prop.Setter); overridingProperties.RemoveAt(i); i -= 1; goto continue_outer; } if (overridingProperties.Contains(clash)) { // This would have probably been removed later anyways. //Console.Error.WriteLine("Warning: duplicate property names {0}::{1}. Not generating property for one of the clashes.", typeDef.FullName, propName); RemoveProperty(clash); overridingProperties.Remove(clash); // this must come after us. continue; } // else remove other property RemoveProperty(clash); } if (typeDef.Methods.Any(x => x != prop.Setter && x != prop.Getter && x.Name == propName)) { Console.Error.WriteLine("Warning: Inherited property {0}::{1} clashes with methods. Prepending \"Invoke\" prefix to methods.", typeDef.FullName, propName); typeDef.Methods.Where(x => x != prop.Setter && x != prop.Getter && x.Name == propName) .ForEach(m => methodRenamer.Rename(m, "Invoke" + m.Name)); } continue_outer :; } }