Browse Source

Rewrote the generating of properties to additionaly handle property overrides and read-only properties.

Signed-off-by: Dimitar Dobrev <dpldobrev@yahoo.com>
pull/77/head
Dimitar Dobrev 12 years ago
parent
commit
824d8b5f30
  1. 2
      src/AST/Class.cs
  2. 36
      src/AST/Property.cs
  3. 1
      src/Generator/Driver.cs
  4. 76
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  5. 2
      src/Generator/Passes/FindSymbolsPass.cs
  6. 5
      src/Generator/Passes/GenerateAbstractImplementationsPass.cs
  7. 239
      src/Generator/Passes/GetterSetterToPropertyPass.cs
  8. 12
      src/Generator/Passes/MultipleInheritancePass.cs
  9. 12
      src/Generator/Passes/RenamePass.cs
  10. 8828
      src/Generator/Passes/verbs.txt
  11. 10
      tests/Basic/Basic.Tests.cs
  12. 9
      tests/VTables/VTables.Tests.cs

2
src/AST/Class.cs

@ -231,7 +231,7 @@ namespace CppSharp.AST @@ -231,7 +231,7 @@ namespace CppSharp.AST
public Property GetRootBaseProperty(Property @override, bool onlyFirstBase = false)
{
return (from @base in Bases
where !onlyFirstBase || !@base.Class.IsInterface
where (!onlyFirstBase || !@base.Class.IsInterface) && @base.IsClass
let baseProperty = (
from property in @base.Class.Properties
where

36
src/AST/Property.cs

@ -28,6 +28,42 @@ namespace CppSharp.AST @@ -28,6 +28,42 @@ namespace CppSharp.AST
public QualifiedType QualifiedType { get; set; }
public bool IsStatic
{
get
{
return (GetMethod != null && GetMethod.IsStatic) ||
(SetMethod != null && SetMethod.IsStatic);
}
}
public bool IsPure
{
get
{
return (GetMethod != null && GetMethod.IsPure) ||
(SetMethod != null && SetMethod.IsPure);
}
}
public bool IsVirtual
{
get
{
return (GetMethod != null && GetMethod.IsVirtual) ||
(SetMethod != null && SetMethod.IsVirtual);
}
}
public bool IsOverride
{
get
{
return (GetMethod != null && GetMethod.IsOverride) ||
(SetMethod != null && SetMethod.IsOverride);
}
}
public Method GetMethod { get; set; }
public Method SetMethod { get; set; }

1
src/Generator/Driver.cs

@ -158,6 +158,7 @@ namespace CppSharp @@ -158,6 +158,7 @@ namespace CppSharp
TranslationUnitPasses.AddPass(new MultipleInheritancePass());
TranslationUnitPasses.AddPass(new ParamTypeToInterfacePass());
}
TranslationUnitPasses.AddPass(new GetterSetterToPropertyPass());
}
public void ProcessCode()

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

@ -784,7 +784,6 @@ namespace CppSharp.Generators.CSharp @@ -784,7 +784,6 @@ namespace CppSharp.Generators.CSharp
{
PushBlock(CSharpBlockKind.Method);
WriteLine("set");
WriteStartBraceIndent();
var param = new Parameter
{
@ -801,7 +800,14 @@ namespace CppSharp.Generators.CSharp @@ -801,7 +800,14 @@ namespace CppSharp.Generators.CSharp
if (decl is Function)
{
var function = decl as Function;
if (function.IsPure && Driver.Options.GenerateAbstractImpls)
{
Write("; ");
PopBlock(NewLineKind.BeforeNextBlock);
return;
}
WriteStartBraceIndent();
if (function.Parameters.Count == 0)
throw new NotSupportedException("Expected at least one parameter in setter");
@ -809,6 +815,14 @@ namespace CppSharp.Generators.CSharp @@ -809,6 +815,14 @@ namespace CppSharp.Generators.CSharp
var method = function as Method;
if (method != null && method.OperatorKind == CXXOperatorKind.Subscript)
{
if (method.IsOverride && method.IsSynthetized)
{
GenerateVirtualTableFunctionCall(function, @class);
}
else
{
if (method.OperatorKind == CXXOperatorKind.Subscript)
{
GenerateIndexerSetter(returnType, method);
}
@ -818,8 +832,16 @@ namespace CppSharp.Generators.CSharp @@ -818,8 +832,16 @@ namespace CppSharp.Generators.CSharp
GenerateInternalFunctionCall(function, parameters);
}
}
}
else
{
var parameters = new List<Parameter> { param };
GenerateInternalFunctionCall(function, parameters);
}
}
else if (decl is Field)
{
WriteStartBraceIndent();
var field = decl as Field;
WriteLine("var {0} = (Internal*){1}.ToPointer();",
@ -865,12 +887,26 @@ namespace CppSharp.Generators.CSharp @@ -865,12 +887,26 @@ namespace CppSharp.Generators.CSharp
where T : Declaration, ITypedDecl
{
PushBlock(CSharpBlockKind.Method);
WriteLine("get");
WriteStartBraceIndent();
Write("get");
if (decl is Function)
{
var function = decl as Function;
if (function.IsPure && Driver.Options.GenerateAbstractImpls)
{
Write("; ");
PopBlock(NewLineKind.BeforeNextBlock);
return;
}
WriteLine("");
WriteStartBraceIndent();
Method method = function as Method;
if (method != null && method.IsOverride && method.IsSynthetized)
{
GenerateVirtualTableFunctionCall(function, @class);
}
else
{
bool isPrimitiveIndexer = function.OperatorKind == CXXOperatorKind.Subscript &&
function.ReturnType.Type.IsPointerToPrimitiveType();
if (isPrimitiveIndexer)
@ -879,8 +915,11 @@ namespace CppSharp.Generators.CSharp @@ -879,8 +915,11 @@ namespace CppSharp.Generators.CSharp
if (isPrimitiveIndexer)
TypePrinter.PopContext();
}
}
else if (decl is Field)
{
WriteLine("");
WriteStartBraceIndent();
var field = decl as Field;
WriteLine("var {0} = (Internal*){1}.ToPointer();",
@ -905,6 +944,8 @@ namespace CppSharp.Generators.CSharp @@ -905,6 +944,8 @@ namespace CppSharp.Generators.CSharp
}
else if (decl is Variable)
{
WriteLine("");
WriteStartBraceIndent();
var @var = decl as Variable;
var libSymbol = GetDeclarationLibrarySymbol(@var);
@ -990,10 +1031,22 @@ namespace CppSharp.Generators.CSharp @@ -990,10 +1031,22 @@ namespace CppSharp.Generators.CSharp
type = ((PointerType) prop.Type).Pointee;
if (prop.ExplicitInterfaceImpl == null)
WriteLine("{0}{1} {2}", Helpers.GetAccess(prop.Access),
type, GetPropertyName(prop));
{
Write(Helpers.GetAccess(prop.Access));
if (prop.IsStatic)
Write("static ");
if (prop.IsOverride)
Write("override ");
else if (prop.IsPure && Driver.Options.GenerateAbstractImpls)
Write("abstract ");
else if (prop.IsVirtual)
Write("virtual ");
WriteLine("{0} {1}", type, GetPropertyName(prop));
}
else
{
WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop));
}
WriteStartBraceIndent();
if (prop.Field != null)
@ -1044,7 +1097,9 @@ namespace CppSharp.Generators.CSharp @@ -1044,7 +1097,9 @@ namespace CppSharp.Generators.CSharp
public void GenerateVTable(Class @class)
{
var entries = VTables.GatherVTableMethodEntries(@class);
entries = entries.Where(entry => !entry.Method.Ignore).ToList();
entries = entries.Where(e => !e.Method.Ignore ||
@class.Properties.Any(p => !p.Ignore &&
(p.GetMethod == e.Method || p.SetMethod == e.Method))).ToList();
if (entries.Count == 0)
return;
@ -1203,7 +1258,16 @@ namespace CppSharp.Generators.CSharp @@ -1203,7 +1258,16 @@ namespace CppSharp.Generators.CSharp
if (hasReturn)
Write("var _ret = ");
if (method.IsGenerated)
{
WriteLine("target.{0}({1});", SafeIdentifier(method.Name), string.Join(", ", marshals));
}
else
{
var name = ((Class) method.Namespace).Properties.First(
p => p.GetMethod == method || p.SetMethod == method).Name;
WriteLine("target.{0};", name);
}
// TODO: Handle hidden structure return types.

2
src/Generator/Passes/FindSymbolsPass.cs

@ -12,7 +12,7 @@ namespace CppSharp.Passes @@ -12,7 +12,7 @@ namespace CppSharp.Passes
var mangledDecl = decl as IMangledDecl;
var method = decl as Method;
if (mangledDecl != null && !(method != null && method.IsPure) &&
if (mangledDecl != null && !(method != null && (method.IsPure || method.IsSynthetized)) &&
!VisitMangledDeclaration(mangledDecl))
{
decl.ExplicityIgnored = true;

5
src/Generator/Passes/GenerateAbstractImplementationsPass.cs

@ -57,7 +57,8 @@ namespace CppSharp.Passes @@ -57,7 +57,8 @@ namespace CppSharp.Passes
{
Name = abstractMethod.Name + "Delegate",
QualifiedType = abstractMethod.GetFunctionType(),
IgnoreFlags = abstractMethod.IgnoreFlags
IgnoreFlags = abstractMethod.IgnoreFlags,
Namespace = internalImpl
};
internalImpl.Typedefs.Add(@delegate);
}
@ -79,7 +80,7 @@ namespace CppSharp.Passes @@ -79,7 +80,7 @@ namespace CppSharp.Passes
var internalImpl = new Class
{
Name = @class.Name + "Internal",
Access = AccessSpecifier.Private,
Access = @class.Access,
Namespace = @class.Namespace
};
var @base = new BaseClassSpecifier { Type = new TagType(@class) };

239
src/Generator/Passes/GetterSetterToPropertyPass.cs

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CppSharp.AST;
namespace CppSharp.Passes
@ -10,6 +13,12 @@ namespace CppSharp.Passes @@ -10,6 +13,12 @@ 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;
@ -21,123 +30,169 @@ namespace CppSharp.Passes @@ -21,123 +30,169 @@ 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());
}
}
static bool IsSetter(Function method)
public override bool VisitTranslationUnit(TranslationUnit unit)
{
var isRetVoid = method.ReturnType.Type.IsPrimitiveType(
PrimitiveType.Void);
var isSetter = method.OriginalName.StartsWith("set",
StringComparison.InvariantCultureIgnoreCase);
return isRetVoid && isSetter && method.Parameters.Count == 1;
bool result = base.VisitTranslationUnit(unit);
GenerateProperties();
return result;
}
static bool IsGetter(Function method)
public override bool VisitMethodDecl(Method method)
{
var isRetVoid = method.ReturnType.Type.IsPrimitiveType(
PrimitiveType.Void);
var isGetter = method.OriginalName.StartsWith("get",
StringComparison.InvariantCultureIgnoreCase);
return !isRetVoid && isGetter && method.Parameters.Count == 0;
if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator &&
!method.Ignore)
DistributeMethod(method);
return base.VisitMethodDecl(method);
}
Property GetOrCreateProperty(Class @class, string name, QualifiedType type)
public void GenerateProperties()
{
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
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)
{
Name = name,
Namespace = @class,
QualifiedType = type
};
@class.Properties.Add(prop);
return prop;
// Make it a read-only property
GenerateProperty(getter.Namespace, getter);
}
}
public override bool VisitMethodDecl(Method method)
private void GenerateProperties(IEnumerable<Method> settersToUse, bool readOnly)
{
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))
foreach (var group in settersToUse.GroupBy(m => m.Namespace))
{
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;
foreach (var setter in group)
{
Class type = (Class) setter.Namespace;
string afterSet = setter.Name.Substring(3);
foreach (var getter in nonSetters.Where(m => m.Namespace == type))
{
if (string.Compare(getter.Name, afterSet, StringComparison.OrdinalIgnoreCase) == 0 &&
getter.ReturnType == setter.Parameters[0].QualifiedType &&
!type.Methods.Any(
m =>
m != getter &&
string.Compare(getter.Name, m.Name, StringComparison.OrdinalIgnoreCase) == 0))
{
GenerateProperty(getter.Namespace, getter, readOnly ? null : setter);
goto next;
}
if (IsSetter(method) && IsValidSetter(method))
}
Property baseVirtualProperty = type.GetRootBaseProperty(new Property { Name = afterSet });
if (!type.IsInterface && baseVirtualProperty != null)
{
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;
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 = nonSetter.Name;
if (GetFirstWord(name) == "get")
name = name.Substring(3);
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);
}
}
}
}
// Ignore the original method now that we know it is a setter.
method.IsGenerated = false;
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 = getter.Name.Substring(GetFirstWord(getter.Name) == "get" ? 3 : 0);
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;
}
// TODO: add comments
type.Properties.Add(property);
getter.IsGenerated = false;
if (setter != null)
setter.IsGenerated = false;
}
}
Driver.Diagnostics.EmitMessage(DiagnosticId.PropertySynthetized,
"Setter created: {0}::{1}", @class.Name, name);
return false;
private void DistributeMethod(Method method)
{
if (GetFirstWord(method.Name) == "set" &&
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);
}
return false;
}
// Check if a matching getter exist or no other setter exists.
private bool IsValidSetter(Method method)
private bool IsGetter(Method method)
{
var @class = method.Namespace as Class;
var name = method.Name.Substring("set".Length);
if (method.Parameters.Count == 0)
if (method.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void) ||
method.Parameters.Count > 0 || method.IsDestructor)
return false;
var result = GetFirstWord(method.Name);
return result == "get" || result == "is" || result == "has" ||
(result != "to" && result != "new" && !verbs.Contains(result));
}
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;
private static string GetFirstWord(string name)
{
List<char> firstVerb = new List<char>
{
char.ToLowerInvariant(name[0])
};
firstVerb.AddRange(name.Skip(1).TakeWhile(char.IsLower));
return new string(firstVerb.ToArray());
}
}
}

12
src/Generator/Passes/MultipleInheritancePass.cs

@ -66,9 +66,15 @@ namespace CppSharp.Passes @@ -66,9 +66,15 @@ namespace CppSharp.Passes
let i = GetInterface(@base, b.Class)
select new BaseClassSpecifier { Type = new TagType(i) });
@interface.Methods.AddRange(@base.Methods.Where(
m => !m.IsConstructor && !m.IsDestructor && !m.IsStatic && !m.Ignore));
@interface.Properties.AddRange(@base.Properties.Where(p => !p.Ignore));
@interface.Methods.AddRange(
from m in @base.Methods
where !m.IsConstructor && !m.IsDestructor && !m.IsStatic && !m.Ignore
select new Method(m) { Namespace = @interface });
@interface.Properties.AddRange(
from property in @base.Properties
where !property.Ignore
select new Property(property) { Namespace = @interface });
if (@interface.Bases.Count == 0)
{

12
src/Generator/Passes/RenamePass.cs

@ -39,6 +39,7 @@ namespace CppSharp.Passes @@ -39,6 +39,7 @@ namespace CppSharp.Passes
if (decl is Enumeration) return true;
if (decl is Property) return true;
if (decl is Event) return true;
if (decl is TypedefDecl) return true;
return false;
}
@ -125,6 +126,14 @@ namespace CppSharp.Passes @@ -125,6 +126,14 @@ namespace CppSharp.Passes
return base.VisitProperty(property);
}
public override bool VisitTypedefDecl(TypedefDecl typedef)
{
if (!Targets.HasFlag(RenameTargets.Delegate))
return false;
return base.VisitTypedefDecl(typedef);
}
public override bool VisitMethodDecl(Method method)
{
if (!Targets.HasFlag(RenameTargets.Method))
@ -173,7 +182,8 @@ namespace CppSharp.Passes @@ -173,7 +182,8 @@ namespace CppSharp.Passes
EnumItem = 1 << 6,
Event = 1 << 7,
Property = 1 << 8,
Any = Function | Method | Parameter | Class | Field | Enum | EnumItem | Event | Property,
Delegate = 1 << 9,
Any = Function | Method | Parameter | Class | Field | Enum | EnumItem | Event | Property | Delegate,
}
/// <summary>

8828
src/Generator/Passes/verbs.txt

File diff suppressed because it is too large Load Diff

10
tests/Basic/Basic.Tests.cs

@ -112,17 +112,17 @@ public class BasicTests @@ -112,17 +112,17 @@ public class BasicTests
public void TestAbstractReturnType()
{
var returnsAbstractFoo = new ReturnsAbstractFoo();
var abstractFoo = returnsAbstractFoo.getFoo();
Assert.AreEqual(abstractFoo.pureFunction(), 5);
Assert.AreEqual(abstractFoo.pureFunction1(), 10);
Assert.AreEqual(abstractFoo.pureFunction2(), 15);
var abstractFoo = returnsAbstractFoo.Foo;
Assert.AreEqual(abstractFoo.pureFunction, 5);
Assert.AreEqual(abstractFoo.pureFunction1, 10);
Assert.AreEqual(abstractFoo.pureFunction2, 15);
}
[Test]
public void TestANSI()
{
Foo foo = new Foo();
Assert.That(foo.GetANSI(), Is.EqualTo("ANSI"));
Assert.That(foo.ANSI, Is.EqualTo("ANSI"));
}
}

9
tests/VTables/VTables.Tests.cs

@ -4,11 +4,14 @@ using VTables; @@ -4,11 +4,14 @@ using VTables;
public class FooDerived : Foo
{
public override int vfoo()
public override int vfoo
{
get
{
Console.WriteLine("Hello from FooDerived");
return 10;
}
}
}
[TestFixture]
@ -18,8 +21,8 @@ public class VTablesTests @@ -18,8 +21,8 @@ public class VTablesTests
public void TestFoo()
{
var foo = new Foo();
Assert.That(foo.vfoo(), Is.EqualTo(5));
Assert.That(foo.Vbar(), Is.EqualTo(3));
Assert.That(foo.vfoo, Is.EqualTo(5));
Assert.That(foo.Vbar, Is.EqualTo(3));
Assert.That(foo.CallFoo(), Is.EqualTo(7));
var foo2 = new FooDerived();

Loading…
Cancel
Save