From 54c93f4d91234b3522c69db1b8489f493d3fe25d Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 13 Sep 2013 19:22:11 +0300 Subject: [PATCH 1/6] Added support for read-only [] operators. Signed-off-by: Dimitar Dobrev --- src/AST/Property.cs | 12 ++++ .../Generators/CSharp/CSharpTextTemplate.cs | 37 +++++------- .../Passes/CheckOperatorsOverloads.cs | 60 +++++++++++++------ 3 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/AST/Property.cs b/src/AST/Property.cs index 0514d111..b3394aa8 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace CppSharp.AST { /// @@ -36,6 +38,16 @@ namespace CppSharp.AST // The field that should be get and set by this property public Field Field { get; set; } + private readonly List parameters = new List(); + + /// + /// Only applicable to index ([]) properties. + /// + public List Parameters + { + get { return parameters; } + } + public override T Visit(IDeclVisitor visitor) { return visitor.VisitProperty(this); diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 7a2588e0..e40f902e 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -893,14 +893,12 @@ namespace CppSharp.Generators.CSharp private void GenerateClassProperties(Class @class) { - foreach (var prop in @class.Properties) + foreach (var prop in @class.Properties.Where(p => !p.Ignore)) { - if (prop.Ignore) continue; - PushBlock(CSharpBlockKind.Property); WriteLine("{0} {1} {2}", prop.Access == AccessSpecifier.Public ? "public" : "protected", - prop.Type, SafeIdentifier(prop.Name)); + prop.Type, GetPropertyName(prop)); WriteStartBraceIndent(); if (prop.Field != null) @@ -925,6 +923,12 @@ namespace CppSharp.Generators.CSharp } } + private string GetPropertyName(Property prop) + { + return prop.Parameters.Count == 0 ? SafeIdentifier(prop.Name) + : string.Format("this[{0}]", FormatMethodParameters(prop.Parameters)); + } + private void GenerateVariable(Class @class, Type type, Variable variable) { PushBlock(CSharpBlockKind.Variable); @@ -1469,7 +1473,7 @@ namespace CppSharp.Generators.CSharp var functionName = GetFunctionIdentifier(function); Write("public static {0} {1}(", function.OriginalReturnType, functionName); - GenerateMethodParameters(function); + Write(FormatMethodParameters(function.Parameters)); WriteLine(")"); WriteStartBraceIndent(); @@ -1517,7 +1521,7 @@ namespace CppSharp.Generators.CSharp else Write("{0} {1}(", method.OriginalReturnType, functionName); - GenerateMethodParameters(method); + Write(FormatMethodParameters(method.Parameters)); Write(")"); @@ -1938,23 +1942,14 @@ namespace CppSharp.Generators.CSharp } } - private void GenerateMethodParameters(Function function) + private string FormatMethodParameters(IEnumerable @params) { - var @params = new List(); - - for (var i = 0; i < function.Parameters.Count; ++i) - { - var param = function.Parameters[i]; - - if (param.Kind == ParameterKind.IndirectReturnType) - continue; - - var typeName = param.CSharpType(TypePrinter); - @params.Add(string.Format("{0}{1} {2}", GetParameterUsage(param.Usage), + return string.Join(", ", + from param in @params + where param.Kind != ParameterKind.IndirectReturnType + let typeName = param.CSharpType(this.TypePrinter) + select string.Format("{0}{1} {2}", GetParameterUsage(param.Usage), typeName, SafeIdentifier(param.Name))); - } - - Write(string.Join(", ", @params)); } #endregion diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index 0e75f235..96769a05 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -57,22 +57,48 @@ namespace CppSharp.Passes if (@operator.SynthKind == FunctionSynthKind.NonMemberOperator) continue; - // Handle missing operator parameters - if (@operator.IsStatic) - @operator.Parameters = @operator.Parameters.Skip(1).ToList(); - - var type = new PointerType() + if (@operator.OperatorKind == CXXOperatorKind.Subscript) { - QualifiedPointee = new QualifiedType(new TagType(@class)), - Modifier = PointerType.TypeModifier.LVReference - }; - - @operator.Parameters.Insert(0, new Parameter + CreateIndexer(@class, @operator); + } + else { - Name = Generator.GeneratedIdentifier("op"), - QualifiedType = new QualifiedType(type), - Kind = ParameterKind.OperatorParameter - }); + // Handle missing operator parameters + if (@operator.IsStatic) + @operator.Parameters = @operator.Parameters.Skip(1).ToList(); + + var type = new PointerType() + { + QualifiedPointee = new QualifiedType(new TagType(@class)), + Modifier = PointerType.TypeModifier.LVReference + }; + + @operator.Parameters.Insert(0, new Parameter + { + Name = Generator.GeneratedIdentifier("op"), + QualifiedType = new QualifiedType(type), + Kind = ParameterKind.OperatorParameter + }); + } + } + } + + private static void CreateIndexer(Class @class, Method @operator) + { + if (@class.Properties.All(p => p.Parameters.Count == 0 || + p.Parameters[0].QualifiedType != @operator.Parameters[0].QualifiedType)) + { + Property property = new Property + { + Name = "Item", + QualifiedType = @operator.ReturnType, + Access = @operator.Access, + Namespace = @class + }; + property.GetMethod = @operator; + property.Parameters.AddRange(@operator.Parameters); + @class.Properties.Add(property); + @operator.IsGenerated = false; } } @@ -153,6 +179,9 @@ namespace CppSharp.Passes case CXXOperatorKind.Pipe: case CXXOperatorKind.Caret: + // The array indexing operator can be overloaded + case CXXOperatorKind.Subscript: + // The comparison operators can be overloaded case CXXOperatorKind.EqualEqual: case CXXOperatorKind.ExclaimEqual: @@ -180,9 +209,6 @@ namespace CppSharp.Passes case CXXOperatorKind.LessLessEqual: case CXXOperatorKind.GreaterGreaterEqual: - // The array indexing operator cannot be overloaded - case CXXOperatorKind.Subscript: - // The conditional logical operators cannot be overloaded case CXXOperatorKind.AmpAmp: case CXXOperatorKind.PipePipe: From 6cb37ef382aa294609e0a6cc7b71d51903cbcffd Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 13 Sep 2013 19:42:17 +0300 Subject: [PATCH 2/6] Added a test for indexers ([] operators). Signed-off-by: Dimitar Dobrev --- tests/UTF16/UTF16.Tests.cs | 9 +++++++-- tests/UTF16/UTF16.cpp | 13 ++++++++++++- tests/UTF16/UTF16.h | 4 ++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/UTF16/UTF16.Tests.cs b/tests/UTF16/UTF16.Tests.cs index 0a4599b6..c9f1a2ed 100644 --- a/tests/UTF16/UTF16.Tests.cs +++ b/tests/UTF16/UTF16.Tests.cs @@ -1,5 +1,6 @@ -using NUnit.Framework; -using UTF16; +using System; +using NUnit.Framework; +using Foo = UTF16.Foo; [TestFixture] public class UTF16Tests @@ -11,5 +12,9 @@ public class UTF16Tests const string georgia = "საქართველო"; foo.Unicode = georgia; Assert.That(foo.Unicode, Is.EqualTo(georgia)); + + // TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch + Assert.That(foo[0], Is.EqualTo(5)); + Assert.That(foo.OperatorSubscript(0), Is.EqualTo(IntPtr.Zero)); } } diff --git a/tests/UTF16/UTF16.cpp b/tests/UTF16/UTF16.cpp index 566968cf..ed65b3d8 100644 --- a/tests/UTF16/UTF16.cpp +++ b/tests/UTF16/UTF16.cpp @@ -1 +1,12 @@ -#include "UTF16.h" \ No newline at end of file +#include "UTF16.h" + +// TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch +int Foo::operator[](int i) const +{ + return 5; +} + +char* Foo::operator[](int i) +{ + return 0; +} \ No newline at end of file diff --git a/tests/UTF16/UTF16.h b/tests/UTF16/UTF16.h index 13da5e79..16f29e28 100644 --- a/tests/UTF16/UTF16.h +++ b/tests/UTF16/UTF16.h @@ -8,6 +8,10 @@ class DLL_API Foo { public: const char* Unicode; + + // TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch + int operator[](int i) const; + char* operator[](int i); }; DLL_API int FooCallFoo(Foo* foo); From 083dc3a9f8d897084515f97543ad5f6a0ec1a0a6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 13 Sep 2013 21:35:10 +0300 Subject: [PATCH 3/6] Improved the generation of indexers by removing const overloads of []. NOTE: did that by removing the method type check in the pass for ambiguous overloads. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 2 +- .../Passes/CheckAmbiguousFunctions.cs | 8 ------ .../Passes/CheckOperatorsOverloads.cs | 26 ++++++++----------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index aa88269a..4e40d1eb 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -140,9 +140,9 @@ namespace CppSharp TranslationUnitPasses.AddPass(new FindSymbolsPass()); TranslationUnitPasses.AddPass(new MoveOperatorToClassPass()); + TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); TranslationUnitPasses.AddPass(new CheckOperatorsOverloadsPass()); TranslationUnitPasses.AddPass(new CheckVirtualOverrideReturnCovariance()); - TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); Generator.SetupPasses(); TranslationUnitPasses.AddPass(new FieldToPropertyPass()); diff --git a/src/Generator/Passes/CheckAmbiguousFunctions.cs b/src/Generator/Passes/CheckAmbiguousFunctions.cs index 1a4aed97..a44f642e 100644 --- a/src/Generator/Passes/CheckAmbiguousFunctions.cs +++ b/src/Generator/Passes/CheckAmbiguousFunctions.cs @@ -23,14 +23,6 @@ namespace CppSharp.Passes /// public class CheckAmbiguousFunctions : TranslationUnitPass { - public override bool VisitMethodDecl(Method method) - { - if (method.Kind != CXXMethodKind.Normal) - return false; - - return base.VisitMethodDecl(method); - } - public override bool VisitFunctionDecl(AST.Function function) { if (AlreadyVisited(function)) diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index 96769a05..8da7ae5a 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -44,7 +44,7 @@ namespace CppSharp.Passes private void CheckInvalidOperators(Class @class) { - foreach (var @operator in @class.Operators) + foreach (var @operator in @class.Operators.Where(o => !o.Ignore)) { if (!IsValidOperatorOverload(@operator)) { @@ -85,21 +85,17 @@ namespace CppSharp.Passes private static void CreateIndexer(Class @class, Method @operator) { - if (@class.Properties.All(p => p.Parameters.Count == 0 || - p.Parameters[0].QualifiedType != @operator.Parameters[0].QualifiedType)) + Property property = new Property { - Property property = new Property - { - Name = "Item", - QualifiedType = @operator.ReturnType, - Access = @operator.Access, - Namespace = @class - }; - property.GetMethod = @operator; - property.Parameters.AddRange(@operator.Parameters); - @class.Properties.Add(property); - @operator.IsGenerated = false; - } + Name = "Item", + QualifiedType = @operator.ReturnType, + Access = @operator.Access, + Namespace = @class, + GetMethod = @operator + }; + property.Parameters.AddRange(@operator.Parameters); + @class.Properties.Add(property); + @operator.IsGenerated = false; } static void HandleMissingOperatorOverloadPair(Class @class, CXXOperatorKind op1, From 7dde8df3e15e484e975bfa262b75b383321a2776 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 13 Sep 2013 21:40:08 +0300 Subject: [PATCH 4/6] Generated the inlines only when using the C# back-end because the CLI one does not need them. Signed-off-by: Dimitar Dobrev --- src/Generator/Driver.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 4e40d1eb..92e3b6a7 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -134,7 +134,8 @@ namespace CppSharp TranslationUnitPasses.AddPass(new ResolveIncompleteDeclsPass()); TranslationUnitPasses.AddPass(new CleanInvalidDeclNamesPass()); TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); - TranslationUnitPasses.AddPass(new GenerateInlinesCodePass()); + if (Options.IsCSharpGenerator) + TranslationUnitPasses.AddPass(new GenerateInlinesCodePass()); library.SetupPasses(this); From 39bc4da0e96083ef490b7da98b686c498c15dba8 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 13 Sep 2013 21:51:59 +0300 Subject: [PATCH 5/6] Fixed the test for indexers. Signed-off-by: Dimitar Dobrev --- tests/UTF16/UTF16.Tests.cs | 1 - tests/UTF16/UTF16.cpp | 4 ++-- tests/UTF16/UTF16.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/UTF16/UTF16.Tests.cs b/tests/UTF16/UTF16.Tests.cs index c9f1a2ed..8f5221bf 100644 --- a/tests/UTF16/UTF16.Tests.cs +++ b/tests/UTF16/UTF16.Tests.cs @@ -15,6 +15,5 @@ public class UTF16Tests // TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch Assert.That(foo[0], Is.EqualTo(5)); - Assert.That(foo.OperatorSubscript(0), Is.EqualTo(IntPtr.Zero)); } } diff --git a/tests/UTF16/UTF16.cpp b/tests/UTF16/UTF16.cpp index ed65b3d8..1e7ee0ca 100644 --- a/tests/UTF16/UTF16.cpp +++ b/tests/UTF16/UTF16.cpp @@ -6,7 +6,7 @@ int Foo::operator[](int i) const return 5; } -char* Foo::operator[](int i) +int Foo::operator[](int i) { - return 0; + return 5; } \ No newline at end of file diff --git a/tests/UTF16/UTF16.h b/tests/UTF16/UTF16.h index 16f29e28..6893aabc 100644 --- a/tests/UTF16/UTF16.h +++ b/tests/UTF16/UTF16.h @@ -11,7 +11,7 @@ public: // TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch int operator[](int i) const; - char* operator[](int i); + int operator[](int i); }; DLL_API int FooCallFoo(Foo* foo); From cf58c70d238fe7a42e2d20b73567efeefacda8e6 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Sat, 14 Sep 2013 00:16:55 +0300 Subject: [PATCH 6/6] Moved all ignored tests for the C# back-end to a temporary test project and enabled them. Signed-off-by: Dimitar Dobrev --- tests/Basic/Basic.Tests.cs | 7 ------- tests/Basic/Basic.h | 4 ---- tests/CSharpTemp/CSharpTemp.Tests.cs | 21 +++++++++++++++++++++ tests/CSharpTemp/CSharpTemp.cpp | 12 ++++++++++++ tests/CSharpTemp/CSharpTemp.cs | 22 ++++++++++++++++++++++ tests/CSharpTemp/CSharpTemp.h | 15 +++++++++++++++ tests/CSharpTemp/premake4.lua | 2 ++ 7 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 tests/CSharpTemp/CSharpTemp.Tests.cs create mode 100644 tests/CSharpTemp/CSharpTemp.cpp create mode 100644 tests/CSharpTemp/CSharpTemp.cs create mode 100644 tests/CSharpTemp/CSharpTemp.h create mode 100644 tests/CSharpTemp/premake4.lua diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index 846d4d99..395b8bb1 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -117,13 +117,6 @@ public class BasicTests Assert.AreEqual(abstractFoo.pureFunction2(), 15); } - [Test, Ignore] - public void TestPropertyAccessModifier() - { - Assert.That(typeof(Foo2).GetProperty("P", - BindingFlags.Instance | BindingFlags.NonPublic), Is.Not.Null); - } - [Test] public void TestANSI() { diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 0dd190d2..b020aece 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -29,10 +29,6 @@ public: Foo2 operator<<(signed int i); Foo2 operator<<(signed long l); - - // TODO: uncomment when the C++/CLI back-end supports protected members -//protected: -// int P; }; struct DLL_API Bar diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs new file mode 100644 index 00000000..afce2c5b --- /dev/null +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -0,0 +1,21 @@ +using System.Reflection; +using NUnit.Framework; +using Foo = CSharpTemp.Foo; + +[TestFixture] +public class CSharpTempTests +{ + [Test] + public void TestIndexer() + { + var foo = new Foo(); + Assert.That(foo[0], Is.EqualTo(5)); + } + + [Test] + public void TestPropertyAccessModifier() + { + Assert.That(typeof(Foo).GetProperty("P", + BindingFlags.Instance | BindingFlags.NonPublic), Is.Not.Null); + } +} \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp new file mode 100644 index 00000000..a90f00e1 --- /dev/null +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -0,0 +1,12 @@ +#include "CSharpTemp.h" + +// TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch +int Foo::operator[](int i) const +{ + return 5; +} + +int Foo::operator[](int i) +{ + return 5; +} \ No newline at end of file diff --git a/tests/CSharpTemp/CSharpTemp.cs b/tests/CSharpTemp/CSharpTemp.cs new file mode 100644 index 00000000..30bc0ef6 --- /dev/null +++ b/tests/CSharpTemp/CSharpTemp.cs @@ -0,0 +1,22 @@ +using CppSharp.Generators; +using CppSharp.Utils; + +namespace CppSharp.Tests +{ + public class CSharpTempTests : LibraryTest + { + public CSharpTempTests(LanguageGeneratorKind kind) + : base("CSharpTemp", kind) + { + } + + static class Program + { + public static void Main(string[] args) + { + ConsoleDriver.Run(new CSharpTempTests(LanguageGeneratorKind.CSharp)); + } + } + } +} + diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h new file mode 100644 index 00000000..62f00aa1 --- /dev/null +++ b/tests/CSharpTemp/CSharpTemp.h @@ -0,0 +1,15 @@ +#if defined(_MSC_VER) +#define DLL_API __declspec(dllexport) +#else +#define DLL_API +#endif + +class DLL_API Foo +{ +public: + int operator[](int i) const; + int operator[](int i); + +protected: + int P; +}; diff --git a/tests/CSharpTemp/premake4.lua b/tests/CSharpTemp/premake4.lua new file mode 100644 index 00000000..51423dbe --- /dev/null +++ b/tests/CSharpTemp/premake4.lua @@ -0,0 +1,2 @@ +group "Tests/CSharpTemp" + SetupTestCSharp("CSharpTemp") \ No newline at end of file