/// <summary> /// Do prop1 and prop2 have the same name and signature? /// </summary> internal static bool AreSame(this NetPropertyDefinition prop1, NetPropertyDefinition prop2) { if (prop1.Name != prop2.Name) { return(false); } if (!prop1.PropertyType.AreSame(prop2.PropertyType)) { return(false); } var count = prop1.Parameters.Count; if (count != prop2.Parameters.Count) { return(false); } for (var i = 0; i < count; i++) { if (!prop1.Parameters[i].ParameterType.AreSame(prop2.Parameters[i].ParameterType)) { return(false); } } return(true); }
private void RemoveProperty(NetPropertyDefinition prop) { if (prop.Getter != null) { prop.Getter.Property = null; } if (prop.Setter != null) { prop.Setter.Property = null; } typeDef.Properties.Remove(prop); }
public static bool IsNameClash(NetTypeDefinition typeDef, NetPropertyDefinition prop) { if ((prop.Getter ?? prop.Setter).InterfaceType != null) { return(false); } var name = prop.Name; return(prop.Name == typeDef.Name || typeDef.NestedTypes.Any(x => x.Name == name) || typeDef.Methods.Any(x => (x != prop.Getter && x != prop.Setter) && (x.Name == name))); }
private void AddToProperty(NetTypeDefinition typeDef, NetMethodDefinition method, MethodDefinition ilMethod) { var ilProp = ilMethod.DeclaringType.Properties.First(p => p.SetMethod == ilMethod || p.GetMethod == ilMethod); if (ilProp.Parameters.Count > 0) //no need to handle indexers for now { return; } var propType = ilMethod.IsGetter ? method.ReturnType : method.Parameters[0].ParameterType; var prop = typeDef.Properties.FirstOrDefault(p => p.Name == ilProp.Name //&& p.Parameters.Count == ilProp.Parameters.Count && p.PropertyType == propType && p.MainMethod.InterfaceType.AreSame(method.InterfaceType)); if (prop == null) { prop = new NetPropertyDefinition(); prop.Name = ilProp.Name; prop.Description = method.Description; prop.DeclaringType = method.DeclaringType; prop.EditorBrowsableState = method.EditorBrowsableState; prop.CustomAttributes.AddRange(method.CustomAttributes); typeDef.Properties.Add(prop); } if (ilMethod.IsGetter) { prop.Getter = method; } else { prop.Setter = method; } }
/// <summary> /// Build properties for the given type /// </summary> internal void BuildProperties(TargetFramework target, MethodRenamer methodRenamer) { if (typeDef.IsInterface) { return; } var getters = typeDef.Methods.Where(IsGetter).ToList(); var setters = typeDef.Methods.Where(IsSetter).ToList(); var generatedNames = new HashSet <string>(); foreach (var getMethod in getters) { // Get the name of the property var name = GetPropertyName(getMethod); // If there are other methods with same name, we do not create a property if (typeDef.Methods.Any(x => (x != getMethod) && (x.Name == name))) { continue; } if (typeDef.NestedTypes.Any(x => x.Name == name)) { continue; } if (typeDef.Name == name) { continue; } if (typeDef.Fields.Any(x => x.Name == name)) { continue; } if (!generatedNames.Add(name + "_" + getMethod.Parameters.Count)) { continue; } // Create property var prop = new NetPropertyDefinition { Name = name, Getter = getMethod, Description = getMethod.Description }; AddCustomAttributes(getMethod, prop.CustomAttributes); // Clone parameters if (getMethod.Parameters.Any()) { prop.Parameters.AddRange(getMethod.Parameters.Select(x => new NetParameterDefinition(x.Name, x.ParameterType, false))); prop.Name = "this"; } // Rename getMethod if needed if (getMethod.Name == prop.Name) { methodRenamer.Rename(getMethod, "Get" + getMethod.Name); } // Find setter var setMethod = FindSetter(getMethod, setters); if (setMethod != null) { prop.Setter = setMethod; // Rename setMethod if needed if (setMethod.Name == prop.Name) { methodRenamer.Rename(setMethod, "Set" + setMethod.Name); } } // Add property typeDef.Properties.Add(prop); } }
/// <summary> /// Should the given property be excluded from fixup? /// </summary> private static bool DoNotFixOverridenProperty(NetPropertyDefinition prop) { return(prop.Name == "Adapter"); }
/// <summary> /// Create code for the given property /// </summary> private void CreatePropertyCode(NetPropertyDefinition prop) { CreateComments(prop.Description, prop.Getter.OriginalJavaName); CreateAttributes(prop.CustomAttributes, prop); writer.Write(indent); writer.Write(Convert(prop.Getter, prop.Getter.Attributes, context.GenerateExternalMethods, true)); writer.Write(CreateRef(prop.PropertyType, true, true, true, false, prop.DeclaringType, target)); writer.Write(" "); writer.Write(prop.Name); if (prop.Parameters.Any()) { writer.Write("["); var needComma = false; var pIndex = 0; foreach (var p in prop.Parameters) { if (needComma) { writer.Write(", "); } writer.Write(CreateRef(p.ParameterType, false, true, true, false, prop.DeclaringType, target)); writer.Write(pIndex == 0 ? " index" : string.Format(" p{0}", pIndex)); pIndex++; needComma = true; } writer.Write("]"); } // Create method body var isAbstract = prop.Getter.IsAbstract; writer.WriteLine(); writer.Write(indent); writer.WriteLine("{"); CreateAttributes(prop.Getter.CustomAttributes, prop.Getter); writer.Write(indent); writer.Write("\t\tget"); bool isEmptyMethod = false; // isAbstract || context.GenerateExternalMethods; if (!isEmptyMethod) { ////writer.Write("{ return default("); ////writer.Write(CreateRef(prop.PropertyType, false, true, true, prop.DeclaringType)); ////writer.Write("); }"); writer.Write("{{ return {0}({1}); }}", prop.Getter.Name, string.Join(", ", prop.Getter.Parameters.Select((x, i) => (i == 0) ? "index" : x.Name))); } else { writer.Write(";"); } writer.WriteLine(); if (prop.Setter != null) { CreateAttributes(prop.Setter.CustomAttributes, prop.Setter); writer.Write(indent); writer.Write("\t\tset"); if (!isEmptyMethod) { writer.Write("{{ {0}({1}); }}", prop.Setter.Name, string.Join(", ", prop.Setter.Parameters.Select((x, i) => (i == 0) ? "value" : x.Name))); } else { writer.Write(";"); } ////writer.Write(!isAbstract && !context.GenerateExternalMethods ? "{ }" : ";"); writer.WriteLine(); } writer.Write(indent); writer.WriteLine("}"); writer.WriteLine(); }
private static bool IsAnyOverride(NetPropertyDefinition prop) { return((prop.Getter != null && prop.Getter.IsOverride) || (prop.Setter != null && prop.Setter.IsOverride)); }
/// <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 :; } }
/// <summary> /// Build properties for the given type /// </summary> internal void BuildProperties(TargetFramework target, MethodRenamer methodRenamer) { var getters = typeDef.Methods.Where(IsGetter).ToList(); var setters = typeDef.Methods.Where(IsSetter).ToList(); foreach (var getMethod in getters) { // Get the name of the property var name = GetPropertyName(getMethod); // other clashes must be handled later. if (getMethod.InterfaceType == null) { typeDef.Fields.Where(x => x.Name == name).ForEach(RenameClashingField); } // Create property var prop = new NetPropertyDefinition { Name = name, Getter = getMethod, Description = getMethod.Description, }; AddCustomAttributes(getMethod, prop.CustomAttributes); // Clone parameters if (getMethod.Parameters.Any()) { prop.Parameters.AddRange(getMethod.Parameters.Select(x => new NetParameterDefinition(x.Name, x.ParameterType, false))); prop.Name = "this"; } getMethod.Property = prop; // Find setter var setMethod = FindSetter(getMethod, setters); if (setMethod != null) { prop.Setter = setMethod; setMethod.Property = prop; setters.Remove(setMethod); } // Add property typeDef.Properties.Add(prop); } // create properties for lone setters that override a base setter foreach (var setMethod in setters.Where(s => s.IsOverride)) { var name = GetPropertyName(setMethod); // other clashes must be handled in FixOverrides if (setMethod.InterfaceType == null) { typeDef.Fields.Where(x => x.Name == name).ForEach(RenameClashingField); } // Create property var prop = new NetPropertyDefinition { Name = name, Setter = setMethod, Description = setMethod.Description }; setMethod.Property = prop; AddCustomAttributes(setMethod, prop.CustomAttributes); // Clone parameters if (setMethod.Parameters.Skip(1).Any()) { prop.Parameters.AddRange(setMethod.Parameters.Skip(1).Select(x => new NetParameterDefinition(x.Name, x.ParameterType, false))); prop.Name = "this"; } typeDef.Properties.Add(prop); } }
/// <summary> /// Create code for the given property /// </summary> private void CreatePropertyCode(NetPropertyDefinition prop) { var mainMethod = prop.MainMethod; CreateComments(prop.Description, mainMethod.OriginalJavaName); CreateAttributes(prop.CustomAttributes, prop); writer.Write(indent); if (mainMethod.InterfaceMethod == null) { writer.Write(Convert(mainMethod, mainMethod.Attributes, context.GenerateExternalMethods, false)); writer.Write(CreateRef(prop.PropertyType, true, true, true, false, prop.DeclaringType, target)); writer.Write(" "); } else { writer.Write(CreateRef(prop.PropertyType, true, true, true, false, prop.DeclaringType, target)); writer.Write(" "); writer.Write(CreateRef(mainMethod.InterfaceType, false, true, true, false, mainMethod.DeclaringType, target)); writer.Write("."); } writer.Write(prop.Name); if (prop.Parameters.Any()) { writer.Write("["); var needComma = false; var pIndex = 0; foreach (var p in prop.Parameters) { if (needComma) { writer.Write(", "); } writer.Write(CreateRef(p.ParameterType, false, true, true, false, prop.DeclaringType, target)); writer.Write(pIndex == 0 ? " index" : string.Format(" p{0}", pIndex)); pIndex++; needComma = true; } writer.Write("]"); } // Create method body var isAbstract = mainMethod.IsAbstract; var isInterface = mainMethod.DeclaringType.IsInterface; writer.WriteLine(); writer.Write(indent); writer.WriteLine("{"); // increase indent var oldIndent = indent; indent += "\t\t"; try { if (prop.Getter != null) { CreateAttributes(mainMethod.CustomAttributes, prop.Getter); writer.Write(indent); writer.Write("get"); bool isEmptyMethod = isAbstract || isInterface || context.GenerateExternalMethods; if (!isEmptyMethod) { writer.Write("{ return default("); writer.Write(CreateRef(prop.PropertyType, false, true, true, false, prop.Getter.DeclaringType, target)); writer.Write("); }"); //writer.Write("{{ return {0}({1}); }}", prop.Getter.Name, string.Join(", ", prop.Getter.Parameters.Select((x, i) => (i == 0) ? "index" : x.Name))); } else { writer.Write(";"); } writer.WriteLine(); } if (prop.Setter != null) { CreateAttributes(prop.Setter.CustomAttributes, prop.Setter); writer.Write(indent); writer.Write("set"); //if (!isEmptyMethod) //{ // writer.Write("{{ {0}({1}); }}", prop.Setter.Name, // string.Join(", ", prop.Setter.Parameters.Select((x, i) => (i == 0) ? "value" : x.Name))); //} //else //{ // writer.Write(";"); //} writer.Write(!isAbstract && !context.GenerateExternalMethods ? "{ }" : ";"); writer.WriteLine(); } } finally { indent = indent = oldIndent; } // restore indent writer.Write(indent); writer.WriteLine("}"); writer.WriteLine(); }