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

5
src/Generator/Generator.lua

@ -4,7 +4,10 @@ project "CppSharp.Generator" @@ -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"

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

@ -898,7 +898,7 @@ namespace CppSharp.Generators.CSharp @@ -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 @@ -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 @@ -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 @@ -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 @@ -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)
{
PushBlock(CSharpBlockKind.VTableDelegate);

245
src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs

@ -0,0 +1,245 @@ @@ -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 @@ @@ -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 @@ -15,12 +10,6 @@ namespace CppSharp.Passes
/// </summary>
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()
{
Options.VisitClassFields = false;
@ -32,207 +21,123 @@ namespace CppSharp.Passes @@ -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<Method> 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<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());
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;
}
}
}

Loading…
Cancel
Save