[c] Implement support for fields in reference and value types.

This commit is contained in:
Joao Matos 2017-05-12 12:21:59 +01:00
Родитель 9483b1b262
Коммит 3af3f6e6a1
11 изменённых файлов: 279 добавлений и 45 удалений

Просмотреть файл

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -258,9 +258,7 @@ namespace MonoEmbeddinator4000.Generators
continue;
var decl = VisitField(field);
// TODO: Ignore fields until we implement usage of FieldToPropertyPass
//@class.Declarations.Add(decl);
@class.Declarations.Add(decl);
}
foreach (var @event in type.DeclaredEvents)
@ -625,10 +623,12 @@ namespace MonoEmbeddinator4000.Generators
public Field VisitField(FieldInfo fieldInfo)
{
var field = new Field()
var field = new Field
{
Name = UnmangleTypeName(fieldInfo.Name),
QualifiedType = VisitType(fieldInfo.FieldType)
Namespace = Visit(fieldInfo.DeclaringType.GetTypeInfo()) as Class,
QualifiedType = VisitType(fieldInfo.FieldType),
IsStatic = fieldInfo.IsStatic
};
var accessMask = (fieldInfo.Attributes & FieldAttributes.FieldAccessMask);
@ -644,17 +644,24 @@ namespace MonoEmbeddinator4000.Generators
public Property VisitProperty(PropertyInfo propertyInfo, Class @class)
{
var property = new Property()
var property = new Property
{
Name = UnmangleTypeName(propertyInfo.Name),
Namespace = Visit(propertyInfo.DeclaringType.GetTypeInfo()) as Class,
QualifiedType = VisitType(propertyInfo.PropertyType),
};
if (propertyInfo.GetMethod != null)
{
property.GetMethod = VisitMethod(propertyInfo.GetMethod, @class);
property.GetMethod.Namespace = property.Namespace;
}
if (propertyInfo.SetMethod != null)
{
property.SetMethod = VisitMethod(propertyInfo.SetMethod, @class);
property.SetMethod.Namespace = property.Namespace;
}
return property;
}

Просмотреть файл

@ -145,6 +145,20 @@ namespace MonoEmbeddinator4000.Generators
return true;
}
public override bool VisitProperty(Property property)
{
if (property.Field == null)
return false;
var getter = property.GetMethod;
VisitMethodDecl(getter);
var setter = property.SetMethod;
VisitMethodDecl(setter);
return true;
}
}
}

Просмотреть файл

@ -393,7 +393,7 @@ namespace MonoEmbeddinator4000.Generators
var handle = CSources.GetMonoObjectField(Options, CSources.MonoObjectFieldUsage.Parameter,
Context.ArgName, "_handle");
var @object = $"mono_gchandle_get_target({handle})";
var @object = $"{Context.ArgName} ? mono_gchandle_get_target({handle}) : 0";
if (@class.IsValueType)
@object = $"mono_object_unbox({@object})";

Просмотреть файл

@ -377,5 +377,130 @@ namespace MonoEmbeddinator4000.Generators
return true;
}
public void GenerateFieldLookup(Field field)
{
var fieldId = GeneratedIdentifier("field");
WriteLine($"static MonoClassField *{fieldId} = 0;");
WriteLine($"if (!{fieldId})");
WriteStartBraceIndent();
var @class = field.Namespace as Class;
var classLookupId = GeneratedIdentifier($"lookup_class_{@class.QualifiedName.Replace('.', '_')}");
WriteLine($"{classLookupId}();");
var fieldNameId = GeneratedIdentifier("field_name");
WriteLine($"const char {fieldNameId}[] = \"{field.Name}\";");
var classId = $"class_{@class.QualifiedName}";
WriteLine($"{fieldId} = mono_class_get_field_from_name({classId}, {fieldNameId});");
WriteCloseBraceIndent();
}
public override bool VisitProperty(Property property)
{
if (property.Field == null)
return false;
GenerateFieldGetter(property);
NewLine();
GenerateFieldSetter(property);
NewLine();
return true;
}
void GenerateFieldGetter(Property property)
{
var getter = property.GetMethod;
GenerateMethodSpecifier(getter, getter.Namespace as Class);
NewLine();
WriteStartBraceIndent();
var field = property.Field;
GenerateFieldLookup(field);
var instanceId = field.IsStatic ? "0" : GeneratedIdentifier("instance");
var resultId = GeneratedIdentifier("result");
if (!field.IsStatic)
{
var handle = GetMonoObjectField(Options, MonoObjectFieldUsage.Instance,
FixMethodParametersPass.ObjectParameterId, "_handle");
WriteLine($"MonoObject* {instanceId} = mono_gchandle_get_target({handle});");
}
var fieldId = GeneratedIdentifier("field");
var domainId = $"{GeneratedIdentifier("mono_context")}.domain";
WriteLine($"MonoObject* {resultId} = mono_field_get_value_object({domainId}, {fieldId}, {instanceId});");
var ctx = new MarshalContext(Context)
{
ArgName = resultId,
ReturnVarName = resultId,
ReturnType = property.QualifiedType
};
var marshal = new CMarshalManagedToNative(EmbedOptions, ctx);
property.QualifiedType.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore);
WriteLine($"return {marshal.Context.Return.ToString()};");
WriteCloseBraceIndent();
}
void GenerateFieldSetter(Property property)
{
var setter = property.SetMethod;
var @class = property.Namespace as Class;
GenerateMethodSpecifier(setter, setter.Namespace as Class);
NewLine();
WriteStartBraceIndent();
var field = property.Field;
var fieldId = GeneratedIdentifier("field");
GenerateFieldLookup(field);
var ctx = new MarshalContext(Context) { ArgName = "value" };
var marshal = new CMarshalNativeToManaged(EmbedOptions, ctx);
property.QualifiedType.Visit(marshal);
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore))
Write(marshal.Context.SupportBefore);
var valueId = GeneratedIdentifier("value");
WriteLine($"void* {valueId} = {marshal.Context.Return.ToString()};");
if (field.IsStatic)
{
var vtableId = GeneratedIdentifier("vtable");
var domainId = $"{GeneratedIdentifier("mono_context")}.domain";
var classId = $"class_{@class.QualifiedName}";
WriteLine ($"MonoVTable* {vtableId} = mono_class_vtable({domainId}, {classId});");
WriteLine ($"mono_field_static_set_value({vtableId}, {fieldId}, {valueId});");
}
else
{
var instanceId = GeneratedIdentifier("instance");
var handle = GetMonoObjectField(Options, MonoObjectFieldUsage.Instance,
FixMethodParametersPass.ObjectParameterId, "_handle");
WriteLine($"MonoObject* {instanceId} = mono_gchandle_get_target({handle});");
WriteLine ($"mono_field_set_value({instanceId}, {fieldId}, {valueId});");
}
WriteCloseBraceIndent();
}
}
}

Просмотреть файл

@ -427,6 +427,12 @@ namespace MonoEmbeddinator4000.Generators
return true;
}
public override bool VisitFieldDecl(Field field)
{
// Ignore fields since they're converted to getter/setter pais.
return true;
}
public static string GetAccess(AccessSpecifier accessSpecifier)
{
switch (accessSpecifier)

Просмотреть файл

@ -4,68 +4,65 @@ using CppSharp.Passes;
namespace MonoEmbeddinator4000.Passes
{
public class FieldToGetterSetterPass : TranslationUnitPass
public class FieldToGetterSetterPropertyPass : TranslationUnitPass
{
public override bool VisitFieldDecl(Field field)
{
if (!VisitDeclaration(field))
return false;
var @class = field.Namespace as Class;
if (@class == null)
if (field.Access == AccessSpecifier.Private)
return false;
if (field.Access == AccessSpecifier.Private)
return true;
// Check if we already have a synthetized getter/setter.
//var existing = @class.Methods.FirstOrDefault(
// method => method.Parameters.SingleOrDefault(
// (Parameter p) =>
// {
// Class paramClass;
// if(!p.Type.TryGetClass(out paramClass))
// return false;
// return paramClass == @class;
// }));
//if (existing != null)
// return false;
if (field.IsImplicit)
return false;
field.GenerationKind = GenerationKind.None;
var @class = field.Namespace as Class;
var getter = new Method
{
Name = string.Format("get{0}", field.Name),
Name = $"get_{field.Name}",
Namespace = @class,
ReturnType = field.QualifiedType,
Access = field.Access
Access = field.Access,
SynthKind = FunctionSynthKind.FieldAcessor,
Field = field
};
@class.Methods.Add(getter);
var setter = new Method
{
Name = string.Format("set{0}", field.Name),
Name = $"set_{field.Name}",
Namespace = @class,
ReturnType = new QualifiedType(new BuiltinType(PrimitiveType.Void)),
Access = field.Access,
SynthKind = FunctionSynthKind.FieldAcessor,
Field = field
};
var param = new Parameter
{
Name = "value",
QualifiedType = field.QualifiedType
QualifiedType = field.QualifiedType,
};
setter.Parameters.Add(param);
@class.Methods.Add(setter);
var property = new Property
{
Name = field.Name,
Namespace = field.Namespace,
GetMethod = getter,
SetMethod = setter,
Field = field,
QualifiedType = field.QualifiedType
};
Diagnostics.Debug("Getter/setter created from field: {0}::{1}",
@class.QualifiedOriginalName, field.Name);
@class.Declarations.Add(property);
return false;
Diagnostics.Debug($"Getter/setter property created from field {field.QualifiedName}");
return true;
}
}
}

Просмотреть файл

@ -66,10 +66,25 @@ namespace MonoEmbeddinator4000.Passes
if (method.IsConstructor)
return false;
if (Options.GeneratorKind == GeneratorKind.C)
var isStaticField = method.Field != null && method.Field.IsStatic;
if (Options.GeneratorKind == GeneratorKind.C && !isStaticField)
AddObjectParameterToMethod(method, @class);
return true;
}
public override bool VisitProperty(Property property)
{
if (!VisitDeclaration(property))
return false;
if (property.GetMethod != null)
property.GetMethod.Visit(this);
if (property.SetMethod != null)
property.SetMethod.Visit(this);
return true;
}
}
}

Просмотреть файл

@ -106,7 +106,8 @@ namespace MonoEmbeddinator4000.Passes
Name = CGenerator.ObjectInstanceId,
QualifiedType = new QualifiedType(ptrType),
Access = AccessSpecifier.Public,
Namespace = @class
Namespace = @class,
IsImplicit = true
};
@class.Fields.Add(field);

Просмотреть файл

@ -14,9 +14,7 @@ namespace MonoEmbeddinator4000.Passes
if (!VisitDeclaration(property))
return false;
var @class = property.Namespace as Class;
if (@class == null)
return false;
return false;
property.GenerationKind = GenerationKind.None;
@ -29,6 +27,7 @@ namespace MonoEmbeddinator4000.Passes
IsStatic = property.IsStatic
};
var @class = property.Namespace as Class;
@class.Methods.Add(getter);
var param = new Parameter

2
external/CppSharp поставляемый

@ -1 +1 @@
Subproject commit 2670d47eb18953e889876fdca3e563dd0fc6240d
Subproject commit bd86afa9a2ee16256ea552fe22bb44d9ee66477e

Просмотреть файл

@ -238,6 +238,10 @@ TEST_CASE("Structs.C", "[C][Structs]") {
Structs_Point* p4 = Structs_Point_op_Subtraction(p3, p2);
REQUIRE(Structs_Point_op_Equality(p4, p1) == true);
Structs_Point* z = Structs_Point_get_Zero();
REQUIRE(Structs_Point_get_X(z) == 0.0f);
REQUIRE(Structs_Point_get_Y(z) == 0.0f);
}
TEST_CASE("Enums.C", "[C][Enums]") {
@ -254,6 +258,72 @@ TEST_CASE("Enums.C", "[C][Enums]") {
REQUIRE(s == Enums_ShortEnum_Min);
}
TEST_CASE("FieldsInReference.C", "[C][Fields]") {
REQUIRE(Fields_Class_get_MaxLong() == LONG_MAX);
REQUIRE(Fields_Class_get_Integer() == 0);
Fields_Class_set_Integer(1);
REQUIRE(Fields_Class_get_Integer() == 1);
Fields_Class* scratch = Fields_Class_get_Scratch();
REQUIRE(Fields_Class_get_Boolean(scratch) == true);
scratch = Fields_Class_new(/*enabled=*/false);
Fields_Class_set_Scratch(scratch);
REQUIRE(Fields_Class_get_Boolean(scratch) == false);
Fields_Class* ref1 = Fields_Class_new(/*enabled=*/true);
REQUIRE(Fields_Class_get_Boolean(ref1) == true);
Fields_Class_set_Boolean(ref1, false);
REQUIRE(Fields_Class_get_Boolean(ref1) == false);
Fields_Struct* struct1 = Fields_Class_get_Structure(ref1);
REQUIRE(struct1 != NULL);
REQUIRE(Fields_Struct_get_Boolean(struct1) == false);
struct1 = Fields_Struct_new(/*enabled=*/true);
REQUIRE(Fields_Struct_get_Boolean(struct1) == true);
Fields_Class* ref2 = Fields_Class_new(/*enabled=*/false);
Fields_Struct* struct2 = Fields_Class_get_Structure(ref2);
REQUIRE(struct2 != NULL);
REQUIRE(Fields_Class_get_Boolean(ref2) == false);
}
TEST_CASE("FieldsInValueType.C", "[C][Fields]") {
REQUIRE(Fields_Struct_get_Integer() == 0);
Fields_Struct_set_Integer(1);
REQUIRE(Fields_Struct_get_Integer() == 1);
Fields_Struct* scratch = Fields_Struct_get_Scratch();
REQUIRE(Fields_Struct_get_Boolean(scratch) == false);
scratch = Fields_Struct_new(/*enabled=*/true);
Fields_Struct_set_Scratch(scratch);
REQUIRE(Fields_Struct_get_Boolean(scratch) == true);
Fields_Struct* empty = Fields_Struct_get_Empty();
REQUIRE(empty != NULL);
REQUIRE(Fields_Struct_get_Class(empty) == NULL);
Fields_Struct* struct1 = Fields_Struct_new(/*enabled=*/true);
REQUIRE(Fields_Struct_get_Boolean(struct1) == true);
Fields_Struct_set_Boolean(struct1, false);
REQUIRE(Fields_Struct_get_Boolean(struct1) == false);
Fields_Class* struct1_class = Fields_Struct_get_Class(struct1);
REQUIRE(struct1_class != NULL);
REQUIRE(Fields_Class_get_Boolean(struct1_class) == false);
Fields_Struct_set_Class(struct1, NULL);
REQUIRE(Fields_Struct_get_Class(struct1) == NULL);
struct1_class = Fields_Class_new(/*enabled=*/true);
REQUIRE(Fields_Class_get_Boolean(struct1_class) == true);
Fields_Struct* struct2 = Fields_Struct_new(/*enabled=*/false);
Fields_Class* struct2_class = Fields_Struct_get_Class(struct2);
REQUIRE(struct2_class != NULL);
REQUIRE(Fields_Class_get_Boolean(struct2_class) == false);
}
TEST_CASE("Interfaces.C", "[C][Interfaces]") {
Interfaces_IMakeItUp* m = Interfaces_Supplier_Create();
REQUIRE(Interfaces_IMakeItUp_get_Boolean(m) == true);