Skip to content

Commit

Permalink
Support boxed records
Browse files Browse the repository at this point in the history
  • Loading branch information
badcel committed Nov 3, 2023
1 parent 807ef28 commit 9a8ea7b
Show file tree
Hide file tree
Showing 40 changed files with 1,238 additions and 5 deletions.
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Internal/TypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

internal class TypedRecord : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public TypedRecord(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record obj)
{
if (!Record.IsTyped(obj))
return;

var source = Renderer.Internal.TypedRecord.Render(obj);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(obj.Namespace),
Name: obj.Name,
Source: source,
IsInternal: true
);

_publisher.Publish(codeUnit);
}
}
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Internal/TypedRecordData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

internal class TypedRecordData : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public TypedRecordData(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record obj)
{
if (!Record.IsTyped(obj))
return;

var source = Renderer.Internal.TypedRecordData.Render(obj);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(obj.Namespace),
Name: Model.TypedRecord.GetDataName(obj),
Source: source,
IsInternal: true
);

_publisher.Publish(codeUnit);
}
}
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Internal/TypedRecordHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Internal;

internal class TypedRecordHandle : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public TypedRecordHandle(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record obj)
{
if (!Record.IsTyped(obj))
return;

var source = Renderer.Internal.TypedRecordHandle.Render(obj);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(obj.Namespace),
Name: Model.TypedRecord.GetInternalHandle(obj),
Source: source,
IsInternal: true
);

_publisher.Publish(codeUnit);
}
}
29 changes: 29 additions & 0 deletions src/Generation/Generator/Generator/Public/TypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Generator.Model;

namespace Generator.Generator.Public;

internal class TypedRecord : Generator<GirModel.Record>
{
private readonly Publisher _publisher;

public TypedRecord(Publisher publisher)
{
_publisher = publisher;
}

public void Generate(GirModel.Record record)
{
if (!Record.IsTyped(record))
return;

var source = Renderer.Public.TypedRecord.Render(record);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(record.Namespace),
Name: Record.GetPublicClassName(record),
Source: source,
IsInternal: false
);

_publisher.Publish(codeUnit);
}
}
12 changes: 10 additions & 2 deletions src/Generation/Generator/Model/Record.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ internal static partial class Record
{
public static bool IsStandard(GirModel.Record record)
{
return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record);
return !IsOpaqueTyped(record) && !IsOpaqueUntyped(record) && !IsTyped(record);
}

public static bool IsOpaqueTyped(GirModel.Record record)
Expand All @@ -21,7 +21,15 @@ public static bool IsOpaqueUntyped(GirModel.Record record)
//untyped.
return record is { Opaque: true, TypeFunction: null or { CIdentifier: "intern" } };
}


public static bool IsTyped(GirModel.Record record)
{
//Even if there is a TypeFunction it does not mean that it actually is
//a typed / boxed record. There is a magic keyword "intern" which means this
//record is actually fundamental and does not have a type function.
return record is { Opaque: false, TypeFunction.CIdentifier: not "intern" };
}

public static string GetFullyQualifiedInternalStructName(GirModel.Record record)
=> Namespace.GetInternalName(record.Namespace) + "." + GetInternalStructName(record);

Expand Down
37 changes: 37 additions & 0 deletions src/Generation/Generator/Model/TypedRecord.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Generator.Model;

internal static class TypedRecord
{
public static string GetPublicClassName(GirModel.Record record)
=> record.Name;

public static string GetFullyQualifiedPublicClassName(GirModel.Record record)
=> Namespace.GetPublicName(record.Namespace) + "." + GetPublicClassName(record);

public static string GetFullyQualifiedInternalClassName(GirModel.Record record)
=> Namespace.GetInternalName(record.Namespace) + "." + record.Name;

public static string GetInternalHandle(GirModel.Record record)
=> $"{Type.GetName(record)}Handle";

public static string GetInternalOwnedHandle(GirModel.Record record)
=> $"{Type.GetName(record)}OwnedHandle";

public static string GetInternalUnownedHandle(GirModel.Record record)
=> $"{Type.GetName(record)}UnownedHandle";

public static string GetFullyQuallifiedInternalHandle(GirModel.Record record)
=> $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalHandle(record)}";

public static string GetFullyQuallifiedOwnedHandle(GirModel.Record record)
=> $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalOwnedHandle(record)}";

public static string GetFullyQuallifiedUnownedHandle(GirModel.Record record)
=> $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}";

public static string GetFullyQuallifiedNullHandle(GirModel.Record record)
=> $"{Namespace.GetInternalName(record.Namespace)}.{GetInternalUnownedHandle(record)}.NullHandle";

public static string GetDataName(GirModel.Record record)
=> $"{Type.GetName(record)}Data";
}
5 changes: 5 additions & 0 deletions src/Generation/Generator/Records.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ public static void Generate(IEnumerable<GirModel.Record> records, string path)
new Generator.Internal.OpaqueUntypedRecordHandle(publisher),
new Generator.Public.OpaqueUntypedRecord(publisher),

//Typed records
new Generator.Internal.TypedRecord(publisher),
new Generator.Internal.TypedRecordHandle(publisher),
new Generator.Public.TypedRecord(publisher),

//Regular records
new Generator.Internal.RecordDelegates(publisher),
new Generator.Internal.RecordHandle(publisher),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal static class CallbackParameters
new Parameter.RecordCallback(), //Callbacks do not support record safe handles in parameters
new Parameter.RecordGLibPtrArray(),
new Parameter.String(),
new Parameter.TypedRecordCallback(),
new Parameter.Union(),
new Parameter.UnsignedPointer(),
new Parameter.Utf8StringArrayCallback(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;

namespace Generator.Renderer.Internal.Parameter;

internal class TypedRecord : ParameterConverter
{
public bool Supports(GirModel.AnyType anyType)
{
return anyType.Is<GirModel.Record>(out var record) && Model.Record.IsTyped(record);
}

public RenderableParameter Convert(GirModel.Parameter parameter)
{
return new RenderableParameter(
Attribute: string.Empty,
Direction: GetDirection(parameter),
NullableTypeName: GetNullableTypeName(parameter),
Name: Model.Parameter.GetName(parameter)
);
}

private static string GetNullableTypeName(GirModel.Parameter parameter)
{
//Native records are represented as SafeHandles and are not nullable

var type = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT0;
return parameter switch
{
{ Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.None } => Model.TypedRecord.GetFullyQuallifiedInternalHandle(type),
{ Direction: GirModel.Direction.In, Transfer: GirModel.Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type),
_ => throw new Exception($"Can't detect record parameter type {parameter.Name}: CallerAllocates={parameter.CallerAllocates} Direction={parameter.Direction} Transfer={parameter.Transfer}")
};
}

private static string GetDirection(GirModel.Parameter parameter) => parameter switch
{
{ Direction: GirModel.Direction.In } => ParameterDirection.In(),
{ Direction: GirModel.Direction.InOut } => ParameterDirection.In(),
_ => throw new Exception($"Unknown parameter direction for opaque typed record parameter {parameter.Name}")
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;

namespace Generator.Renderer.Internal.Parameter;

internal class TypedRecordArray : ParameterConverter
{
public bool Supports(GirModel.AnyType anyType)
{
return anyType.IsArray<GirModel.Record>(out var record) && Model.Record.IsTyped(record);
}

public RenderableParameter Convert(GirModel.Parameter parameter)
{
if (!parameter.AnyTypeOrVarArgs.AsT0.AsT1.IsPointer)
{
var record = (GirModel.Record) parameter.AnyTypeOrVarArgs.AsT0.AsT1.AnyType.AsT0;
throw new Exception($"Unpointed record array of type {record.Name} not yet supported");
}

return new RenderableParameter(
Attribute: string.Empty,
Direction: string.Empty,
NullableTypeName: $"ref {Model.Type.Pointer}",
Name: Model.Parameter.GetName(parameter)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;

namespace Generator.Renderer.Internal.Parameter;

internal class TypedRecordCallback : ParameterConverter
{
public bool Supports(GirModel.AnyType anyType)
{
return anyType.Is<GirModel.Record>(out var record) && Model.Record.IsTyped(record);
}

public RenderableParameter Convert(GirModel.Parameter parameter)
{
return new RenderableParameter(
Attribute: string.Empty,
Direction: GetDirection(parameter),
NullableTypeName: Model.Type.Pointer,
Name: Model.Parameter.GetName(parameter)
);
}

private static string GetDirection(GirModel.Parameter parameter) => parameter switch
{
{ Direction: GirModel.Direction.In } => ParameterDirection.In(),
{ Direction: GirModel.Direction.InOut } => ParameterDirection.In(),
{ Direction: GirModel.Direction.Out, CallerAllocates: true } => ParameterDirection.In(),
{ Direction: GirModel.Direction.Out } => ParameterDirection.Out(),
_ => throw new Exception("Unknown direction for record parameter in callback")
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ internal static class Parameters
new Parameter.RecordGLibPtrArray(),
new Parameter.String(),
new Parameter.StringGLibPtrArray(),
new Parameter.TypedRecord(),
new Parameter.TypedRecordArray(),
new Parameter.Union(),
new Parameter.UnsignedPointer(),
new Parameter.Utf8StringArray(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;

namespace Generator.Renderer.Internal.ParameterToManagedExpressions;

internal class TypedRecord : ToManagedParameterConverter
{
public bool Supports(GirModel.AnyType type)
=> type.Is<GirModel.Record>(out var record) && Model.Record.IsTyped(record);

public void Initialize(ParameterToManagedData parameterData, IEnumerable<ParameterToManagedData> parameters)
{
if (parameterData.Parameter.Direction != GirModel.Direction.In)
throw new NotImplementedException($"{parameterData.Parameter.AnyTypeOrVarArgs}: typed record with direction != in not yet supported");

var record = (GirModel.Record) parameterData.Parameter.AnyTypeOrVarArgs.AsT0.AsT0;
var variableName = Model.Parameter.GetConvertedName(parameterData.Parameter);

var signatureName = Model.Parameter.GetName(parameterData.Parameter);

var ownedHandle = parameterData.Parameter switch
{
{ Transfer: GirModel.Transfer.Full } => $"new {Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}({signatureName})",
{ Transfer: GirModel.Transfer.None } => $"{Model.TypedRecord.GetFullyQuallifiedOwnedHandle(record)}.FromUnowned({signatureName})",
_ => throw new Exception($"Unknown transfer type for typed record parameter {parameterData.Parameter.Name}")
};

var nullable = parameterData.Parameter.Nullable
? $" {signatureName} == IntPtr.Zero ? null :"
: string.Empty;

parameterData.SetSignatureName(signatureName);
parameterData.SetExpression($"var {variableName} ={nullable} new {Model.TypedRecord.GetFullyQualifiedPublicClassName(record)}({ownedHandle});");
parameterData.SetCallName(variableName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ internal static class ParameterToManagedExpression
new ParameterToManagedExpressions.Record(),
new ParameterToManagedExpressions.RecordArray(),
new ParameterToManagedExpressions.String(),
new ParameterToManagedExpressions.TypedRecord(),
new ParameterToManagedExpressions.Utf8StringArray(),
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using GirModel;

namespace Generator.Renderer.Internal.ReturnType;

internal class TypedRecord : ReturnTypeConverter
{
public bool Supports(GirModel.ReturnType returnType)
{
return returnType.AnyType.Is<GirModel.Record>(out var record) && Model.Record.IsTyped(record);
}

public RenderableReturnType Convert(GirModel.ReturnType returnType)
{
var type = (GirModel.Record) returnType.AnyType.AsT0;

var typeName = returnType switch
{
{ Transfer: Transfer.Full } => Model.TypedRecord.GetFullyQuallifiedOwnedHandle(type),
_ => Model.TypedRecord.GetFullyQuallifiedUnownedHandle(type)
};

//Returned SafeHandles are never "null" but "invalid" in case of C NULL.
return new RenderableReturnType(typeName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Generator.Renderer.Internal.ReturnType;

internal class TypedRecordCallback : ReturnTypeConverter
{
public bool Supports(GirModel.ReturnType returnType)
{
return returnType.AnyType.Is<GirModel.Record>(out var record) && Model.Record.IsTyped(record);
}

public RenderableReturnType Convert(GirModel.ReturnType returnType)
{
return new RenderableReturnType(Model.Type.Pointer);
}
}
Loading

0 comments on commit 9a8ea7b

Please sign in to comment.