Skip to content

Commit

Permalink
Support class based safe handles
Browse files Browse the repository at this point in the history
  • Loading branch information
badcel committed Oct 5, 2024
1 parent acc83c6 commit b8dde53
Show file tree
Hide file tree
Showing 50 changed files with 705 additions and 812 deletions.
1 change: 1 addition & 0 deletions src/Generation/Generator/Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public static void Generate(IEnumerable<GirModel.Class> classes, string path)
//Standard generators
new Generator.Internal.ClassMethods(publisher),
new Generator.Internal.ClassStruct(publisher),
new Generator.Internal.ClassHandle(publisher),
new Generator.Public.ClassConstructors(publisher),
new Generator.Public.ClassMethods(publisher),
new Generator.Public.ClassFunctions(publisher),
Expand Down
32 changes: 32 additions & 0 deletions src/Generation/Generator/Generator/Internal/ClassHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Generator.Model;

namespace Generator.Generator.Internal;

internal class ClassHandle : Generator<GirModel.Class>
{
private readonly Publisher _publisher;

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

public void Generate(GirModel.Class obj)
{
if (obj.Fundamental)
return;

if (obj.Parent is null)
return; //Do not generate a handle for GObject.Object itself

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

_publisher.Publish(codeUnit);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public void Generate(GirModel.Class obj)
if (obj.Fundamental)
return;

if (obj.Parent is null)
return; //Do not generate Framework for GObject.Object itsel

var source = Renderer.Public.ClassFramework.Render(obj);
var codeUnit = new CodeUnit(
Project: Namespace.GetCanonicalName(obj.Namespace),
Expand Down

This file was deleted.

1 change: 0 additions & 1 deletion src/Generation/Generator/Interfaces.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ public static void Generate(IEnumerable<GirModel.Interface> interfaces, string p
var generators = new List<Generator<GirModel.Interface>>()
{
new Generator.Internal.InterfaceMethods(publisher),
new Generator.Public.InterfaceFramework(publisher),
new Generator.Public.InterfaceMethods(publisher),
new Generator.Public.InterfaceProperties(publisher),

Expand Down
21 changes: 21 additions & 0 deletions src/Generation/Generator/Model/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ public static string GetFullyQualifiedInternalStructName(GirModel.Class @class)

public static string GetInternalStructName(GirModel.Class @class)
=> @class.Name + "Data";

public static string GetInternalHandleName(GirModel.Class @class)
=> @class.Name + "Handle";

public static string GetFullyQualifiedInternalHandleName(GirModel.Class @class)
=> Namespace.GetInternalName(@class.Namespace) + "." + GetInternalHandleName(@class);

public static string GetFullyQualifiedPublicName(GirModel.Class @class)
=> Namespace.GetPublicName(@class.Namespace) + "." + @class.Name;

public static bool HidesConstructor(GirModel.Class? cls, GirModel.Constructor constructor)
{
Expand Down Expand Up @@ -84,4 +93,16 @@ private static bool ParameterMatch(GirModel.Parameter[] p1, GirModel.Parameter[]

return true;
}

public static bool IsInitiallyUnowned(GirModel.Class cls) => IsNamedInitiallyUnowned(cls.Name) || InheritsInitiallyUnowned(cls);

private static bool InheritsInitiallyUnowned(GirModel.Class @class)
{
if (@class.Parent is null)
return false;

return IsNamedInitiallyUnowned(@class.Parent.Name) || InheritsInitiallyUnowned(@class.Parent);
}

private static bool IsNamedInitiallyUnowned(string name) => name == "InitiallyUnowned";
}
48 changes: 48 additions & 0 deletions src/Generation/Generator/Renderer/Internal/Class/ClassHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Generator.Model;

namespace Generator.Renderer.Internal;

internal static class ClassHandle
{
public static string Render(GirModel.Class cls)
{
var handleName = Class.GetInternalHandleName(cls);

return $$"""
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

#nullable enable

namespace {{Namespace.GetInternalName(cls.Namespace)}};

public partial class {{handleName}} : GObject.Internal.ObjectHandle
{
public {{handleName}}(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle){ }

public static {{handleName}} For<T>(bool owned, GObject.ConstructArgument[] constructArguments) where T : {{Class.GetFullyQualifiedPublicName(cls)}}, GObject.GTypeProvider
{
// We can't check if a reference is floating via "g_object_is_floating" here
// as the function could be "lying" depending on the intent of framework writers.
// E.g. A Gtk.Window created via "g_object_new_with_properties" returns an unowned
// reference which is not marked as floating as the gtk toolkit "owns" it.
// For this reason we just delegate the problem to the caller and require a
// definition whether the ownership of the new object will be transferred to us or not.

var ptr = GObject.Internal.Object.NewWithProperties(
objectType: T.GetGType(),
nProperties: (uint) constructArguments.Length,
names: constructArguments.Select(x => x.Name).ToArray(),
values: GObject.Internal.ValueArray2OwnedHandle.Create(constructArguments.Select(x => x.Value).ToArray())
);

return new {{handleName}}(ptr, owned);
}
}
""";
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public static string Render(GirModel.Namespace ns)
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using GObject;
using GObject.Internal;
namespace {Namespace.GetInternalName(ns)};
Expand All @@ -40,24 +42,29 @@ internal static void RegisterTypes()
.Join(Environment.NewLine)}
}}
private static void Register<T>(Func<nuint> getType, params OSPlatform[] supportedPlatforms) where T : class
private static void Register<T>(params OSPlatform[] supportedPlatforms) where T : InstanceFactory, GTypeProvider
{{
try
{{
try
{{
#if NET7_0_OR_GREATER
if(supportedPlatforms.Any(RuntimeInformation.IsOSPlatform))
GObject.Internal.TypeDictionary.Add(typeof(T), new GObject.Type(getType()));
}}
catch(System.Exception e)
{{
Debug.WriteLine($""Could not register type '{{nameof(T)}}': {{e.Message}}"");
}}
}}
GObject.Internal.DynamicInstanceFactory.Register(T.GetGType(), T.Create);
#else
if (supportedPlatforms.Any(RuntimeInformation.IsOSPlatform))
GObject.Internal.DynamicInstanceFactory.Register(GTypeProviderHelper.GetGType<T>(), InstanceFactoryHelper.Create<T>);
#endif
}}
catch(System.Exception e)
{{
Debug.WriteLine($""Could not register type: {{e.Message}}"");
}}
}}
}}";
}

private static string RenderRegistration(GirModel.ComplexType type)
{
return @$"Register<{ComplexType.GetFullyQualified(type)}>(Internal.{type.Name}.{Function.GetGType}{RenderPlatforms(type as GirModel.PlatformDependent)});";
return $"Register<{ComplexType.GetFullyQualified(type)}>({RenderPlatforms(type as GirModel.PlatformDependent)});";
}

private static string RenderPlatforms(GirModel.PlatformDependent? platformDependent)
Expand All @@ -76,6 +83,6 @@ private static string RenderPlatforms(GirModel.PlatformDependent? platformDepend
if (platformDependent.SupportsWindows)
statements.Add("OSPlatform.Windows");

return ", " + string.Join(", ", statements);
return string.Join(", ", statements);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ private static void Default(ParameterToManagedData parameterData)
var parameterName = Model.Parameter.GetName(parameterData.Parameter);
var callName = Model.Parameter.GetConvertedName(parameterData.Parameter);

var type = Model.ComplexType.GetFullyQualified(cls);

var wrapHandle = parameterData.Parameter.Nullable
? "GObject.Internal.ObjectWrapper.WrapNullableHandle"
: "GObject.Internal.ObjectWrapper.WrapHandle";

? $"({type}?) GObject.Internal.InstanceWrapper.WrapNullableHandle"
: $"({type}) GObject.Internal.InstanceWrapper.WrapHandle";
parameterData.SetSignatureName(() => parameterName);
parameterData.SetExpression(() => $"var {callName} = {wrapHandle}<{Model.ComplexType.GetFullyQualified(cls)}>({parameterName}, {Model.Transfer.IsOwnedRef(parameterData.Parameter.Transfer).ToString().ToLower()});");
parameterData.SetExpression(() => $"var {callName} = {wrapHandle}<{type}>({parameterName}, {Model.Transfer.IsOwnedRef(parameterData.Parameter.Transfer).ToString().ToLower()});");
parameterData.SetCallName(() => callName);
}
}
50 changes: 7 additions & 43 deletions src/Generation/Generator/Renderer/Public/Class/ClassFramework.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ public static string Render(GirModel.Class cls)

return $@"
using System;
using GObject;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
Expand All @@ -26,7 +25,8 @@ namespace {Namespace.GetPublicName(cls.Namespace)};
{PlatformSupportAttribute.Render(cls as GirModel.PlatformDependent)}
public {@sealed}partial class {cls.Name} {RenderInheritance(cls)}
{{
{RenderParentConstructors(cls)}
{$"protected internal { cls.Name }({Class.GetFullyQualifiedInternalHandleName(cls)} handle) : base(handle) {{ }}"}
{RenderPublicConstructor(cls)}
}}";
}

Expand All @@ -36,7 +36,7 @@ private static string RenderInheritance(GirModel.Class cls)
var interfaces = cls.Implements.Select(ComplexType.GetFullyQualified);

var elements = new List<string>(interfaces);

if (parentClass is not null)
elements.Insert(0, parentClass);

Expand All @@ -45,46 +45,10 @@ private static string RenderInheritance(GirModel.Class cls)
: $": {string.Join(", ", elements)}";
}

private static string RenderParentConstructors(GirModel.Class cls)
{
if (cls.Parent is null)
return string.Empty;

var constructors = new List<string>()
{
$@"protected internal { cls.Name }(IntPtr ptr, bool ownedRef) : base(ptr, ownedRef) {{}}",
};

if (IsInitiallyUnowned(cls))
{
constructors.Add($@"
// As initially unowned objects always start with a floating reference
// we can safely set the ""owned"" parameter to false.
protected internal {cls.Name}(params ConstructArgument[] constructArguments) : base(owned: false, constructArguments) {{}}");
constructors.Add($"public {cls.Name}() : this(Array.Empty<ConstructArgument>()) {{}}");
}
else if (InheritsInitiallyUnowned(cls))
{
constructors.Add($"protected internal {cls.Name}(params ConstructArgument[] constructArguments) : base(constructArguments) {{}}");
constructors.Add($"public {cls.Name}() : this(Array.Empty<ConstructArgument>()) {{}}");
}
else
{
constructors.Add($"protected internal {cls.Name}(bool owned, params ConstructArgument[] constructArguments) : base(owned, constructArguments) {{}}");
}

return constructors.Join(Environment.NewLine);
}

private static bool IsInitiallyUnowned(GirModel.Class cls) => IsNamedInitiallyUnowned(cls.Name);

private static bool InheritsInitiallyUnowned(GirModel.Class @class)
private static string RenderPublicConstructor(GirModel.Class cls)
{
if (@class.Parent is null)
return false;

return IsNamedInitiallyUnowned(@class.Parent.Name) || InheritsInitiallyUnowned(@class.Parent);
return Class.IsInitiallyUnowned(cls)
? $" public {cls.Name}() : this({Class.GetFullyQualifiedInternalHandleName(cls)}.For<{cls.Name}>(false, Array.Empty<GObject.ConstructArgument>())) {{ }}"
: $" public {cls.Name}() : this({Class.GetFullyQualifiedInternalHandleName(cls)}.For<{cls.Name}>(true, Array.Empty<GObject.ConstructArgument>())) {{ }}";
}

private static bool IsNamedInitiallyUnowned(string name) => name == "InitiallyUnowned";
}
36 changes: 34 additions & 2 deletions src/Generation/Generator/Renderer/Public/Class/ClassFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,45 @@ namespace {Namespace.GetPublicName(cls.Namespace)};
// AUTOGENERATED FILE - DO NOT MODIFY
public partial class {cls.Name} : GObject.GTypeProvider
public partial class {cls.Name} : GObject.GTypeProvider, GObject.InstanceFactory
{{
{FunctionRenderer.Render(cls.TypeFunction)}
#if NET7_0_OR_GREATER
static GObject.Type GTypeProvider.GetGType()
#else
public static GObject.Type GetGType()
#endif
{{
return {RenderGetGType(cls.TypeFunction)};
}}
#if NET7_0_OR_GREATER
static object GObject.InstanceFactory.Create(IntPtr handle, bool ownsHandle)
#else
public static object Create(IntPtr handle, bool ownsHandle)
#endif
{{
return CreateIntern(handle, ownsHandle);
}}
private static {cls.Name} CreateIntern(IntPtr handle, bool ownsHandle)
{{
return {RenderObjectFactory(cls)};
}}
{cls.Functions
.Select(FunctionRenderer.Render)
.Join(Environment.NewLine)}
}}";
}

private static string RenderGetGType(GirModel.Function function)
{
return $"{Namespace.GetInternalName(function.Namespace)}.{function.Parent!.Name}.{Function.GetName(function)}()";
}

private static string RenderObjectFactory(GirModel.Class cls)
{
return $"new {cls.Name}(new {Class.GetFullyQualifiedInternalHandleName(cls)}(handle, ownsHandle))";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ private static string CreateExpression(GirModel.Constructor constructor, string

return cls.Fundamental
? $"new {cls.Name}({fromVariableName})"
: $"new {cls.Name}({fromVariableName}, {ownedRef.ToString().ToLower()})";
: $"{cls.Name}.CreateIntern({fromVariableName}, {ownedRef.ToString().ToLower()})";
}
}
Loading

0 comments on commit b8dde53

Please sign in to comment.