Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support class based safe handles #1118

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading