From 7478c5329edd59d8efed1a08911d17b8d75966b8 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Sun, 4 May 2014 21:19:27 +0200 Subject: [PATCH 1/5] Cleaned up tests regarding indexed properties. --- tests/Basic/Basic.Tests.cs | 10 +++++----- tests/Basic/Basic.h | 21 +++++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index e9fd6e1d..cdec7817 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -238,11 +238,11 @@ public class BasicTests : GeneratorTestFixture [Test] public unsafe void TestIndexers() { - var someStruct = new SomeStruct(); - Assert.That(someStruct[0], Is.EqualTo(1)); - Assert.That(someStruct["foo"], Is.EqualTo(1)); - someStruct[0] = 2; - Assert.That(someStruct[0], Is.EqualTo(2)); + var properties = new TestIndexedProperties(); + Assert.AreEqual(1, properties[0]); + Assert.AreEqual(1, properties["foo"]); + properties[0] = 2; + Assert.AreEqual(2, properties[0]); } [Test] diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 3a60dcfd..574dd5a7 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -336,17 +336,11 @@ typedef unsigned long foo_t; typedef struct DLL_API SomeStruct { SomeStruct(); - foo_t& operator[](int i); - // CSharp backend can't deal with a setter here - foo_t operator[](const char* name); foo_t p; } SomeStruct; SomeStruct::SomeStruct() : p(1) {} -foo_t& SomeStruct::operator[](int i) { return p; } -foo_t SomeStruct::operator[](const char* name) { return p; } - class DLL_API SomeClassExtendingTheStruct : public SomeStruct { }; @@ -396,6 +390,21 @@ TestProperties::TestProperties() : Field(0) {} int TestProperties::getFieldValue() { return Field; } void TestProperties::setFieldValue(int Value) { Field = Value; } +class DLL_API TestIndexedProperties +{ + foo_t p; +public: + TestIndexedProperties(); + // Should lead to a read/write indexer with return type uint + foo_t& operator[](int i); + // Should lead to a read-only indexer with return type uint + foo_t operator[](const char* name); +}; + +TestIndexedProperties::TestIndexedProperties() : p(1) {} +foo_t& TestIndexedProperties::operator[](int i) { return p; } +foo_t TestIndexedProperties::operator[](const char* name) { return p; } + enum struct MyEnum { A, B, C }; class DLL_API TestArraysPointers From 0c260bd223f92562591147590b7660c0d3f33793 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Mon, 5 May 2014 05:14:28 +0200 Subject: [PATCH 2/5] Improved support for indexed properties. More types and types with different qualifiers are supported now - in both backends. See test cases for details. --- src/Generator/Generators/CLI/CLIMarshal.cs | 30 +++++++-- .../Generators/CLI/CLISourcesTemplate.cs | 62 +++++++++++------ .../Generators/CSharp/CSharpTextTemplate.cs | 66 ++++++++----------- .../Passes/CheckOperatorsOverloads.cs | 27 ++++---- src/Generator/Types/CppTypePrinter.cs | 6 +- tests/Basic/Basic.Tests.cs | 22 +++++-- tests/Basic/Basic.h | 15 ++++- 7 files changed, 143 insertions(+), 85 deletions(-) diff --git a/src/Generator/Generators/CLI/CLIMarshal.cs b/src/Generator/Generators/CLI/CLIMarshal.cs index d9b6dd7d..eee6af54 100644 --- a/src/Generator/Generators/CLI/CLIMarshal.cs +++ b/src/Generator/Generators/CLI/CLIMarshal.cs @@ -93,11 +93,31 @@ namespace CppSharp.Generators.CLI { var returnVarName = Context.ReturnVarName; if (quals.IsConst != Context.ReturnType.Qualifiers.IsConst) - returnVarName = string.Format("const_cast<{0}>({1})", - Context.ReturnType, Context.ReturnVarName); - if (pointer.Pointee is TypedefType) - Context.Return.Write("reinterpret_cast<{0}>({1})", pointer, - returnVarName); + { + var nativeTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase, false); + var constlessPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(Context.ReturnType.Type.GetPointee()) + }; + var nativeConstlessTypeName = constlessPointer.Visit(nativeTypePrinter, new TypeQualifiers()); + returnVarName = string.Format("const_cast<{0}>({1})", + nativeConstlessTypeName, Context.ReturnVarName); + } + if (pointer.Pointee is TypedefType) + { + var desugaredPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(pointee) + }; + var nativeTypePrinter = new CppTypePrinter(Context.Driver.TypeDatabase); + var nativeTypeName = desugaredPointer.Visit(nativeTypePrinter, quals); + Context.Return.Write("reinterpret_cast<{0}>({1})", nativeTypeName, + returnVarName); + } else Context.Return.Write(returnVarName); return true; diff --git a/src/Generator/Generators/CLI/CLISourcesTemplate.cs b/src/Generator/Generators/CLI/CLISourcesTemplate.cs index 6215dfdd..3ad35f77 100644 --- a/src/Generator/Generators/CLI/CLISourcesTemplate.cs +++ b/src/Generator/Generators/CLI/CLISourcesTemplate.cs @@ -372,16 +372,16 @@ namespace CppSharp.Generators.CLI return; } var param = new Parameter - { - Name = "value", - QualifiedType = decl.QualifiedType - }; + { + Name = "value", + QualifiedType = new QualifiedType(type) + }; var ctx = new MarshalContext(Driver) - { - Parameter = param, - ArgName = param.Name, - }; + { + Parameter = param, + ArgName = param.Name, + }; var marshal = new CLIMarshalManagedToNativePrinter(ctx); param.Visit(marshal); @@ -400,7 +400,10 @@ namespace CppSharp.Generators.CLI if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("{0} = {1};", variable, marshal.Context.Return); + if (isIndexer && decl.Type.IsPointer()) + WriteLine("*({0}) = {1};", variable, marshal.Context.Return); + else + WriteLine("{0} = {1};", variable, marshal.Context.Return); } WriteCloseBraceIndent(); @@ -432,7 +435,10 @@ namespace CppSharp.Generators.CLI if (decl is Function) { var func = decl as Function; - GenerateFunctionCall(func, @class); + if (isIndexer && func.Type.IsAddress()) + GenerateFunctionCall(func, @class, type); + else + GenerateFunctionCall(func, @class); } else { @@ -832,11 +838,13 @@ namespace CppSharp.Generators.CLI WriteCloseBraceIndent(); } - public void GenerateFunctionCall(Function function, Class @class = null) + public void GenerateFunctionCall(Function function, Class @class = null, Type publicRetType = null) { CheckArgumentRange(function); var retType = function.ReturnType; + if (publicRetType == null) + publicRetType = retType.Type; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); const string valueMarshalName = "_this0"; @@ -861,9 +869,12 @@ namespace CppSharp.Generators.CLI var @params = GenerateFunctionParamsMarshal(function.Parameters, function); + var returnIdentifier = Generator.GeneratedIdentifier("ret"); if (needsReturn) - Write("auto {0}{1} = ",(function.ReturnType.Type.IsReference())? "&": string.Empty, - Generator.GeneratedIdentifier("ret")); + if (retType.Type.IsReference()) + Write("auto &{0} = ", returnIdentifier); + else + Write("auto {0} = ", returnIdentifier); if (function.OperatorKind == CXXOperatorKind.Conversion || function.OperatorKind == CXXOperatorKind.ExplicitConversion) @@ -944,24 +955,33 @@ namespace CppSharp.Generators.CLI if (retType.Type.IsPointer() && (isIntPtr || retTypeName.EndsWith("^"))) { WriteLine("if ({0} == nullptr) return {1};", - Generator.GeneratedIdentifier("ret"), + returnIdentifier, isIntPtr ? "System::IntPtr()" : "nullptr"); } var ctx = new MarshalContext(Driver) - { - ArgName = Generator.GeneratedIdentifier("ret"), - ReturnVarName = Generator.GeneratedIdentifier("ret"), - ReturnType = retType - }; + { + ArgName = returnIdentifier, + ReturnVarName = returnIdentifier, + ReturnType = retType + }; var marshal = new CLIMarshalNativeToManagedPrinter(ctx); - function.ReturnType.Visit(marshal); + retType.Visit(marshal); if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine("return {0};", marshal.Context.Return); + // Special case for indexer - needs to dereference if the internal + // function is a pointer type and the property is not. + if (retType.Type.IsPointer() && + retType.Type.GetPointee().Equals(publicRetType) && + publicRetType.IsPrimitiveType()) + WriteLine("return *({0});", marshal.Context.Return); + else if (retType.Type.IsReference() && publicRetType.IsReference()) + WriteLine("return ({0})({1});", publicRetType, marshal.Context.Return); + else + WriteLine("return {0};", marshal.Context.Return); } } diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index 0a7ea727..03d9e5a3 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -967,8 +967,8 @@ namespace CppSharp.Generators.CSharp private void GenerateIndexerSetter(QualifiedType returnType, Function function) { - Type type; - returnType.Type.IsPointerTo(out type); + Type type; + function.Type.IsPointerTo(out type); PrimitiveType primitiveType; string internalFunction = GetFunctionNativeIdentifier(function); if (type.IsPrimitiveType(out primitiveType)) @@ -984,7 +984,7 @@ namespace CppSharp.Generators.CSharp } } - private void GeneratePropertyGetter(T decl, Class @class) + private void GeneratePropertyGetter(QualifiedType returnType, T decl, Class @class) where T : Declaration, ITypedDecl { PushBlock(CSharpBlockKind.Method); @@ -1004,19 +1004,9 @@ namespace CppSharp.Generators.CSharp WriteStartBraceIndent(); var method = function as Method; if (method != null && method.IsOverride && method.IsSynthetized) - { GenerateVirtualTableFunctionCall(method, @class); - } else - { - bool isPrimitiveIndexer = function.OperatorKind == CXXOperatorKind.Subscript && - function.ReturnType.Type.IsPointerToPrimitiveType(); - if (isPrimitiveIndexer) - TypePrinter.PushContext(CSharpTypePrinterContextKind.PrimitiveIndexer); - GenerateInternalFunctionCall(function); - if (isPrimitiveIndexer) - TypePrinter.PopContext(); - } + GenerateInternalFunctionCall(function, function.Parameters, returnType.Type); } else if (decl is Field) { @@ -1185,12 +1175,6 @@ namespace CppSharp.Generators.CSharp PushBlock(CSharpBlockKind.Property); - // If this is an indexer that returns an address use the real type - // because there is a setter anyway. - var type = prop.Type; - if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) - type = ((PointerType) prop.Type).Pointee; - ArrayType arrayType = prop.Type as ArrayType; if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null) { @@ -1214,11 +1198,11 @@ namespace CppSharp.Generators.CSharp else if (prop.IsVirtual) Write("virtual "); - WriteLine("{0} {1}", type, GetPropertyName(prop)); + WriteLine("{0} {1}", prop.Type, GetPropertyName(prop)); } else { - WriteLine("{0} {1}.{2}", type, prop.ExplicitInterfaceImpl.Name, + WriteLine("{0} {1}.{2}", prop.Type, prop.ExplicitInterfaceImpl.Name, GetPropertyName(prop)); } WriteStartBraceIndent(); @@ -1226,7 +1210,7 @@ namespace CppSharp.Generators.CSharp if (prop.Field != null) { if (prop.HasGetter) - GeneratePropertyGetter(prop.Field, @class); + GeneratePropertyGetter(prop.QualifiedType, prop.Field, @class); if (prop.HasSetter) GeneratePropertySetter(prop.Field.QualifiedType, prop.Field, @@ -1234,13 +1218,11 @@ namespace CppSharp.Generators.CSharp } else { - if (prop.HasGetter) - GeneratePropertyGetter(prop.GetMethod, @class); + if (prop.HasGetter) + GeneratePropertyGetter(prop.QualifiedType, prop.GetMethod, @class); if (prop.HasSetter) - GeneratePropertySetter(prop.GetMethod != null ? - prop.GetMethod.ReturnType : prop.SetMethod.Parameters[0].QualifiedType, - prop.SetMethod, @class); + GeneratePropertySetter(prop.QualifiedType, prop.SetMethod, @class); } WriteCloseBraceIndent(); @@ -1260,7 +1242,7 @@ namespace CppSharp.Generators.CSharp WriteLine("public static {0} {1}", type, variable.Name); WriteStartBraceIndent(); - GeneratePropertyGetter(variable, @class); + GeneratePropertyGetter(variable.QualifiedType, variable, @class); if (!variable.QualifiedType.Qualifiers.IsConst) GeneratePropertySetter(variable.QualifiedType, variable, @class); @@ -2185,8 +2167,8 @@ namespace CppSharp.Generators.CSharp GenerateVTableClassSetupCall(@class); } - public void GenerateInternalFunctionCall(Function function, - List parameters = null) + public void GenerateInternalFunctionCall(Function function, + List parameters = null, Type returnType = null) { if (parameters == null) parameters = function.Parameters; @@ -2194,11 +2176,11 @@ namespace CppSharp.Generators.CSharp CheckArgumentRange(function); var functionName = string.Format("Internal.{0}", GetFunctionNativeIdentifier(function)); - GenerateFunctionCall(functionName, parameters, function); + GenerateFunctionCall(functionName, parameters, function, returnType); } - public void GenerateFunctionCall(string functionName, List parameters, - Function function) + public void GenerateFunctionCall(string functionName, List parameters, + Function function, Type returnType = null) { if (function.IsPure) { @@ -2206,7 +2188,9 @@ namespace CppSharp.Generators.CSharp return; } - var retType = function.OriginalReturnType; + var retType = function.OriginalReturnType; + if (returnType == null) + returnType = retType.Type; var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void); var isValueType = false; @@ -2365,10 +2349,14 @@ namespace CppSharp.Generators.CSharp if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) Write(marshal.Context.SupportBefore); - WriteLine( - TypePrinter.ContextKind == CSharpTypePrinterContextKind.PrimitiveIndexer - ? "return *{0};" - : "return {0};", marshal.Context.Return); + // Special case for indexer - needs to dereference if the internal + // function is a pointer type and the property is not. + if (retType.Type.IsAddress() && + retType.Type.GetPointee().Equals(returnType) && + returnType.IsPrimitiveType()) + WriteLine("return *{0};", marshal.Context.Return); + else + WriteLine("return {0};", marshal.Context.Return); } } diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index 980671ab..166a8cef 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -97,21 +97,24 @@ namespace CppSharp.Passes GetMethod = @operator }; - if (!@operator.ReturnType.Qualifiers.IsConst && @operator.ReturnType.Type.IsAddress()) - property.SetMethod = @operator; - + var returnType = @operator.Type; + if (returnType.IsAddress()) + { + var pointer = returnType as PointerType; + var qualifiedPointee = pointer.QualifiedPointee; + if (!qualifiedPointee.Qualifiers.IsConst) + property.SetMethod = @operator; + } + + // If we've a setter use the pointee as the type of the property. + var pointerType = property.Type as PointerType; + if (pointerType != null && property.HasSetter) + property.QualifiedType = new QualifiedType( + pointerType.Pointee, property.QualifiedType.Qualifiers); + if (Driver.Options.IsCLIGenerator) - { - // If we've a setter use the pointee as the type of the property. - var pointerType = property.Type as PointerType; - if (pointerType != null && property.HasSetter) - { - property.QualifiedType = new QualifiedType(pointerType.Pointee, property.QualifiedType.Qualifiers); - property.GetMethod.ReturnType = property.QualifiedType; - } // C++/CLI uses "default" as the indexer property name. property.Name = "default"; - } property.Parameters.AddRange(@operator.Parameters); diff --git a/src/Generator/Types/CppTypePrinter.cs b/src/Generator/Types/CppTypePrinter.cs index 9e084752..d2d2ad5d 100644 --- a/src/Generator/Types/CppTypePrinter.cs +++ b/src/Generator/Types/CppTypePrinter.cs @@ -17,10 +17,12 @@ namespace CppSharp.Types { public CppTypePrintScopeKind PrintScopeKind; public bool PrintLogicalNames; + public bool PrintTypeQualifiers; - public CppTypePrinter(ITypeMapDatabase database) + public CppTypePrinter(ITypeMapDatabase database, bool printTypeQualifiers = true) { PrintScopeKind = CppTypePrintScopeKind.GlobalQualified; + PrintTypeQualifiers = printTypeQualifiers; } public string VisitTagType(TagType tag, TypeQualifiers quals) @@ -78,7 +80,7 @@ namespace CppSharp.Types var pointeeType = pointer.Pointee.Visit(this, quals); var mod = ConvertModifierToString(pointer.Modifier); - var s = quals.IsConst ? "const " : string.Empty; + var s = PrintTypeQualifiers && quals.IsConst ? "const " : string.Empty; s += string.Format("{0}{1}", pointeeType, mod); return s; diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index cdec7817..8cbb0520 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -238,11 +238,23 @@ public class BasicTests : GeneratorTestFixture [Test] public unsafe void TestIndexers() { - var properties = new TestIndexedProperties(); - Assert.AreEqual(1, properties[0]); - Assert.AreEqual(1, properties["foo"]); - properties[0] = 2; - Assert.AreEqual(2, properties[0]); + var indexedProperties = new TestIndexedProperties(); + Assert.AreEqual(1, indexedProperties[0]); + Assert.AreEqual(1, indexedProperties["foo"]); + indexedProperties[0] = 2; + Assert.AreEqual(2, indexedProperties[0]); + indexedProperties[0f] = 3; + Assert.AreEqual(3, indexedProperties[0f]); + var properties = indexedProperties[(byte)0]; + Assert.AreEqual(0, properties.Field); + var newProperties = new TestProperties(); + newProperties.Field = 4; + indexedProperties[(byte)0] = newProperties; + Assert.AreEqual(4, indexedProperties[(byte)0].Field); + newProperties = indexedProperties[(short)0]; + Assert.AreEqual(4, newProperties.Field); + newProperties.Field = 5; + Assert.AreEqual(5, indexedProperties[(byte)0].Field); } [Test] diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 574dd5a7..c3715f40 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -393,17 +393,30 @@ void TestProperties::setFieldValue(int Value) { Field = Value; } class DLL_API TestIndexedProperties { foo_t p; + TestProperties f; public: TestIndexedProperties(); // Should lead to a read/write indexer with return type uint foo_t& operator[](int i); + // Should lead to a read/write indexer with return type uint + foo_t* operator[](float f); // Should lead to a read-only indexer with return type uint foo_t operator[](const char* name); + // Should lead to a read-only indexer with return type uint* + const foo_t& operator[](double d); + // Should lead to a read/write indexer with return type TestProperties + TestProperties* operator[](unsigned char b); + // Should lead to a read-only indexer with return type TestProperties + const TestProperties& operator[](short b); }; -TestIndexedProperties::TestIndexedProperties() : p(1) {} +TestIndexedProperties::TestIndexedProperties() : p(1), f() {} foo_t& TestIndexedProperties::operator[](int i) { return p; } foo_t TestIndexedProperties::operator[](const char* name) { return p; } +foo_t* TestIndexedProperties::operator[](float f) { return &p; } +const foo_t& TestIndexedProperties::operator[](double f) { return p; } +TestProperties* TestIndexedProperties::operator[](unsigned char b) { return &f; } +const TestProperties& TestIndexedProperties::operator[](short b) { return f; } enum struct MyEnum { A, B, C }; From 755861492a26881dac5ea982127da2885a723f0b Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Mon, 28 Apr 2014 19:39:35 +0200 Subject: [PATCH 3/5] Fixed the getter/setter to property passes to not include synthesized methods. --- src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs | 2 +- src/Generator/Passes/GetterSetterToPropertyPass.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs b/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs index 6ebe8484..d121c9cb 100644 --- a/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs +++ b/src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs @@ -70,7 +70,7 @@ namespace CppSharp.Passes public override bool VisitMethodDecl(Method method) { if (!method.IsConstructor && !method.IsDestructor && !method.IsOperator && - method.IsGenerated) + method.IsGenerated && !method.IsSynthetized) DistributeMethod(method); return base.VisitMethodDecl(method); } diff --git a/src/Generator/Passes/GetterSetterToPropertyPass.cs b/src/Generator/Passes/GetterSetterToPropertyPass.cs index d2d68013..3c0670a7 100644 --- a/src/Generator/Passes/GetterSetterToPropertyPass.cs +++ b/src/Generator/Passes/GetterSetterToPropertyPass.cs @@ -87,6 +87,9 @@ namespace CppSharp.Passes if (method.IsConstructor) return false; + if (method.IsSynthetized) + return false; + if (IsGetter(method)) { var name = method.Name.Substring("get".Length); From f9ef513d5f1eb3bb472370ca3a8d71ce4781aba3 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Mon, 5 May 2014 05:33:03 +0200 Subject: [PATCH 4/5] Removed obsolete code. --- src/Generator/Generators/CSharp/CSharpTypePrinter.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs index fdfde8c0..accd7935 100644 --- a/src/Generator/Generators/CSharp/CSharpTypePrinter.cs +++ b/src/Generator/Generators/CSharp/CSharpTypePrinter.cs @@ -12,8 +12,7 @@ namespace CppSharp.Generators.CSharp Native, Managed, ManagedPointer, - GenericDelegate, - PrimitiveIndexer + GenericDelegate } public class CSharpTypePrinterContext : TypePrinterContext From b83b5029b0983cfedc23bc609b2b15c39047289a Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 7 May 2014 17:56:03 +0200 Subject: [PATCH 5/5] Added extension method SetPropertyAsReadOnly to ASTContext. The Property.HasGetter and HasSetter method will take the GenerationKind flag of the field/method pair into account. --- src/AST/Property.cs | 12 +++++++++--- src/Generator.Tests/Passes/TestPasses.cs | 15 +++++++++++++++ src/Generator/Library.cs | 20 ++++++++++++++++++++ tests/Native/Passes.h | 7 +++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/AST/Property.cs b/src/AST/Property.cs index 4c52b5d7..a5524f3a 100644 --- a/src/AST/Property.cs +++ b/src/AST/Property.cs @@ -72,7 +72,10 @@ namespace CppSharp.AST { get { - return (GetMethod != null) || (Field != null); + return (GetMethod != null && + GetMethod.GenerationKind != GenerationKind.None) || + (Field != null && + Field.GenerationKind != GenerationKind.None); } } @@ -80,8 +83,11 @@ namespace CppSharp.AST { get { - return (SetMethod != null) || - (Field != null && !Field.QualifiedType.Qualifiers.IsConst); + return (SetMethod != null && + SetMethod.GenerationKind != GenerationKind.None) || + (Field != null && + !Field.QualifiedType.Qualifiers.IsConst && + Field.GenerationKind != GenerationKind.None); } } diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index c2656f73..58d8edec 100644 --- a/src/Generator.Tests/Passes/TestPasses.cs +++ b/src/Generator.Tests/Passes/TestPasses.cs @@ -108,5 +108,20 @@ namespace CppSharp.Generator.Tests.Passes Assert.IsFalse(AstContext.FindClass("Foo").First().Methods.Find( m => m.Name == "toIgnore").IsGenerated); } + + [Test] + public void TestSetPropertyAsReadOnly() + { + const string className = "TestReadOnlyProperties"; + passBuilder.AddPass(new FieldToPropertyPass()); + passBuilder.AddPass(new GetterSetterToPropertyPass()); + passBuilder.RunPasses(pass => pass.VisitLibrary(AstContext)); + AstContext.SetPropertyAsReadOnly(className, "readOnlyProperty"); + Assert.IsFalse(AstContext.FindClass(className).First().Properties.Find( + m => m.Name == "readOnlyProperty").HasSetter); + AstContext.SetPropertyAsReadOnly(className, "ReadOnlyPropertyMethod"); + Assert.IsFalse(AstContext.FindClass(className).First().Properties.Find( + m => m.Name == "ReadOnlyPropertyMethod").HasSetter); + } } } diff --git a/src/Generator/Library.cs b/src/Generator/Library.cs index 5a8aba08..0b52bc31 100644 --- a/src/Generator/Library.cs +++ b/src/Generator/Library.cs @@ -204,6 +204,26 @@ namespace CppSharp } } + public static void SetPropertyAsReadOnly(this ASTContext context, string className, string propertyName) + { + var properties = context.FindClass(className) + .SelectMany(c => c.Properties.Where(p => p.Name == propertyName && p.HasSetter)); + foreach (var property in properties) + if (property.SetMethod != null) + property.SetMethod.GenerationKind = GenerationKind.None; + else + { + var field = property.Field; + var quals = field.QualifiedType.Qualifiers; + quals.IsConst = true; + + var qualType = field.QualifiedType; + qualType.Qualifiers = quals; + + field.QualifiedType = qualType; + } + } + /// /// Sets the parameter usage for a function parameter. /// diff --git a/tests/Native/Passes.h b/tests/Native/Passes.h index 2435b21d..09cd1e5a 100644 --- a/tests/Native/Passes.h +++ b/tests/Native/Passes.h @@ -26,6 +26,13 @@ struct TestRename int lowerCaseField; }; +struct TestReadOnlyProperties +{ + int readOnlyProperty; + int getReadOnlyPropertyMethod() { return 0; } + void setReadOnlyPropertyMethod(int value) { } +}; + #define TEST_ENUM_ITEM_NAME_0 0 #define TEST_ENUM_ITEM_NAME_1 1 #define TEST_ENUM_ITEM_NAME_2 2