Skip to content

Commit

Permalink
Merge pull request #994 from gircore/typed-record-field-access
Browse files Browse the repository at this point in the history
TypedRecord: Add field access
  • Loading branch information
badcel authored Jan 12, 2024
2 parents 14cd764 + bd0856d commit 8847a1d
Show file tree
Hide file tree
Showing 16 changed files with 336 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Generator.Model;

namespace Generator.Fixer.Record;

internal class PublicMethodsColldingWithFieldFixer : Fixer<GirModel.Record>
{
public void Fixup(GirModel.Record record)
{
foreach (var field in record.Fields)
{
foreach (var method in record.Methods)
{
if (Field.GetName(field) == Method.GetPublicName(method))
{
if (method.ReturnType.AnyType.Is<GirModel.Void>())
{
Log.Warning($"{record.Namespace.Name}.{record.Name}: Method {Method.GetPublicName(method)} is named like a field but returns no value. It makes no sense to prefix the method with 'Get'. Thus it will be ignored.");
Method.Disable(method);
}
else
{
var newName = $"Get{Method.GetPublicName(method)}";
Log.Warning($"{record.Namespace.Name}.{record.Name}: Method {Method.GetPublicName(method)} collides with field {Field.GetName(field)} and is renamed to {newName}.");

Method.SetPublicName(method, newName);
}
}
}
}
}
}
3 changes: 2 additions & 1 deletion src/Generation/Generator/Fixer/Records.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ public static class Records
{
private static readonly List<Fixer<GirModel.Record>> Fixers = new()
{
new InternalMethodsNamedLikeRecordFixer()
new InternalMethodsNamedLikeRecordFixer(),
new PublicMethodsColldingWithFieldFixer()
};

public static void Fixup(IEnumerable<GirModel.Record> records)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public RenderableField Convert(GirModel.Field field)
{
return new RenderableField(
Name: Model.Field.GetName(field),
Attribute: MarshalAs.UnmanagedLpString(),
NullableTypeName: Type.GetName(field.AnyTypeOrCallback.AsT0.AsT0)
Attribute: null,
NullableTypeName: Type.Pointer
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,7 @@ private static string RenderFieldGetter(GirModel.Record record, GirModel.Field f
if (IsClosed || IsInvalid)
throw new InvalidOperationException(""Handle is closed or invalid"");
var data = Unsafe.AsRef<{dataName}>((void*)handle);
return data.{renderableField.Name};
return Unsafe.AsRef<{dataName}>((void*)handle).{renderableField.Name};
}}";
}

Expand All @@ -266,8 +265,7 @@ private static string RenderFieldSetter(GirModel.Record record, GirModel.Field f
if (IsClosed || IsInvalid)
throw new InvalidOperationException(""Handle is closed or invalid"");
var data = Unsafe.AsRef<{dataName}>((void*)handle);
data.{renderableField.Name} = value;
Unsafe.AsRef<{dataName}>((void*)handle).{renderableField.Name} = value;
}}";
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Generator.Renderer.Public.Field;

internal class Bitfield : FieldConverter
{
public bool Supports(GirModel.Field field)
{
return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is<GirModel.Bitfield>();
}

public RenderableField Convert(GirModel.Field field)
{
var name = Model.Field.GetName(field);
return new RenderableField(
Name: name,
NullableTypeName: GetNullableTypeName(field),
SetExpression: SetExpression,
GetExpression: GetExpression
);
}

private static string GetNullableTypeName(GirModel.Field field)
{
var type = (GirModel.Bitfield) field.AnyTypeOrCallback.AsT0.AsT0;
return field.IsPointer
? Model.Type.Pointer
: Model.ComplexType.GetFullyQualified(type);
}

private static string SetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Set{Model.Field.GetName(field)}(value)";
}

private static string GetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Get{Model.Field.GetName(field)}()";
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Generator.Renderer.Public.Field;

internal class Enumeration : FieldConverter
{
public bool Supports(GirModel.Field field)
{
return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is<GirModel.Enumeration>();
}

public RenderableField Convert(GirModel.Field field)
{
return new RenderableField(
Name: Model.Field.GetName(field),
NullableTypeName: GetNullableTypeName(field),
SetExpression: SetExpression,
GetExpression: GetExpression
);
}

private static string GetNullableTypeName(GirModel.Field field)
{
var type = (GirModel.Enumeration) field.AnyTypeOrCallback.AsT0.AsT0;
return field.IsPointer
? Model.Type.Pointer
: Model.ComplexType.GetFullyQualified(type);
}

private static string SetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Set{Model.Field.GetName(field)}(value)";
}

private static string GetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Get{Model.Field.GetName(field)}()";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace Generator.Renderer.Public.Field;

internal class PrimitiveValueType : FieldConverter
{
public bool Supports(GirModel.Field field)
{
return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is<GirModel.PrimitiveValueType>();
}

public RenderableField Convert(GirModel.Field field)
{
return new RenderableField(
Name: Model.Field.GetName(field),
NullableTypeName: GetNullableTypeName(field),
SetExpression: SetExpression,
GetExpression: GetExpression
);
}

private static string GetNullableTypeName(GirModel.Field field)
{
return field.IsPointer
? Model.Type.Pointer
: Model.Type.GetName(field.AnyTypeOrCallback.AsT0.AsT0);
}

private static string SetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Set{Model.Field.GetName(field)}(value)";
}

private static string GetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Get{Model.Field.GetName(field)}()";
}
}
33 changes: 33 additions & 0 deletions src/Generation/Generator/Renderer/Public/Field/Converter/String.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Generator.Model;

namespace Generator.Renderer.Public.Field;

internal class String : FieldConverter
{
public bool Supports(GirModel.Field field)
{
return field.AnyTypeOrCallback.TryPickT0(out var anyType, out _) && anyType.Is<GirModel.String>();
}

public RenderableField Convert(GirModel.Field field)
{
//Struct fields are always nullable as there are no information on nullability

return new RenderableField(
Name: Model.Field.GetName(field),
NullableTypeName: $"{Type.GetName(field.AnyTypeOrCallback.AsT0.AsT0)}?",
SetExpression: SetExpression,
GetExpression: GetExpression
);
}

private static string SetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Handle.Set{Model.Field.GetName(field)}(GLib.Internal.StringHelper.StringToPtrUtf8(value))";
}

private static string GetExpression(GirModel.Record record, GirModel.Field field)
{
return $"Marshal.PtrToStringUTF8(Handle.Get{Model.Field.GetName(field)}())";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Generator.Renderer.Public.Field;

public interface FieldConverter
{
bool Supports(GirModel.Field field);
RenderableField Convert(GirModel.Field field);
}
23 changes: 23 additions & 0 deletions src/Generation/Generator/Renderer/Public/Field/Fields.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;

namespace Generator.Renderer.Public;

internal static class Fields
{
private static readonly List<Field.FieldConverter> Converters = new()
{
new Field.Bitfield(),
new Field.Enumeration(),
new Field.PrimitiveValueType(),
new Field.String(),
};

public static Field.RenderableField GetRenderableField(GirModel.Field field)
{
foreach (var converter in Converters)
if (converter.Supports(field))
return converter.Convert(field);

throw new System.Exception($"Internal field \"{field.Name}\" of type {field.AnyTypeOrCallback} can not be rendered");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System;

namespace Generator.Renderer.Public.Field;

public delegate string Expression(GirModel.Record record, GirModel.Field field);

public record RenderableField(string Name, string NullableTypeName, Expression SetExpression, Expression GetExpression);
35 changes: 35 additions & 0 deletions src/Generation/Generator/Renderer/Public/TypedRecord.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Text;
using Generator.Model;

namespace Generator.Renderer.Public;
Expand Down Expand Up @@ -49,6 +50,10 @@ public partial class {name}
{FunctionRenderer.Render(record.TypeFunction)}
{record.Fields
.Select(f => RenderField(record, f))
.Join(Environment.NewLine)}
{record.Functions
.Select(FunctionRenderer.Render)
.Join(Environment.NewLine)}
Expand All @@ -59,4 +64,34 @@ public partial class {name}
.Join(Environment.NewLine)}
}}";
}

private static string RenderField(GirModel.Record record, GirModel.Field field)
{
try
{
var renderableField = Fields.GetRenderableField(field);

if (field is { IsReadable: false, IsWritable: false } || field.IsPrivate)
return string.Empty;

var result = new StringBuilder();

result.AppendLine($"public {renderableField.NullableTypeName} {renderableField.Name} {{");

if (field.IsReadable)
result.AppendLine($"get => {renderableField.GetExpression(record, field)};");

if (field.IsWritable)
result.AppendLine($"set => {renderableField.SetExpression(record, field)};");

result.AppendLine("}");

return result.ToString();
}
catch (Exception ex)
{
Log.Warning($"Did not render typed record {record.Name} field {field.Name}: {ex.Message}");
return string.Empty;
}
}
}
3 changes: 1 addition & 2 deletions src/Libs/GLib-2.0/Public/GException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ public sealed class GException : Exception, IDisposable
private readonly Internal.ErrorHandle _errorHandle;

public GException(Internal.ErrorHandle errorHandle)
: base(Marshal.PtrToStructure<Internal.ErrorData>(errorHandle.DangerousGetHandle()).Message)
: base(Marshal.PtrToStringUTF8(errorHandle.GetMessage()))
{

_errorHandle = errorHandle;
}

Expand Down
2 changes: 2 additions & 0 deletions src/Native/GirTestLib/girtest-typed-record-tester.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ girtest_typed_record_tester_new ()
GirTestTypedRecordTester *result;
result = g_new0 (GirTestTypedRecordTester, 1);
result->ref_count = 1;
result->custom_string = g_strdup("Hello");
return result;
}

Expand Down Expand Up @@ -113,6 +114,7 @@ girtest_typed_record_tester_unref (GirTestTypedRecordTester *self)
if (self->ref_count > 0)
return;

g_free(self->custom_string);
g_free (self);
}

Expand Down
29 changes: 29 additions & 0 deletions src/Native/GirTestLib/girtest-typed-record-tester.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,30 @@

G_BEGIN_DECLS

/**
* GirTestTypedRecordTesterEnum:
* @A: 1
* @B: 2
*
* Enum to test bindings.
*/
typedef enum {
TYPED_RECORD_TESTER_ENUM_A = 0,
TYPED_RECORD_TESTER_ENUM_B = 1
} GirTestTypedRecordTesterEnum;

/**
* GirTestTypedRecordTesterBitfield:
* @ZERO: No flags set.
* @ONE: Set first flag.
*
* Flags to test bindings.
*/
typedef enum {
TYPED_RECORD_TESTER_ZERO = 0,
TYPED_RECORD_TESTER_ONE = (1 << 0)
} GirTestTypedRecordTesterBitfield;

/**
* GirTestTypedRecordTester:
*
Expand All @@ -12,6 +36,11 @@ G_BEGIN_DECLS
struct _GirTestTypedRecordTester
{
int ref_count;
GirTestTypedRecordTesterEnum custom_enum;
GirTestTypedRecordTesterBitfield custom_bitfield;
gchar* custom_string;
/* < private > */
int custom_int_private;
};

typedef struct _GirTestTypedRecordTester GirTestTypedRecordTester;
Expand Down
Loading

0 comments on commit 8847a1d

Please sign in to comment.