Skip to content

Commit

Permalink
Override the return type of the interop signature of NativeLong/Nativ…
Browse files Browse the repository at this point in the history
…eULong per platform.
  • Loading branch information
jkoritzinsky committed May 11, 2019
1 parent c614c66 commit d6cf323
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 165 deletions.
29 changes: 27 additions & 2 deletions SharpGen.Runtime/NativeLong.cs
Expand Up @@ -26,6 +26,11 @@ public NativeLong(long value)
_value = (NativeType)value;
}

public NativeLong(IntPtr value)
{
_value = (NativeType)value;
}

public override string ToString()
{
return _value.ToString();
Expand Down Expand Up @@ -166,7 +171,17 @@ public bool Equals(NativeLong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeLong" /> to <see cref = "int" />.
/// Performs an implicit conversion from <see cref = "NativeLong" /> to <see cref = "long" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator IntPtr(NativeLong value)
{
return (IntPtr)value.ToInt64();
}

/// <summary>
/// Performs an implicit conversion to <see cref = "NativeLong" /> from <see cref = "int" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
Expand All @@ -176,13 +191,23 @@ public bool Equals(NativeLong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeLong" /> to <see cref = "long" />.
/// Performs an implicit conversion to <see cref = "NativeLong" /> from <see cref = "long" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static explicit operator NativeLong(long value)
{
return new NativeLong(value);
}

/// <summary>
/// Performs an implicit conversion to <see cref = "NativeLong" /> from <see cref = "IntPtr" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator NativeLong(IntPtr value)
{
return new NativeLong(value);
}
}
}
33 changes: 29 additions & 4 deletions SharpGen.Runtime/NativeULong.cs
Expand Up @@ -26,6 +26,11 @@ public NativeULong(ulong value)
_value = (NativeType)value;
}

public NativeULong(UIntPtr value)
{
_value = (NativeType)value;
}

public override string ToString()
{
return _value.ToString();
Expand Down Expand Up @@ -136,7 +141,7 @@ public bool Equals(NativeULong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "int" />.
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "uint" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
Expand All @@ -146,7 +151,7 @@ public bool Equals(NativeULong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "long" />.
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "ulong" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
Expand All @@ -156,7 +161,17 @@ public bool Equals(NativeULong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "int" />.
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "UIntPtr" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator UIntPtr(NativeULong value)
{
return (UIntPtr)value.ToUInt64();
}

/// <summary>
/// Performs an implicit conversion to <see cref = "NativeULong" /> from <see cref = "uint" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
Expand All @@ -166,13 +181,23 @@ public bool Equals(NativeULong other)
}

/// <summary>
/// Performs an implicit conversion from <see cref = "NativeULong" /> to <see cref = "long" />.
/// Performs an implicit conversion to <see cref = "NativeULong" /> from <see cref = "ulong" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static explicit operator NativeULong(ulong value)
{
return new NativeULong(value);
}

/// <summary>
/// Performs an implicit conversion to <see cref = "NativeULong" /> from <see cref = "UIntPtr" />.
/// </summary>
/// <param name = "value">The value.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator NativeULong(UIntPtr value)
{
return new NativeULong(value);
}
}
}
9 changes: 3 additions & 6 deletions SharpGen/Generator/CallableCodeGenerator.cs
Expand Up @@ -127,23 +127,20 @@ public override IEnumerable<MemberDeclarationSyntax> GenerateCode(CsCallable csE
.Select(param => Generators.Marshalling.GetMarshaller(param).GeneratePin(param))
.Where(stmt => stmt != null).ToList();

var callStmt = Block();

callStmt = callStmt.AddStatements(
GeneratorHelpers.GetPlatformSpecificStatements(
var callStmt = GeneratorHelpers.GetPlatformSpecificStatements(
globalNamespace,
csElement.InteropSignatures.Keys,
(platform) =>
ExpressionStatement(
Generators.NativeInvocation.GenerateCode((csElement, platform, csElement.InteropSignatures[platform])))));
Generators.NativeInvocation.GenerateCode((csElement, platform, csElement.InteropSignatures[platform]))));

var fixedStatement = fixedStatements.FirstOrDefault()?.WithStatement(callStmt);
foreach (var statement in fixedStatements.Skip(1))
{
fixedStatement = statement.WithStatement(fixedStatement);
}

statements.Add((StatementSyntax)fixedStatement ?? callStmt);
statements.Add(fixedStatement ?? callStmt);

foreach (var param in csElement.Parameters)
{
Expand Down
10 changes: 9 additions & 1 deletion SharpGen/GlobalNamespaceProvider.cs
Expand Up @@ -71,7 +71,15 @@ public enum WellKnownName
/// <summary>
/// Static class that enables platform detection for SharpGen-generated code.
/// </summary>
PlatformDetection
PlatformDetection,
/// <summary>
/// Native `long` type
/// </summary>
NativeLong,
/// <summary>
/// Native `unsigned long` type
/// </summary>
NativeULong
}

/// <summary>
Expand Down
220 changes: 220 additions & 0 deletions SharpGen/Transform/InteropSignatureTransform.cs
@@ -0,0 +1,220 @@
using SharpGen.Logging;
using SharpGen.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace SharpGen.Transform
{
class InteropSignatureTransform
{
private readonly GlobalNamespaceProvider provider;
private readonly Logger logger;
private readonly Dictionary<string, InteropType> returnTypeOverrides;
private readonly Dictionary<string, InteropType> windowsOnlyReturnTypeOverrides;
private readonly Dictionary<string, InteropType> systemvOnlyReturnTypeOverrides;

public InteropSignatureTransform(GlobalNamespaceProvider provider, Logger logger)
{
this.provider = provider;
this.logger = logger;
returnTypeOverrides = new Dictionary<string, InteropType>
{
{ provider.GetTypeName(WellKnownName.Result), typeof(int) },
{ provider.GetTypeName(WellKnownName.PointerSize), typeof(void*) }
};

windowsOnlyReturnTypeOverrides = new Dictionary<string, InteropType>
{
{ provider.GetTypeName(WellKnownName.NativeLong), typeof(int) },
{ provider.GetTypeName(WellKnownName.NativeULong), typeof(uint) }
};

systemvOnlyReturnTypeOverrides = new Dictionary<string, InteropType>
{
{ provider.GetTypeName(WellKnownName.NativeLong), typeof(IntPtr) },
{ provider.GetTypeName(WellKnownName.NativeULong), typeof(UIntPtr) }
};
}

public Dictionary<PlatformDetectionType, InteropMethodSignature> GetInteropSignatures(CsCallable callable, bool isFunction)
{
var interopSignatures = new Dictionary<PlatformDetectionType, InteropMethodSignature>();
if (callable.IsReturnStructLarge)
{
var sigWithRetBuf = GetNativeInteropSignatureWithForcedReturnBuffer(callable, isFunction);
interopSignatures.Add(PlatformDetectionType.IsWindows, sigWithRetBuf);
interopSignatures.Add(PlatformDetectionType.IsSystemV, GetNativeInteropSignature(callable, isFunction, PlatformDetectionType.IsSystemV));
}
else
{
var returnType = callable.ReturnValue.PublicType.QualifiedName;
InteropType windowsOverride;
windowsOnlyReturnTypeOverrides.TryGetValue(returnType, out windowsOverride);
InteropType systemvOverride;
systemvOnlyReturnTypeOverrides.TryGetValue(returnType, out systemvOverride);

if (windowsOverride == systemvOverride)
interopSignatures.Add(PlatformDetectionType.Any, GetNativeInteropSignature(callable, isFunction, PlatformDetectionType.Any));
else
{

interopSignatures.Add(PlatformDetectionType.IsWindows, GetNativeInteropSignature(callable, isFunction, PlatformDetectionType.IsWindows));
interopSignatures.Add(PlatformDetectionType.IsSystemV, GetNativeInteropSignature(callable, isFunction, PlatformDetectionType.IsSystemV));
}
}

return interopSignatures;
}

private InteropMethodSignature GetNativeInteropSignatureWithForcedReturnBuffer(CsCallable callable, bool isFunction)
{
var cSharpInteropCalliSignature = new InteropMethodSignature
{
IsFunction = isFunction,
CallingConvention = callable.CallingConvention,
Flags = InteropMethodSignatureFlags.ForcedReturnBufferSig
};

cSharpInteropCalliSignature.ReturnType = typeof(void*);
cSharpInteropCalliSignature.ParameterTypes.Add(typeof(void*));

InitCalliSignatureParameters(callable, cSharpInteropCalliSignature);

return cSharpInteropCalliSignature;
}

/// <summary>
/// Registers the native interop signature.
/// </summary>
/// <param name="callable">The cs method.</param>
private InteropMethodSignature GetNativeInteropSignature(CsCallable callable, bool isFunction, PlatformDetectionType platform)
{
// Tag if the method is a function
var cSharpInteropCalliSignature = new InteropMethodSignature
{
IsFunction = isFunction,
CallingConvention = callable.CallingConvention
};

InitSignatureWithReturnType(callable, cSharpInteropCalliSignature, platform);

// Handle Parameters
InitCalliSignatureParameters(callable, cSharpInteropCalliSignature);

return cSharpInteropCalliSignature;
}

private void InitCalliSignatureParameters(CsCallable callable, InteropMethodSignature cSharpInteropCalliSignature)
{
foreach (var param in callable.Parameters)
{
var interopType = GetInteropTypeForParameter(param);

if (interopType == null)
{
logger.Error(LoggingCodes.InvalidMethodParameterType, "Invalid parameter {0} for method {1}", param.PublicType.QualifiedName, callable.CppElement);
}

cSharpInteropCalliSignature.ParameterTypes.Add(interopType);
}
}

private void InitSignatureWithReturnType(CsCallable callable, InteropMethodSignature cSharpInteropCalliSignature, PlatformDetectionType platform)
{
Debug.Assert((platform & (PlatformDetectionType.IsWindows | PlatformDetectionType.IsSystemV)) != (PlatformDetectionType.IsWindows | PlatformDetectionType.IsSystemV));
var platformSpecificReturnTypeOverrides = (platform & PlatformDetectionType.IsWindows) != 0
? windowsOnlyReturnTypeOverrides
: systemvOnlyReturnTypeOverrides;
// Handle Return Type parameter
// MarshalType.Type == null, then check that it is a structure
if (callable.ReturnValue.PublicType is CsStruct || callable.ReturnValue.PublicType is CsEnum)
{
var returnQualifiedName = callable.ReturnValue.PublicType.QualifiedName;
if (returnTypeOverrides.TryGetValue(returnQualifiedName, out var interopType))
{
cSharpInteropCalliSignature.ReturnType = interopType;
}
else if (platformSpecificReturnTypeOverrides.TryGetValue(returnQualifiedName, out interopType))
{
cSharpInteropCalliSignature.ReturnType = interopType;
}
else if (callable.ReturnValue.HasNativeValueType)
cSharpInteropCalliSignature.ReturnType = $"{callable.ReturnValue.MarshalType.QualifiedName}.__Native";
else
cSharpInteropCalliSignature.ReturnType = callable.ReturnValue.MarshalType.QualifiedName;
}
else if (callable.ReturnValue.MarshalType is CsFundamentalType fundamentalReturn)
{
cSharpInteropCalliSignature.ReturnType = fundamentalReturn.Type;
}
else if (callable.ReturnValue.HasPointer)
{
if (callable.ReturnValue.IsInterface)
{
cSharpInteropCalliSignature.ReturnType = typeof(IntPtr);
}
else
{
cSharpInteropCalliSignature.ReturnType = typeof(void*);
}
}
else
{
cSharpInteropCalliSignature.ReturnType = callable.ReturnValue.PublicType.QualifiedName;
logger.Error(LoggingCodes.InvalidMethodReturnType, "Invalid return type {0} for method {1}", callable.ReturnValue.PublicType.QualifiedName, callable.CppElement);
}
}

private InteropType GetInteropTypeForParameter(CsParameter param)
{
InteropType interopType;
var publicName = param.PublicType.QualifiedName;
if (publicName == provider.GetTypeName(WellKnownName.PointerSize))
{
interopType = typeof(void*);
}
else if (param.HasPointer)
{
interopType = typeof(void*);
}
else if (param.MarshalType is CsFundamentalType marshalFundamental)
{
var type = marshalFundamental.Type;
if (type == typeof(IntPtr))
type = typeof(void*);
interopType = type;
}
else if (param.PublicType is CsFundamentalType publicFundamental)
{
var type = publicFundamental.Type;
if (type == typeof(IntPtr))
type = typeof(void*);
interopType = type;
}
else if (param.PublicType is CsStruct csStruct)
{
// If parameter is a struct, then a LocalInterop is needed
if (csStruct.HasMarshalType)
{
interopType = $"{csStruct.QualifiedName}.__Native";
}
else
{
interopType = csStruct.QualifiedName;
}
}
else if (param.PublicType is CsEnum csEnum)
{
interopType = csEnum.UnderlyingType.Type;
}
else
{
interopType = null;
}

return interopType;
}
}
}

0 comments on commit d6cf323

Please sign in to comment.