From f348a80c32077328fefd6c1efab6b4dbae2388f0 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 4 Oct 2013 23:34:23 +0300 Subject: [PATCH] Moved the improved property pass to a new class. Added the list of verbs as a resource in premake. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 2 +- src/Generator/Generator.lua | 5 +- .../Generators/CSharp/CSharpTextTemplate.cs | 36 ++- .../GetterSetterToPropertyAdvancedPass.cs | 245 +++++++++++++++ .../Passes/GetterSetterToPropertyPass.cs | 279 ++++++------------ 5 files changed, 363 insertions(+), 204 deletions(-) create mode 100644 src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 6c790857..7fa742a8 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -159,7 +159,7 @@ namespace CppSharp TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass()); } if (Options.GenerateProperties) - TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass()); + TranslationUnitPasses.AddPass(new GetterSetterToPropertyAdvancedPass()); } public void ProcessCode() diff --git a/src/Generator/Generator.lua b/src/Generator/Generator.lua index bfe88b43..f8fcb194 100644 --- a/src/Generator/Generator.lua +++ b/src/Generator/Generator.lua @@ -4,7 +4,10 @@ project "CppSharp.Generator" language "C#" location "." - files { "**.cs", "**.bmp", "**.resx", "**.config" } + files { "**.cs", "**.bmp", "**.resx", "**.config", "**verbs.txt" } excludes { "Filter.cs" } links { "System", "System.Core", "CppSharp.AST", "CppSharp.Parser" } + + configuration '**verbs.txt' + buildaction "Embed" diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index e7bd048c..a4033148 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -898,7 +898,7 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); return; } - WriteLine(""); + NewLine(); WriteStartBraceIndent(); Method method = function as Method; if (method != null && method.IsOverride && method.IsSynthetized) @@ -918,7 +918,7 @@ namespace CppSharp.Generators.CSharp } else if (decl is Field) { - WriteLine(""); + NewLine(); WriteStartBraceIndent(); var field = decl as Field; @@ -944,7 +944,7 @@ namespace CppSharp.Generators.CSharp } else if (decl is Variable) { - WriteLine(""); + NewLine(); WriteStartBraceIndent(); var @var = decl as Variable; var libSymbol = GetDeclarationLibrarySymbol(@var); @@ -1264,18 +1264,7 @@ namespace CppSharp.Generators.CSharp } else { - var property = ((Class) method.Namespace).Properties.FirstOrDefault( - p => p.GetMethod == method); - if (property == null) - { - property = ((Class) method.Namespace).Properties.First( - p => p.SetMethod == method); - WriteLine("target.{0} = {1};", property.Name, string.Join(", ", marshals)); - } - else - { - WriteLine("target.{0};", property.Name); - } + InvokeProperty(method, marshals); } // TODO: Handle hidden structure return types. @@ -1306,6 +1295,23 @@ namespace CppSharp.Generators.CSharp } } + private void InvokeProperty(Declaration method, IEnumerable marshals) + { + var property = ((Class) method.Namespace).Properties.FirstOrDefault( + p => p.GetMethod == method); + if (property == null) + { + property = ((Class) method.Namespace).Properties.First( + p => p.SetMethod == method); + WriteLine("target.{0} = {1};", SafeIdentifier(property.Name), + string.Join(", ", marshals)); + } + else + { + WriteLine("target.{0};", SafeIdentifier(property.Name)); + } + } + private void GenerateVTableMethodDelegates(Class @class, Method method) { PushBlock(CSharpBlockKind.VTableDelegate); diff --git a/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs b/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs new file mode 100644 index 00000000..aa16af61 --- /dev/null +++ b/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using CppSharp.AST; + +namespace CppSharp.Passes +{ + class GetterSetterToPropertyAdvancedPass : TranslationUnitPass + { + // collect all types of methods first to be able to match pairs and detect virtuals and overrides; + // (a property needs to) be virtual or an override if either of its constituent methods are such) + private readonly List setters = new List(); + private readonly List setMethods = new List(); + private readonly List nonSetters = new List(); + private readonly HashSet getters = new HashSet(); + private static readonly HashSet verbs = new HashSet(); + + static GetterSetterToPropertyAdvancedPass() + { + LoadVerbs(); + } + + private static void LoadVerbs() + { + using (var resourceStream = Assembly.GetExecutingAssembly() + .GetManifestResourceStream("CppSharp.Generator.Passes.verbs.txt")) + { + using (StreamReader streamReader = new StreamReader(resourceStream)) + while (!streamReader.EndOfStream) + verbs.Add(streamReader.ReadLine()); + } + } + + public GetterSetterToPropertyAdvancedPass() + { + Options.VisitClassFields = false; + Options.VisitClassProperties = false; + Options.VisitNamespaceEnums = false; + Options.VisitNamespaceTemplates = false; + Options.VisitNamespaceTypedefs = false; + Options.VisitNamespaceEvents = false; + Options.VisitNamespaceVariables = false; + Options.VisitFunctionParameters = false; + Options.VisitTemplateArguments = false; + } + + public override bool VisitTranslationUnit(TranslationUnit unit) + { + bool result = base.VisitTranslationUnit(unit); + GenerateProperties(); + return result; + } + + public override bool VisitMethodDecl(Method method) + { + if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator && + !method.Ignore) + DistributeMethod(method); + return base.VisitMethodDecl(method); + } + + public void GenerateProperties() + { + GenerateProperties(setters, false); + GenerateProperties(setMethods, true); + + foreach (Method getter in + from getter in getters + where getter.IsGenerated && + ((Class) getter.Namespace).Methods.All(m => m == getter || m.Name != getter.Name) + select getter) + { + // Make it a read-only property + GenerateProperty(getter.Namespace, getter); + } + } + + private void GenerateProperties(IEnumerable settersToUse, bool readOnly) + { + foreach (var group in settersToUse.GroupBy(m => m.Namespace)) + { + foreach (var setter in group) + { + Class type = (Class) setter.Namespace; + StringBuilder nameBuilder = new StringBuilder(setter.Name.Substring(3)); + if (char.IsLower(setter.Name[0])) + nameBuilder[0] = char.ToLowerInvariant(nameBuilder[0]); + string afterSet = nameBuilder.ToString(); + foreach (var getter in nonSetters.Where(m => m.Namespace == type)) + { + string name = GetPropertyName(getter.Name); + if (string.Compare(name, afterSet, StringComparison.OrdinalIgnoreCase) == 0 && + getter.ReturnType == setter.Parameters[0].QualifiedType && + !type.Methods.Any( + m => + m != getter && + string.Compare(name, m.Name, StringComparison.OrdinalIgnoreCase) == 0)) + { + GenerateProperty(getter.Namespace, getter, readOnly ? null : setter); + goto next; + } + } + Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = afterSet }); + if (!type.IsInterface && baseVirtualProperty != null) + { + bool isReadOnly = baseVirtualProperty.SetMethod == null; + GenerateProperty(setter.Namespace, baseVirtualProperty.GetMethod, + readOnly || isReadOnly ? null : setter); + } + next: + ; + } + } + foreach (Method nonSetter in nonSetters) + { + Class type = (Class) nonSetter.Namespace; + string name = GetPropertyName(nonSetter.Name); + Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = name }); + if (!type.IsInterface && baseVirtualProperty != null) + { + bool isReadOnly = baseVirtualProperty.SetMethod == null; + if (readOnly == isReadOnly) + { + GenerateProperty(nonSetter.Namespace, nonSetter, + readOnly ? null : baseVirtualProperty.SetMethod); + } + } + } + } + + private static void GenerateProperty(DeclarationContext context, Method getter, Method setter = null) + { + Class type = (Class) context; + if (type.Properties.All( + p => string.Compare(getter.Name, p.Name, StringComparison.OrdinalIgnoreCase) != 0 || + p.ExplicitInterfaceImpl != getter.ExplicitInterfaceImpl)) + { + Property property = new Property(); + property.Name = GetPropertyName(getter.Name); + property.Namespace = type; + property.QualifiedType = getter.ReturnType; + if (getter.IsOverride || (setter != null && setter.IsOverride)) + { + Property baseVirtualProperty = type.GetRootBaseProperty(property); + if (baseVirtualProperty.SetMethod == null) + setter = null; + foreach (Method method in type.Methods.Where(m => m.Name == property.Name && m.Parameters.Count > 0)) + method.Name = "get" + method.Name; + } + property.GetMethod = getter; + property.SetMethod = setter; + property.ExplicitInterfaceImpl = getter.ExplicitInterfaceImpl; + if (property.ExplicitInterfaceImpl == null && setter != null) + { + property.ExplicitInterfaceImpl = setter.ExplicitInterfaceImpl; + } + if (getter.Comment != null) + { + var comment = new RawComment(); + comment.Kind = getter.Comment.Kind; + comment.BriefText = getter.Comment.BriefText; + comment.Text = getter.Comment.Text; + comment.FullComment = new FullComment(); + comment.FullComment.Blocks.AddRange(getter.Comment.FullComment.Blocks); + if (setter != null && setter.Comment != null) + { + comment.BriefText += Environment.NewLine + setter.Comment.BriefText; + comment.Text += Environment.NewLine + setter.Comment.Text; + comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); + } + property.Comment = comment; + } + type.Properties.Add(property); + getter.IsGenerated = false; + if (setter != null) + setter.IsGenerated = false; + } + } + + private static string GetPropertyName(string name) + { + if (GetFirstWord(name) == "get") + { + if (char.IsLower(name[0])) + { + if (name.Length == 4) + { + return char.ToLowerInvariant( + name[3]).ToString(CultureInfo.InvariantCulture); + } + return char.ToLowerInvariant( + name[3]).ToString(CultureInfo.InvariantCulture) + + name.Substring(4); + } + return name.Substring(3); + } + return name; + } + + private void DistributeMethod(Method method) + { + if (GetFirstWord(method.Name) == "set" && method.Name.Length > 3 && + method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void)) + { + if (method.Parameters.Count == 1) + setters.Add(method); + else + setMethods.Add(method); + } + else + { + if (IsGetter(method)) + getters.Add(method); + if (method.Parameters.Count == 0) + nonSetters.Add(method); + } + } + + private bool IsGetter(Method method) + { + if (method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void) || + method.Parameters.Count > 0 || method.IsDestructor) + return false; + var result = GetFirstWord(method.Name); + return (result.Length < method.Name.Length && + (result == "get" || result == "is" || result == "has")) || + (result != "to" && result != "new" && !verbs.Contains(result)); + } + + private static string GetFirstWord(string name) + { + List firstVerb = new List + { + char.ToLowerInvariant(name[0]) + }; + firstVerb.AddRange(name.Skip(1).TakeWhile( + c => char.IsLower(c) || !char.IsLetterOrDigit(c))); + return new string(firstVerb.ToArray()); + } + } +} diff --git a/src/Generator/Passes/GetterSetterToPropertyPass.cs b/src/Generator/Passes/GetterSetterToPropertyPass.cs index fc14ed78..3ad2ebbd 100644 --- a/src/Generator/Passes/GetterSetterToPropertyPass.cs +++ b/src/Generator/Passes/GetterSetterToPropertyPass.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; using System.Linq; -using System.Reflection; -using System.Text; using CppSharp.AST; namespace CppSharp.Passes @@ -15,12 +10,6 @@ namespace CppSharp.Passes /// public class GetterSetterToPropertyPass : TranslationUnitPass { - private readonly List setters = new List(); - private readonly List setMethods = new List(); - private readonly List nonSetters = new List(); - private readonly HashSet getters = new HashSet(); - private readonly HashSet verbs = new HashSet(); - public GetterSetterToPropertyPass() { Options.VisitClassFields = false; @@ -32,207 +21,123 @@ namespace CppSharp.Passes Options.VisitNamespaceVariables = false; Options.VisitFunctionParameters = false; Options.VisitTemplateArguments = false; - using (var resourceStream = Assembly.GetExecutingAssembly() - .GetManifestResourceStream("CppSharp.Generator.Passes.verbs.txt")) - { - using (StreamReader streamReader = new StreamReader(resourceStream)) - while (!streamReader.EndOfStream) - verbs.Add(streamReader.ReadLine()); - } } - public override bool VisitTranslationUnit(TranslationUnit unit) + static bool IsSetter(Function method) { - bool result = base.VisitTranslationUnit(unit); - GenerateProperties(); - return result; - } + var isRetVoid = method.ReturnType.Type.IsPrimitiveType( + PrimitiveType.Void); - public override bool VisitMethodDecl(Method method) - { - if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator && - !method.Ignore) - DistributeMethod(method); - return base.VisitMethodDecl(method); - } + var isSetter = method.OriginalName.StartsWith("set", + StringComparison.InvariantCultureIgnoreCase); - public void GenerateProperties() - { - GenerateProperties(setters, false); - GenerateProperties(setMethods, true); - - foreach (Method getter in - from getter in getters - where getter.IsGenerated && - ((Class) getter.Namespace).Methods.All(m => m == getter || m.Name != getter.Name) - select getter) - { - // Make it a read-only property - GenerateProperty(getter.Namespace, getter); - } + return isRetVoid && isSetter && method.Parameters.Count == 1; } - private void GenerateProperties(IEnumerable settersToUse, bool readOnly) + static bool IsGetter(Function method) { - foreach (var group in settersToUse.GroupBy(m => m.Namespace)) - { - foreach (var setter in group) - { - Class type = (Class) setter.Namespace; - StringBuilder nameBuilder = new StringBuilder(setter.Name.Substring(3)); - if (char.IsLower(setter.Name[0])) - nameBuilder[0] = char.ToLowerInvariant(nameBuilder[0]); - string afterSet = nameBuilder.ToString(); - foreach (var getter in nonSetters.Where(m => m.Namespace == type)) - { - string name = GetPropertyName(getter.Name); - if (string.Compare(name, afterSet, StringComparison.OrdinalIgnoreCase) == 0 && - getter.ReturnType == setter.Parameters[0].QualifiedType && - !type.Methods.Any( - m => - m != getter && - string.Compare(name, m.Name, StringComparison.OrdinalIgnoreCase) == 0)) - { - GenerateProperty(getter.Namespace, getter, readOnly ? null : setter); - goto next; - } - } - Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = afterSet }); - if (!type.IsInterface && baseVirtualProperty != null) - { - bool isReadOnly = baseVirtualProperty.SetMethod == null; - GenerateProperty(setter.Namespace, baseVirtualProperty.GetMethod, - readOnly || isReadOnly ? null : setter); - } - next: - ; - } - } - foreach (Method nonSetter in nonSetters) - { - Class type = (Class) nonSetter.Namespace; - string name = GetPropertyName(nonSetter.Name); - Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = name }); - if (!type.IsInterface && baseVirtualProperty != null) - { - bool isReadOnly = baseVirtualProperty.SetMethod == null; - if (readOnly == isReadOnly) - { - GenerateProperty(nonSetter.Namespace, nonSetter, - readOnly ? null : baseVirtualProperty.SetMethod); - } - } - } - } + var isRetVoid = method.ReturnType.Type.IsPrimitiveType( + PrimitiveType.Void); - private static void GenerateProperty(DeclarationContext context, Method getter, Method setter = null) - { - Class type = (Class) context; - if (type.Properties.All( - p => string.Compare(getter.Name, p.Name, StringComparison.OrdinalIgnoreCase) != 0 || - p.ExplicitInterfaceImpl != getter.ExplicitInterfaceImpl)) - { - Property property = new Property(); - property.Name = GetPropertyName(getter.Name); - property.Namespace = type; - property.QualifiedType = getter.ReturnType; - if (getter.IsOverride || (setter != null && setter.IsOverride)) - { - Property baseVirtualProperty = type.GetRootBaseProperty(property); - if (baseVirtualProperty.SetMethod == null) - setter = null; - foreach (Method method in type.Methods.Where(m => m.Name == property.Name && m.Parameters.Count > 0)) - method.Name = "get" + method.Name; - } - property.GetMethod = getter; - property.SetMethod = setter; - property.ExplicitInterfaceImpl = getter.ExplicitInterfaceImpl; - if (property.ExplicitInterfaceImpl == null && setter != null) - { - property.ExplicitInterfaceImpl = setter.ExplicitInterfaceImpl; - } - if (getter.Comment != null) - { - var comment = new RawComment(); - comment.Kind = getter.Comment.Kind; - comment.BriefText = getter.Comment.BriefText; - comment.Text = getter.Comment.Text; - comment.FullComment = new FullComment(); - comment.FullComment.Blocks.AddRange(getter.Comment.FullComment.Blocks); - if (setter != null && setter.Comment != null) - { - comment.BriefText += Environment.NewLine + setter.Comment.BriefText; - comment.Text += Environment.NewLine + setter.Comment.Text; - comment.FullComment.Blocks.AddRange(setter.Comment.FullComment.Blocks); - } - property.Comment = comment; - } - type.Properties.Add(property); - getter.IsGenerated = false; - if (setter != null) - setter.IsGenerated = false; - } + var isGetter = method.OriginalName.StartsWith("get", + StringComparison.InvariantCultureIgnoreCase); + + return !isRetVoid && isGetter && method.Parameters.Count == 0; } - private static string GetPropertyName(string name) + Property GetOrCreateProperty(Class @class, string name, QualifiedType type) { - if (GetFirstWord(name) == "get") + var prop = @class.Properties.FirstOrDefault(property => property.Name == name + && property.QualifiedType.Equals(type)); + + var prop2 = @class.Properties.FirstOrDefault(property => property.Name == name); + + if (prop == null && prop2 != null) + Driver.Diagnostics.EmitWarning(DiagnosticId.PropertySynthetized, + "Property {0}::{1} already exist with type {2}", @class.Name, name, type.Type.ToString()); + + if (prop != null) + return prop; + + prop = new Property { - if (char.IsLower(name[0])) - { - if (name.Length == 4) - { - return char.ToLowerInvariant( - name[3]).ToString(CultureInfo.InvariantCulture); - } - return char.ToLowerInvariant( - name[3]).ToString(CultureInfo.InvariantCulture) + - name.Substring(4); - } - return name.Substring(3); - } - return name; + Name = name, + Namespace = @class, + QualifiedType = type + }; + + @class.Properties.Add(prop); + return prop; } - private void DistributeMethod(Method method) + public override bool VisitMethodDecl(Method method) { - if (GetFirstWord(method.Name) == "set" && method.Name.Length > 3 && - method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void)) + if (AlreadyVisited(method)) + return false; + + if (ASTUtils.CheckIgnoreMethod(method)) + return false; + + var @class = method.Namespace as Class; + + if (@class == null || @class.IsIncomplete) + return false; + + if (IsGetter(method)) { - if (method.Parameters.Count == 1) - setters.Add(method); - else - setMethods.Add(method); + var name = method.Name.Substring("get".Length); + var prop = GetOrCreateProperty(@class, name, method.ReturnType); + prop.GetMethod = method; + prop.Access = method.Access; + + // Do not generate the original method now that we know it is a getter. + method.IsGenerated = false; + + Driver.Diagnostics.EmitMessage(DiagnosticId.PropertySynthetized, + "Getter created: {0}::{1}", @class.Name, name); + + return false; } - else + + if (IsSetter(method) && IsValidSetter(method)) { - if (IsGetter(method)) - getters.Add(method); - if (method.Parameters.Count == 0) - nonSetters.Add(method); + var name = method.Name.Substring("set".Length); + + var type = method.Parameters[0].QualifiedType; + var prop = GetOrCreateProperty(@class, name, type); + prop.SetMethod = method; + prop.Access = method.Access; + + // Ignore the original method now that we know it is a setter. + method.IsGenerated = false; + + Driver.Diagnostics.EmitMessage(DiagnosticId.PropertySynthetized, + "Setter created: {0}::{1}", @class.Name, name); + + return false; } + + return false; } - private bool IsGetter(Method method) + // Check if a matching getter exist or no other setter exists. + private bool IsValidSetter(Method method) { - if (method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void) || - method.Parameters.Count > 0 || method.IsDestructor) + var @class = method.Namespace as Class; + var name = method.Name.Substring("set".Length); + + if (method.Parameters.Count == 0) return false; - var result = GetFirstWord(method.Name); - return (result.Length < method.Name.Length && - (result == "get" || result == "is" || result == "has")) || - (result != "to" && result != "new" && !verbs.Contains(result)); - } - private static string GetFirstWord(string name) - { - List firstVerb = new List - { - char.ToLowerInvariant(name[0]) - }; - firstVerb.AddRange(name.Skip(1).TakeWhile( - c => char.IsLower(c) || !char.IsLetterOrDigit(c))); - return new string(firstVerb.ToArray()); + var type = method.Parameters[0].Type; + + var getter = @class.Methods.FirstOrDefault(m => m.Name == "Get" + name && m.Type.Equals(type)); + + var otherSetter = @class.Methods.FirstOrDefault(m => m.Name == method.Name + && m.Parameters.Count == 1 + && !m.Parameters[0].Type.Equals(type)); + + return getter != null || otherSetter == null; } } }