diff --git a/build/Tests.lua b/build/Tests.lua index e0b87855..b7607769 100644 --- a/build/Tests.lua +++ b/build/Tests.lua @@ -90,6 +90,7 @@ function SetupTestProjectsCSharp(name, file, lib) kind "SharedLib" language "C#" location "." + flags { "Unsafe" } files { name .. ".Tests.cs" } links { name .. ".CSharp" } diff --git a/src/AST/Type.cs b/src/AST/Type.cs index 421852e8..84818c3a 100644 --- a/src/AST/Type.cs +++ b/src/AST/Type.cs @@ -47,6 +47,11 @@ namespace CppSharp.AST return tag.Declaration is Enumeration; } + public bool IsAddress() + { + return IsPointer() || IsReference(); + } + public bool IsPointer() { var functionPointer = this as MemberPointerType; diff --git a/src/Generator/Generators/CSharp/CSharpMarshal.cs b/src/Generator/Generators/CSharp/CSharpMarshal.cs index 82fa525c..8a852b96 100644 --- a/src/Generator/Generators/CSharp/CSharpMarshal.cs +++ b/src/Generator/Generators/CSharp/CSharpMarshal.cs @@ -239,6 +239,7 @@ namespace CppSharp.Generators.CSharp } if (@class.IsRefType && + (Context.ReturnType.Qualifiers.IsConst || !Context.ReturnType.Type.IsAddress()) && (!Context.Driver.Options.GenerateAbstractImpls || !@class.IsAbstract)) { var instanceName = Generator.GeneratedIdentifier("instance"); @@ -533,7 +534,7 @@ namespace CppSharp.Generators.CSharp return; } - if (Context.Parameter.Type.IsPointer() || Context.Parameter.Type.IsReference()) + if (Context.Parameter.Type.IsAddress()) { Context.Return.Write("{0}.{1}", Helpers.SafeIdentifier(Context.Parameter.Name), Helpers.InstanceIdentifier); diff --git a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs index e40f902e..b3ffc235 100644 --- a/src/Generator/Generators/CSharp/CSharpTextTemplate.cs +++ b/src/Generator/Generators/CSharp/CSharpTextTemplate.cs @@ -725,7 +725,7 @@ namespace CppSharp.Generators.CSharp return Tuple.Create(library, decl.Mangled); } - private void GeneratePropertySetter(T decl, Class @class) + private void GeneratePropertySetter(QualifiedType returnType, T decl, Class @class) where T : Declaration, ITypedDecl { PushBlock(CSharpBlockKind.Method); @@ -753,8 +753,16 @@ namespace CppSharp.Generators.CSharp param.QualifiedType = function.Parameters[0].QualifiedType; - var parameters = new List { param }; - GenerateInternalFunctionCall(function, parameters); + var method = function as Method; + if (method != null && method.IsSynthetized) + { + GenerateIndexerSetter(returnType, method); + } + else + { + var parameters = new List { param }; + GenerateInternalFunctionCall(function, parameters); + } } else if (decl is Field) { @@ -779,6 +787,23 @@ namespace CppSharp.Generators.CSharp PopBlock(NewLineKind.BeforeNextBlock); } + private void GenerateIndexerSetter(QualifiedType returnType, Function function) + { + Type type; + returnType.Type.IsPointerTo(out type); + PrimitiveType primitiveType; + if (type.IsPrimitiveType(out primitiveType)) + { + WriteLine("*({0}*) this[{1}] = *({0}*) value;", + type.ToString(), function.Parameters[0].Name); + } + else + { + WriteLine("*({0}.Internal*) this[{1}].{2} = *({0}.Internal*) value.{2};", + type.ToString(), function.Parameters[0].Name, Helpers.InstanceIdentifier); + } + } + private void GeneratePropertyGetter(T decl, Class @class) where T : Declaration, ITypedDecl { @@ -907,7 +932,7 @@ namespace CppSharp.Generators.CSharp GeneratePropertyGetter(prop.Field, @class); if (prop.HasSetter) - GeneratePropertySetter(prop.Field, @class); + GeneratePropertySetter(prop.Field.QualifiedType, prop.Field, @class); } else { @@ -915,7 +940,7 @@ namespace CppSharp.Generators.CSharp GeneratePropertyGetter(prop.GetMethod, @class); if (prop.HasSetter) - GeneratePropertySetter(prop.SetMethod, @class); + GeneratePropertySetter(prop.GetMethod.ReturnType, prop.SetMethod, @class); } WriteCloseBraceIndent(); @@ -938,7 +963,7 @@ namespace CppSharp.Generators.CSharp GeneratePropertyGetter(variable, @class); if (!variable.QualifiedType.Qualifiers.IsConst) - GeneratePropertySetter(variable, @class); + GeneratePropertySetter(variable.QualifiedType, variable, @class); WriteCloseBraceIndent(); PopBlock(NewLineKind.BeforeNextBlock); diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index 8da7ae5a..7fd91748 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -86,14 +86,23 @@ namespace CppSharp.Passes private static void CreateIndexer(Class @class, Method @operator) { Property property = new Property - { - Name = "Item", - QualifiedType = @operator.ReturnType, - Access = @operator.Access, - Namespace = @class, - GetMethod = @operator - }; + { + Name = "Item", + QualifiedType = @operator.ReturnType, + Access = @operator.Access, + Namespace = @class, + GetMethod = @operator + }; property.Parameters.AddRange(@operator.Parameters); + if (!@operator.ReturnType.Qualifiers.IsConst && @operator.ReturnType.Type.IsAddress()) + { + property.SetMethod = new Method(@operator) + { + ReturnType = new QualifiedType( + new BuiltinType(PrimitiveType.Void)), + IsSynthetized = true + }; + } @class.Properties.Add(property); @operator.IsGenerated = false; } diff --git a/tests/CSharpTemp/CSharpTemp.Tests.cs b/tests/CSharpTemp/CSharpTemp.Tests.cs index afce2c5b..12d2acfd 100644 --- a/tests/CSharpTemp/CSharpTemp.Tests.cs +++ b/tests/CSharpTemp/CSharpTemp.Tests.cs @@ -1,4 +1,6 @@ -using System.Reflection; +using System; +using System.Reflection; +using CSharpTemp; using NUnit.Framework; using Foo = CSharpTemp.Foo; @@ -6,10 +8,24 @@ using Foo = CSharpTemp.Foo; public class CSharpTempTests { [Test] - public void TestIndexer() + public unsafe void TestIndexer() { var foo = new Foo(); - Assert.That(foo[0], Is.EqualTo(5)); + + // TODO: Most of the ugliness below will disappear when pointers to simple types are represented by C#-pointers or ref modifiers instead of the nasty IntPtr + var value = *(int*) foo[0]; + Assert.That(value, Is.EqualTo(50)); + int x = 250; + foo[0] = new IntPtr(&x); + value = *(int*) foo[0]; + Assert.That(value, Is.EqualTo(x)); + + Assert.That(foo[(uint) 0], Is.EqualTo(15)); + + var bar = new Bar(); + Assert.That(bar[0].A, Is.EqualTo(10)); + bar[0] = new Foo { A = 25 }; + Assert.That(bar[0].A, Is.EqualTo(25)); } [Test] diff --git a/tests/CSharpTemp/CSharpTemp.cpp b/tests/CSharpTemp/CSharpTemp.cpp index a90f00e1..8c59df01 100644 --- a/tests/CSharpTemp/CSharpTemp.cpp +++ b/tests/CSharpTemp/CSharpTemp.cpp @@ -1,12 +1,32 @@ #include "CSharpTemp.h" -// TODO: move this, it has nothing to do with Unicode, it's here only not to break the CLI branch +Foo::Foo() +{ + A = 10; + P = 50; +} + int Foo::operator[](int i) const { return 5; } -int Foo::operator[](int i) +int Foo::operator[](unsigned int i) { - return 5; -} \ No newline at end of file + return 15; +} + +int& Foo::operator[](int i) +{ + return P; +} + +const Foo& Bar::operator[](int i) const +{ + return m_foo; +} + +Foo& Bar::operator[](int i) +{ + return m_foo; +} diff --git a/tests/CSharpTemp/CSharpTemp.h b/tests/CSharpTemp/CSharpTemp.h index 62f00aa1..647fff34 100644 --- a/tests/CSharpTemp/CSharpTemp.h +++ b/tests/CSharpTemp/CSharpTemp.h @@ -7,9 +7,22 @@ class DLL_API Foo { public: + Foo(); int operator[](int i) const; - int operator[](int i); + int operator[](unsigned int i); + int& operator[](int i); + int A; protected: int P; }; + +class DLL_API Bar +{ +public: + const Foo& operator[](int i) const; + Foo& operator[](int i); + +private: + Foo m_foo; +};