Browse Source

Merge pull request #233 from azeno/property-improvements

Improved support for indexed properties
pull/232/merge
João Matos 12 years ago
parent
commit
4c9a1f5998
  1. 12
      src/AST/Property.cs
  2. 15
      src/Generator.Tests/Passes/TestPasses.cs
  3. 24
      src/Generator/Generators/CLI/CLIMarshal.cs
  4. 36
      src/Generator/Generators/CLI/CLISourcesTemplate.cs
  5. 56
      src/Generator/Generators/CSharp/CSharpTextTemplate.cs
  6. 3
      src/Generator/Generators/CSharp/CSharpTypePrinter.cs
  7. 20
      src/Generator/Library.cs
  8. 19
      src/Generator/Passes/CheckOperatorsOverloads.cs
  9. 2
      src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs
  10. 3
      src/Generator/Passes/GetterSetterToPropertyPass.cs
  11. 6
      src/Generator/Types/CppTypePrinter.cs
  12. 22
      tests/Basic/Basic.Tests.cs
  13. 34
      tests/Basic/Basic.h
  14. 7
      tests/Native/Passes.h

12
src/AST/Property.cs

@ -72,7 +72,10 @@ namespace CppSharp.AST @@ -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 @@ -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);
}
}

15
src/Generator.Tests/Passes/TestPasses.cs

@ -108,5 +108,20 @@ namespace CppSharp.Generator.Tests.Passes @@ -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);
}
}
}

24
src/Generator/Generators/CLI/CLIMarshal.cs

@ -93,11 +93,31 @@ namespace CppSharp.Generators.CLI @@ -93,11 +93,31 @@ namespace CppSharp.Generators.CLI
{
var returnVarName = Context.ReturnVarName;
if (quals.IsConst != Context.ReturnType.Qualifiers.IsConst)
{
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})",
Context.ReturnType, Context.ReturnVarName);
nativeConstlessTypeName, Context.ReturnVarName);
}
if (pointer.Pointee is TypedefType)
Context.Return.Write("reinterpret_cast<{0}>({1})", pointer,
{
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;

36
src/Generator/Generators/CLI/CLISourcesTemplate.cs

@ -374,7 +374,7 @@ namespace CppSharp.Generators.CLI @@ -374,7 +374,7 @@ namespace CppSharp.Generators.CLI
var param = new Parameter
{
Name = "value",
QualifiedType = decl.QualifiedType
QualifiedType = new QualifiedType(type)
};
var ctx = new MarshalContext(Driver)
@ -400,6 +400,9 @@ namespace CppSharp.Generators.CLI @@ -400,6 +400,9 @@ namespace CppSharp.Generators.CLI
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore);
if (isIndexer && decl.Type.IsPointer())
WriteLine("*({0}) = {1};", variable, marshal.Context.Return);
else
WriteLine("{0} = {1};", variable, marshal.Context.Return);
}
@ -432,6 +435,9 @@ namespace CppSharp.Generators.CLI @@ -432,6 +435,9 @@ namespace CppSharp.Generators.CLI
if (decl is Function)
{
var func = decl as Function;
if (isIndexer && func.Type.IsAddress())
GenerateFunctionCall(func, @class, type);
else
GenerateFunctionCall(func, @class);
}
else
@ -832,11 +838,13 @@ namespace CppSharp.Generators.CLI @@ -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 @@ -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,23 +955,32 @@ namespace CppSharp.Generators.CLI @@ -944,23 +955,32 @@ 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"),
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);
// 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);
}
}

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

@ -966,7 +966,7 @@ namespace CppSharp.Generators.CSharp @@ -966,7 +966,7 @@ namespace CppSharp.Generators.CSharp
private void GenerateIndexerSetter(QualifiedType returnType, Function function)
{
Type type;
returnType.Type.IsPointerTo(out type);
function.Type.IsPointerTo(out type);
PrimitiveType primitiveType;
string internalFunction = GetFunctionNativeIdentifier(function);
if (type.IsPrimitiveType(out primitiveType))
@ -982,7 +982,7 @@ namespace CppSharp.Generators.CSharp @@ -982,7 +982,7 @@ namespace CppSharp.Generators.CSharp
}
}
private void GeneratePropertyGetter<T>(T decl, Class @class)
private void GeneratePropertyGetter<T>(QualifiedType returnType, T decl, Class @class)
where T : Declaration, ITypedDecl
{
PushBlock(CSharpBlockKind.Method);
@ -1002,19 +1002,9 @@ namespace CppSharp.Generators.CSharp @@ -1002,19 +1002,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)
{
@ -1183,12 +1173,6 @@ namespace CppSharp.Generators.CSharp @@ -1183,12 +1173,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)
{
@ -1212,11 +1196,11 @@ namespace CppSharp.Generators.CSharp @@ -1212,11 +1196,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();
@ -1224,7 +1208,7 @@ namespace CppSharp.Generators.CSharp @@ -1224,7 +1208,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,
@ -1233,12 +1217,10 @@ namespace CppSharp.Generators.CSharp @@ -1233,12 +1217,10 @@ namespace CppSharp.Generators.CSharp
else
{
if (prop.HasGetter)
GeneratePropertyGetter(prop.GetMethod, @class);
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();
@ -1258,7 +1240,7 @@ namespace CppSharp.Generators.CSharp @@ -1258,7 +1240,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);
@ -2184,7 +2166,7 @@ namespace CppSharp.Generators.CSharp @@ -2184,7 +2166,7 @@ namespace CppSharp.Generators.CSharp
}
public void GenerateInternalFunctionCall(Function function,
List<Parameter> parameters = null)
List<Parameter> parameters = null, Type returnType = null)
{
if (parameters == null)
parameters = function.Parameters;
@ -2192,11 +2174,11 @@ namespace CppSharp.Generators.CSharp @@ -2192,11 +2174,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<Parameter> parameters,
Function function)
Function function, Type returnType = null)
{
if (function.IsPure)
{
@ -2205,6 +2187,8 @@ namespace CppSharp.Generators.CSharp @@ -2205,6 +2187,8 @@ namespace CppSharp.Generators.CSharp
}
var retType = function.OriginalReturnType;
if (returnType == null)
returnType = retType.Type;
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void);
var isValueType = false;
@ -2363,10 +2347,14 @@ namespace CppSharp.Generators.CSharp @@ -2363,10 +2347,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);
}
}

3
src/Generator/Generators/CSharp/CSharpTypePrinter.cs

@ -12,8 +12,7 @@ namespace CppSharp.Generators.CSharp @@ -12,8 +12,7 @@ namespace CppSharp.Generators.CSharp
Native,
Managed,
ManagedPointer,
GenericDelegate,
PrimitiveIndexer
GenericDelegate
}
public class CSharpTypePrinterContext : TypePrinterContext

20
src/Generator/Library.cs

@ -204,6 +204,26 @@ namespace CppSharp @@ -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;
}
}
/// <summary>
/// Sets the parameter usage for a function parameter.
/// </summary>

19
src/Generator/Passes/CheckOperatorsOverloads.cs

@ -97,21 +97,24 @@ namespace CppSharp.Passes @@ -97,21 +97,24 @@ namespace CppSharp.Passes
GetMethod = @operator
};
if (!@operator.ReturnType.Qualifiers.IsConst && @operator.ReturnType.Type.IsAddress())
var returnType = @operator.Type;
if (returnType.IsAddress())
{
var pointer = returnType as PointerType;
var qualifiedPointee = pointer.QualifiedPointee;
if (!qualifiedPointee.Qualifiers.IsConst)
property.SetMethod = @operator;
}
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;
}
property.QualifiedType = new QualifiedType(
pointerType.Pointee, property.QualifiedType.Qualifiers);
if (Driver.Options.IsCLIGenerator)
// C++/CLI uses "default" as the indexer property name.
property.Name = "default";
}
property.Parameters.AddRange(@operator.Parameters);

2
src/Generator/Passes/GetterSetterToPropertyAdvancedPass.cs

@ -70,7 +70,7 @@ namespace CppSharp.Passes @@ -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);
}

3
src/Generator/Passes/GetterSetterToPropertyPass.cs

@ -87,6 +87,9 @@ namespace CppSharp.Passes @@ -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);

6
src/Generator/Types/CppTypePrinter.cs

@ -17,10 +17,12 @@ namespace CppSharp.Types @@ -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 @@ -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;

22
tests/Basic/Basic.Tests.cs

@ -238,11 +238,23 @@ public class BasicTests : GeneratorTestFixture @@ -238,11 +238,23 @@ 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 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]

34
tests/Basic/Basic.h

@ -336,17 +336,11 @@ typedef unsigned long foo_t; @@ -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,34 @@ TestProperties::TestProperties() : Field(0) {} @@ -396,6 +390,34 @@ TestProperties::TestProperties() : Field(0) {}
int TestProperties::getFieldValue() { return Field; }
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), 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 };
class DLL_API TestArraysPointers

7
tests/Native/Passes.h

@ -26,6 +26,13 @@ struct TestRename @@ -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

Loading…
Cancel
Save