+
Skip to content

Improve handling of string parameters #803

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

Merged
merged 3 commits into from
Feb 23, 2023
Merged
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
15 changes: 15 additions & 0 deletions src/Generation/Generator/Model/PlatformString.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Generator.Model;

internal static partial class PlatformString
{
private const string NullableTypeNamePrefix = "GLib.Internal.NullablePlatformString";
private const string NonNullableTypeNamePrefix = "GLib.Internal.NonNullablePlatformString";

public static string GetInternalNullableHandleName() => NullableTypeNamePrefix + "Handle";
public static string GetInternalNullableOwnedHandleName() => NullableTypeNamePrefix + "OwnedHandle";
public static string GetInternalNullableUnownedHandleName() => NullableTypeNamePrefix + "UnownedHandle";

public static string GetInternalNonNullableHandleName() => NonNullableTypeNamePrefix + "Handle";
public static string GetInternalNonNullableOwnedHandleName() => NonNullableTypeNamePrefix + "OwnedHandle";
public static string GetInternalNonNullableUnownedHandleName() => NonNullableTypeNamePrefix + "UnownedHandle";
}
15 changes: 15 additions & 0 deletions src/Generation/Generator/Model/Utf8String.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Generator.Model;

internal static partial class Utf8String
{
private const string NullableTypeNamePrefix = "GLib.Internal.NullableUtf8String";
private const string NonNullableTypeNamePrefix = "GLib.Internal.NonNullableUtf8String";

public static string GetInternalNullableHandleName() => NullableTypeNamePrefix + "Handle";
public static string GetInternalNullableOwnedHandleName() => NullableTypeNamePrefix + "OwnedHandle";
public static string GetInternalNullableUnownedHandleName() => NullableTypeNamePrefix + "UnownedHandle";

public static string GetInternalNonNullableHandleName() => NonNullableTypeNamePrefix + "Handle";
public static string GetInternalNonNullableOwnedHandleName() => NonNullableTypeNamePrefix + "OwnedHandle";
public static string GetInternalNonNullableUnownedHandleName() => NonNullableTypeNamePrefix + "UnownedHandle";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,22 @@ internal static class StringParameterFactory
public static RenderableParameter Create(GirModel.Parameter parameter)
{
return new RenderableParameter(
Attribute: GetAttribute(parameter),
Attribute: string.Empty,
Direction: GetDirection(parameter),
NullableTypeName: GetNullableTypeName(parameter),
Name: Parameter.GetName(parameter)
);
}

private static string GetAttribute(GirModel.Parameter parameter) => parameter.AnyType.AsT0 switch
private static string GetNullableTypeName(GirModel.Parameter parameter) => parameter switch
{
// Marshal as a UTF-8 encoded string
GirModel.Utf8String => MarshalAs.UnmanagedLpUtf8String(),

// Marshal as a null-terminated array of ANSI characters
// TODO: This is likely incorrect:
// - GObject introspection specifies that Windows should use
// UTF-8 and Unix should use ANSI. Does using ANSI for
// everything cause problems here?
GirModel.PlatformString => MarshalAs.UnmanagedLpString(),

_ => ""
// TODO the type name should depend on the transfer / direction / caller-allocates flags.
{ AnyType.AsT0: GirModel.PlatformString, Nullable: true } => PlatformString.GetInternalNullableHandleName(),
{ AnyType.AsT0: GirModel.PlatformString, Nullable: false } => PlatformString.GetInternalNonNullableHandleName(),
{ AnyType.AsT0: GirModel.Utf8String, Nullable: true } => Utf8String.GetInternalNullableHandleName(),
_ => Utf8String.GetInternalNonNullableHandleName(),
};

private static string GetNullableTypeName(GirModel.Parameter parameter)
=> Type.GetName(parameter.AnyType.AsT0) + Nullable.Render(parameter);

private static string GetDirection(GirModel.Parameter parameter) => parameter switch
{
{ Direction: GirModel.Direction.InOut } => ParameterDirection.Ref(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ internal class String : ToManagedParameterConverter
public bool Supports(GirModel.AnyType type)
=> type.Is<GirModel.String>();

public string? GetExpression(GirModel.Parameter parameter, out string variableName)
public string GetExpression(GirModel.Parameter parameter, out string variableName)
{
variableName = Parameter.GetName(parameter);
return null;
variableName = Parameter.GetConvertedName(parameter);
return $"var {variableName} = {Parameter.GetName(parameter)}.ConvertToString();";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,20 @@ namespace Generator.Renderer.Internal;

internal static class PlatformStringReturnTypeFactory
{
public static RenderableReturnType CreateForCallback(GirModel.ReturnType returnType)
{
// This must be IntPtr since SafeHandle's cannot be returned from managed to unmanaged.
return new RenderableReturnType(Type.Pointer);
}

public static RenderableReturnType Create(GirModel.ReturnType returnType)
{
var nullableTypeName = returnType switch
{
// Return values which return a string without transferring ownership to us can not be marshalled automatically
// as the marshaller want's to free the unmanaged memory which is not allowed if the ownership is not transferred
{ Transfer: GirModel.Transfer.None } => Type.Pointer,
_ => Type.GetName(returnType.AnyType.AsT0) + Nullable.Render((GirModel.Nullable) returnType)
{ Nullable: true, Transfer: GirModel.Transfer.None } => PlatformString.GetInternalNullableUnownedHandleName(),
{ Nullable: false, Transfer: GirModel.Transfer.None } => PlatformString.GetInternalNonNullableUnownedHandleName(),
{ Nullable: true, Transfer: GirModel.Transfer.Full } => PlatformString.GetInternalNullableOwnedHandleName(),
_ => PlatformString.GetInternalNonNullableOwnedHandleName(),
};

return new RenderableReturnType(nullableTypeName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public static RenderableReturnType CreateForCallback(this GirModel.ReturnType re
GirModel.PrimitiveValueType => PrimitiveValueReturnTypeFactory.Create(returnValue),
GirModel.Bitfield => BitfieldReturnTypeFactory.Create(returnValue),
GirModel.Enumeration => EnumerationReturnTypeFactory.Create(returnValue),
GirModel.Utf8String => Utf8StringReturnTypeFactory.Create(returnValue),
GirModel.PlatformString => PlatformStringReturnTypeFactory.Create(returnValue),
GirModel.Utf8String => Utf8StringReturnTypeFactory.CreateForCallback(returnValue),
GirModel.PlatformString => PlatformStringReturnTypeFactory.CreateForCallback(returnValue),
GirModel.Record => RecordReturnTypeForCallbackFactory.Create(returnValue),
GirModel.Union => UnionReturnTypeFactory.Create(returnValue),
GirModel.Class => ClassReturnTypeFactory.Create(returnValue),
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Generator.Model;

namespace Generator.Renderer.Internal;

internal static class Utf8StringReturnTypeFactory
{
public static RenderableReturnType CreateForCallback(GirModel.ReturnType returnType)
{
// This must be IntPtr since SafeHandle's cannot be returned from managed to unmanaged.
return new RenderableReturnType(Type.Pointer);
}

public static RenderableReturnType Create(GirModel.ReturnType returnType)
{
var nullableTypeName = returnType switch
{
{ Nullable: true, Transfer: GirModel.Transfer.None } => Utf8String.GetInternalNullableUnownedHandleName(),
{ Nullable: false, Transfer: GirModel.Transfer.None } => Utf8String.GetInternalNonNullableUnownedHandleName(),
{ Nullable: true, Transfer: GirModel.Transfer.Full } => Utf8String.GetInternalNullableOwnedHandleName(),
_ => Utf8String.GetInternalNonNullableOwnedHandleName(),
};

return new RenderableReturnType(nullableTypeName);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using GirModel;

namespace Generator.Renderer.Internal.ReturnTypeToNativeExpressions;

internal class Utf8String : ReturnTypeConverter
{
public bool Supports(AnyType type)
=> type.Is<GirModel.Utf8String>();

public string GetString(GirModel.ReturnType returnType, string fromVariableName)
{
// Returning a string with transfer=none means that the called function owns the string (e.g. a constant lifetime)
// This doesn't work since we're returning a newly-allocated UTF-8 string converted from the managed callback's return value.
if (returnType.Transfer == GirModel.Transfer.None)
throw new System.NotImplementedException("String return type with transfer=none cannot be converted to native");

// If transfer=full, return a string that the native caller will own and we do not free.
return $"GLib.Internal.StringHelper.StringToPtrUtf8({fromVariableName})";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal static class ReturnTypeToNativeExpression
new ReturnTypeToNativeExpressions.Enumeration(),
new ReturnTypeToNativeExpressions.PrimitiveValueType(),
new ReturnTypeToNativeExpressions.Record(),
new ReturnTypeToNativeExpressions.String(),
new ReturnTypeToNativeExpressions.Utf8String(),
};

public static string Render(GirModel.ReturnType from, string fromVariableName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using Generator.Model;

namespace Generator.Renderer.Public.ParameterToNativeExpressions;

internal class PlatformString : ToNativeParameterConverter
{
public bool Supports(GirModel.AnyType type)
=> type.Is<GirModel.PlatformString>();

public void Initialize(ParameterToNativeData parameter, IEnumerable<ParameterToNativeData> _)
{
// TODO - the caller needs to pass in some kind of Span<T> as a buffer that can be filled in by the C function.
// These functions (e.g. g_unichar_to_utf8()) expect a minimum buffer size to be provided.
if (parameter.Parameter.CallerAllocates)
throw new NotImplementedException($"{parameter.Parameter.AnyType}: String type with caller-allocates=1 not yet supported");

// TODO - the default marshalling for 'ref string' produces crashes for functions
// like pango_skip_space(), so custom marshalling may be required.
if (parameter.Parameter.Direction == GirModel.Direction.InOut)
throw new NotImplementedException($"{parameter.Parameter.AnyType}: String type with direction=inout not yet supported");

// TODO - support output strings
if (parameter.Parameter.Direction == GirModel.Direction.Out)
throw new NotImplementedException($"{parameter.Parameter.AnyType}: String type with direction=out not yet supported");

var parameterName = Parameter.GetName(parameter.Parameter);
parameter.SetSignatureName(parameterName);

string nativeVariableName = Parameter.GetConvertedName(parameter.Parameter);

string ownedHandleTypeName = parameter.Parameter.Nullable
? Model.PlatformString.GetInternalNullableOwnedHandleName()
: Model.PlatformString.GetInternalNonNullableOwnedHandleName();

parameter.SetExpression($"var {nativeVariableName} = {ownedHandleTypeName}.Create({parameterName});");
parameter.SetCallName(nativeVariableName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace Generator.Renderer.Public.ParameterToNativeExpressions;

internal class String : ToNativeParameterConverter
internal class Utf8String : ToNativeParameterConverter
{
public bool Supports(GirModel.AnyType type)
=> type.Is<GirModel.String>();
=> type.Is<GirModel.Utf8String>();

public void Initialize(ParameterToNativeData parameter, IEnumerable<ParameterToNativeData> _)
{
Expand All @@ -21,12 +21,20 @@ public void Initialize(ParameterToNativeData parameter, IEnumerable<ParameterToN
if (parameter.Parameter.Direction == GirModel.Direction.InOut)
throw new NotImplementedException($"{parameter.Parameter.AnyType}: String type with direction=inout not yet supported");

var prefix = parameter.Parameter.Direction == GirModel.Direction.Out
? "out "
: string.Empty;
// TODO - support output strings
if (parameter.Parameter.Direction == GirModel.Direction.Out)
throw new NotImplementedException($"{parameter.Parameter.AnyType}: String type with direction=out not yet supported");

var parameterName = Parameter.GetName(parameter.Parameter);
parameter.SetSignatureName(parameterName);
parameter.SetCallName(prefix + parameterName);

string nativeVariableName = Parameter.GetConvertedName(parameter.Parameter);

string ownedHandleTypeName = parameter.Parameter.Nullable
? Model.Utf8String.GetInternalNullableOwnedHandleName()
: Model.Utf8String.GetInternalNonNullableOwnedHandleName();

parameter.SetExpression($"var {nativeVariableName} = {ownedHandleTypeName}.Create({parameterName});");
parameter.SetCallName(nativeVariableName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ internal static class ParameterToNativeExpression
new ParameterToNativeExpressions.PrimitiveValueTypeArray(),
new ParameterToNativeExpressions.Enumeration(),
new ParameterToNativeExpressions.Bitfield(),
new ParameterToNativeExpressions.String(),
new ParameterToNativeExpressions.PlatformString(),
new ParameterToNativeExpressions.Utf8String(),
new ParameterToNativeExpressions.StringArray(),
new ParameterToNativeExpressions.Interface(),
new ParameterToNativeExpressions.InterfaceArray(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using GirModel;
using Transfer = Generator.Model.Transfer;

namespace Generator.Renderer.Public.ReturnTypeToManagedExpressions;

Expand All @@ -10,14 +9,7 @@ public bool Supports(AnyType type)

public string GetString(GirModel.ReturnType returnType, string fromVariableName)
{
var nullCheck = returnType.Nullable
? string.Empty
: " ?? throw new System.Exception(\"Non nullable return type returned a null\")";

//If ownership is transfered the internal return type is encoded as a string as the
//marshaller will handle the ownership transfer automatically
return Transfer.IsOwnedRef(returnType.Transfer)
? fromVariableName
: $"GLib.Internal.StringHelper.ToStringUtf8({fromVariableName}){nullCheck}";
// Convert the PlatformStringHandle return type to a string.
return $"{fromVariableName}.ConvertToString()";
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using GirModel;
using Transfer = Generator.Model.Transfer;

namespace Generator.Renderer.Public.ReturnTypeToManagedExpressions;

Expand All @@ -10,14 +9,7 @@ public bool Supports(AnyType type)

public string GetString(GirModel.ReturnType returnType, string fromVariableName)
{
var nullCheck = returnType.Nullable
? string.Empty
: " ?? throw new System.Exception(\"Non nullable return type returned a null\")";

//If ownership is transfered the internal return type is encoded as a string as the
//marshaller will handle the ownership transfer automatically
return Transfer.IsOwnedRef(returnType.Transfer)
? fromVariableName
: $"GLib.Internal.StringHelper.ToStringUtf8({fromVariableName}){nullCheck}";
// Convert the Utf8StringHandle return type to a string.
return $"{fromVariableName}.ConvertToString()";
}
}
4 changes: 1 addition & 3 deletions src/Generation/GirLoader/Output/String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ internal override bool Matches(TypeReference typeReference)
}

/// <summary>
/// A platform native string. This should be utf-8 on Windows and
/// a zero terminated guint8 array on Unix.
/// TODO: We currently use null terminated ASCII on both platforms, which may be problematic.
/// A platform native filename string. See https://docs.gtk.org/glib/character-set.html
/// </summary>
public class PlatformString : String, GirModel.PlatformString
{
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载