Browse Source

Moved the improved property pass to a new class. Added the list of verbs as a resource in premake.

Signed-off-by: Dimitar Dobrev <dpldobrev@yahoo.com>
pull/77/head
Dimitar Dobrev 12 years ago
parent
commit
f348a80c32
  1. 2
      src/Generator/Driver.cs
  2. 5
      src/Generator/Generator.lua
  3. 36
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  4. 245
      src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs
  5. 279
      src/Generator/Passes/GetterSetterToPropertyPass.cs

2
src/Generator/Driver.cs

@ -159,7 +159,7 @@ namespace CppSharp
TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass()); TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass());
} }
if (Options.GenerateProperties) if (Options.GenerateProperties)
TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass()); TranslationUnitPasses.AddPass(new GetterSetterToPropertyAdvancedPass());
} }
public void ProcessCode() public void ProcessCode()

5
src/Generator/Generator.lua

@ -4,7 +4,10 @@ project "CppSharp.Generator"
language "C#" language "C#"
location "." location "."
files { "**.cs", "**.bmp", "**.resx", "**.config" } files { "**.cs", "**.bmp", "**.resx", "**.config", "**verbs.txt" }
excludes { "Filter.cs" } excludes { "Filter.cs" }
links { "System", "System.Core", "CppSharp.AST", "CppSharp.Parser" } links { "System", "System.Core", "CppSharp.AST", "CppSharp.Parser" }
configuration '**verbs.txt'
buildaction "Embed"

36
src/Generator/Generators/CSharp/CSharpTextTemplate.cs

@ -898,7 +898,7 @@ namespace CppSharp.Generators.CSharp
PopBlock(NewLineKind.BeforeNextBlock); PopBlock(NewLineKind.BeforeNextBlock);
return; return;
} }
WriteLine(""); NewLine();
WriteStartBraceIndent(); WriteStartBraceIndent();
Method method = function as Method; Method method = function as Method;
if (method != null && method.IsOverride && method.IsSynthetized) if (method != null && method.IsOverride && method.IsSynthetized)
@ -918,7 +918,7 @@ namespace CppSharp.Generators.CSharp
} }
else if (decl is Field) else if (decl is Field)
{ {
WriteLine(""); NewLine();
WriteStartBraceIndent(); WriteStartBraceIndent();
var field = decl as Field; var field = decl as Field;
@ -944,7 +944,7 @@ namespace CppSharp.Generators.CSharp
} }
else if (decl is Variable) else if (decl is Variable)
{ {
WriteLine(""); NewLine();
WriteStartBraceIndent(); WriteStartBraceIndent();
var @var = decl as Variable; var @var = decl as Variable;
var libSymbol = GetDeclarationLibrarySymbol(@var); var libSymbol = GetDeclarationLibrarySymbol(@var);
@ -1264,18 +1264,7 @@ namespace CppSharp.Generators.CSharp
} }
else else
{ {
var property = ((Class) method.Namespace).Properties.FirstOrDefault( InvokeProperty(method, marshals);
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);
}
} }
// TODO: Handle hidden structure return types. // TODO: Handle hidden structure return types.
@ -1306,6 +1295,23 @@ namespace CppSharp.Generators.CSharp
} }
} }
private void InvokeProperty(Declaration method, IEnumerable<string> 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) private void GenerateVTableMethodDelegates(Class @class, Method method)
{ {
PushBlock(CSharpBlockKind.VTableDelegate); PushBlock(CSharpBlockKind.VTableDelegate);

245
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<Method> setters = new List<Method>();
private readonly List<Method> setMethods = new List<Method>();
private readonly List<Method> nonSetters = new List<Method>();
private readonly HashSet<Method> getters = new HashSet<Method>();
private static readonly HashSet<string> verbs = new HashSet<string>();
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<Method> 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<char> firstVerb = new List<char>
{
char.ToLowerInvariant(name[0])
};
firstVerb.AddRange(name.Skip(1).TakeWhile(
c => char.IsLower(c) || !char.IsLetterOrDigit(c)));
return new string(firstVerb.ToArray());
}
}
}

279
src/Generator/Passes/GetterSetterToPropertyPass.cs

@ -1,10 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text;
using CppSharp.AST; using CppSharp.AST;
namespace CppSharp.Passes namespace CppSharp.Passes
@ -15,12 +10,6 @@ namespace CppSharp.Passes
/// </summary> /// </summary>
public class GetterSetterToPropertyPass : TranslationUnitPass public class GetterSetterToPropertyPass : TranslationUnitPass
{ {
private readonly List<Method> setters = new List<Method>();
private readonly List<Method> setMethods = new List<Method>();
private readonly List<Method> nonSetters = new List<Method>();
private readonly HashSet<Method> getters = new HashSet<Method>();
private readonly HashSet<string> verbs = new HashSet<string>();
public GetterSetterToPropertyPass() public GetterSetterToPropertyPass()
{ {
Options.VisitClassFields = false; Options.VisitClassFields = false;
@ -32,207 +21,123 @@ namespace CppSharp.Passes
Options.VisitNamespaceVariables = false; Options.VisitNamespaceVariables = false;
Options.VisitFunctionParameters = false; Options.VisitFunctionParameters = false;
Options.VisitTemplateArguments = 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); var isRetVoid = method.ReturnType.Type.IsPrimitiveType(
GenerateProperties(); PrimitiveType.Void);
return result;
}
public override bool VisitMethodDecl(Method method) var isSetter = method.OriginalName.StartsWith("set",
{ StringComparison.InvariantCultureIgnoreCase);
if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator &&
!method.Ignore)
DistributeMethod(method);
return base.VisitMethodDecl(method);
}
public void GenerateProperties() return isRetVoid && isSetter && method.Parameters.Count == 1;
{
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<Method> settersToUse, bool readOnly) static bool IsGetter(Function method)
{ {
foreach (var group in settersToUse.GroupBy(m => m.Namespace)) var isRetVoid = method.ReturnType.Type.IsPrimitiveType(
{ PrimitiveType.Void);
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) var isGetter = method.OriginalName.StartsWith("get",
{ StringComparison.InvariantCultureIgnoreCase);
Class type = (Class) context;
if (type.Properties.All( return !isRetVoid && isGetter && method.Parameters.Count == 0;
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) 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])) Name = name,
{ Namespace = @class,
if (name.Length == 4) QualifiedType = type
{ };
return char.ToLowerInvariant(
name[3]).ToString(CultureInfo.InvariantCulture); @class.Properties.Add(prop);
} return prop;
return char.ToLowerInvariant(
name[3]).ToString(CultureInfo.InvariantCulture) +
name.Substring(4);
}
return name.Substring(3);
}
return name;
} }
private void DistributeMethod(Method method) public override bool VisitMethodDecl(Method method)
{ {
if (GetFirstWord(method.Name) == "set" && method.Name.Length > 3 && if (AlreadyVisited(method))
method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void)) 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) var name = method.Name.Substring("get".Length);
setters.Add(method); var prop = GetOrCreateProperty(@class, name, method.ReturnType);
else prop.GetMethod = method;
setMethods.Add(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)) var name = method.Name.Substring("set".Length);
getters.Add(method);
if (method.Parameters.Count == 0) var type = method.Parameters[0].QualifiedType;
nonSetters.Add(method); 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) || var @class = method.Namespace as Class;
method.Parameters.Count > 0 || method.IsDestructor) var name = method.Name.Substring("set".Length);
if (method.Parameters.Count == 0)
return false; 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) var type = method.Parameters[0].Type;
{
List<char> firstVerb = new List<char> var getter = @class.Methods.FirstOrDefault(m => m.Name == "Get" + name && m.Type.Equals(type));
{
char.ToLowerInvariant(name[0]) var otherSetter = @class.Methods.FirstOrDefault(m => m.Name == method.Name
}; && m.Parameters.Count == 1
firstVerb.AddRange(name.Skip(1).TakeWhile( && !m.Parameters[0].Type.Equals(type));
c => char.IsLower(c) || !char.IsLetterOrDigit(c)));
return new string(firstVerb.ToArray()); return getter != null || otherSetter == null;
} }
} }
} }

Loading…
Cancel
Save