Support .NET language feature: function pointers (#623)
* Support .NET language feature: function pointers * update * update * add test cases * add test dll * update * Use delegate * for other languages * update * Fix new member added for function pointers * update
This commit is contained in:
Родитель
b5f62fcd16
Коммит
93806a9b54
Двоичный файл не отображается.
|
@ -49,8 +49,10 @@ namespace Mono.Documentation
|
|||
public const string IsByRefLikeAttribute = "System.Runtime.CompilerServices.IsByRefLikeAttribute";
|
||||
public const string IsReadOnlyAttribute = "System.Runtime.CompilerServices.IsReadOnlyAttribute";
|
||||
public const string InAttribute = "System.Runtime.InteropServices.InAttribute";
|
||||
public const string OutAttribute = "System.Runtime.InteropServices.OutAttribute";
|
||||
public const string TupleElementNamesAttribute = "System.Runtime.CompilerServices.TupleElementNamesAttribute";
|
||||
public const string IsExternalInit = "System.Runtime.CompilerServices.IsExternalInit";
|
||||
public const string NativeIntegerAttribute = "System.Runtime.CompilerServices.NativeIntegerAttribute";
|
||||
public const string CallConvPrefix = "System.Runtime.CompilerServices.CallConv";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,9 @@ namespace Mono.Documentation.Updater
|
|||
|
||||
string xmlMemberType = member.Parameters[i];
|
||||
|
||||
// After we support function pointers, "method" as type should be skipped and not be compared with current function pointer type.
|
||||
if (xmlMemberType == "method") continue;
|
||||
|
||||
// TODO: take into account extension method reftype
|
||||
bool xmlIsRefType = xmlMemberType.Contains ('&');
|
||||
bool refTypesMatch = isRefType == xmlIsRefType;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Mono.Cecil;
|
||||
using Mono.Documentation.Util;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -591,43 +592,51 @@ namespace Mono.Documentation.Updater.Formatters
|
|||
{
|
||||
if (DocUtils.IsExtensionMethod (method))
|
||||
buf.Append ("this ");
|
||||
AppendParameter (buf, parameters[0]);
|
||||
AppendParameter(buf, parameters[0]);
|
||||
for (int i = 1; i < parameters.Count; ++i)
|
||||
{
|
||||
buf.Append (", ");
|
||||
AppendParameter (buf, parameters[i]);
|
||||
buf.Append(", ");
|
||||
AppendParameter(buf, parameters[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.Append (end);
|
||||
}
|
||||
|
||||
private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
|
||||
protected override StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
TypeReference parameterType = parameter.ParameterType;
|
||||
var refType = new BitArray(3);
|
||||
|
||||
if (parameterType is RequiredModifierType requiredModifierType)
|
||||
{
|
||||
switch(requiredModifierType.ModifierType.FullName)
|
||||
{
|
||||
case Consts.InAttribute: refType.Set(0, true); break;
|
||||
case Consts.OutAttribute: refType.Set(1, true); break;
|
||||
default: break;
|
||||
}
|
||||
parameterType = requiredModifierType.ElementType;
|
||||
}
|
||||
|
||||
if (parameterType is ByReferenceType byReferenceType)
|
||||
{
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
buf.Append ("out ");
|
||||
refType.Set(1, true);
|
||||
}
|
||||
else if(parameter.IsIn && DocUtils.HasCustomAttribute(parameter, Consts.IsReadOnlyAttribute))
|
||||
{
|
||||
buf.Append("in ");
|
||||
refType.Set(0, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.Append("ref ");
|
||||
refType.Set(2, true);
|
||||
}
|
||||
parameterType = byReferenceType.ElementType;
|
||||
}
|
||||
|
||||
buf.Append(refType.Get(0) ? "in " : (refType.Get(1) ? "out " : (refType.Get(2) ? "ref ": "")));
|
||||
|
||||
if (parameter.HasCustomAttributes)
|
||||
{
|
||||
var isParams = parameter.CustomAttributes.Any (ca => ca.AttributeType.Name == "ParamArrayAttribute");
|
||||
|
@ -639,8 +648,7 @@ namespace Mono.Documentation.Updater.Formatters
|
|||
var isNullableType = context.IsNullable ();
|
||||
buf.Append (GetTypeName (parameterType, context));
|
||||
buf.Append (GetTypeNullableSymbol (parameter.ParameterType, isNullableType));
|
||||
buf.Append (" ");
|
||||
buf.Append (parameter.Name);
|
||||
buf.Append (string.IsNullOrEmpty(parameter.Name) ? "" : " " + parameter.Name);
|
||||
|
||||
if (parameter.HasDefault && parameter.IsOptional && parameter.HasConstant)
|
||||
{
|
||||
|
|
|
@ -715,7 +715,7 @@ namespace Mono.Documentation.Updater.Formatters.CppFormatters
|
|||
return buf.Append (end);
|
||||
}
|
||||
|
||||
protected virtual StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
|
||||
protected override StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
if (parameter.ParameterType is ByReferenceType)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
namespace Mono.Documentation.Updater
|
||||
using Mono.Cecil;
|
||||
using System.Text;
|
||||
|
||||
namespace Mono.Documentation.Updater
|
||||
{
|
||||
class DocTypeFullMemberFormatter : MemberFormatter
|
||||
{
|
||||
|
@ -20,5 +23,10 @@
|
|||
{
|
||||
get { return "+"; }
|
||||
}
|
||||
|
||||
protected override StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameterDef)
|
||||
{
|
||||
return buf.Append(GetName(parameterDef.ParameterType, useTypeProjection: false, isTypeofOperator: false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -692,7 +692,7 @@ namespace Mono.Documentation.Updater
|
|||
return buf;
|
||||
}
|
||||
|
||||
private void AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
protected override StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
bool isFSharpFunction = IsFSharpFunction(parameter.ParameterType);
|
||||
if (isFSharpFunction)
|
||||
|
@ -701,6 +701,7 @@ namespace Mono.Documentation.Updater
|
|||
buf.Append(typeName);
|
||||
if (isFSharpFunction)
|
||||
buf.Append(")");
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected override string GetPropertyDeclaration(PropertyDefinition property)
|
||||
|
|
|
@ -440,7 +440,7 @@ namespace Mono.Documentation.Updater.Formatters
|
|||
return buf.Append (end);
|
||||
}
|
||||
|
||||
private StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
|
||||
protected override StringBuilder AppendParameter (StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
if (parameter.ParameterType is ByReferenceType)
|
||||
{
|
||||
|
@ -599,5 +599,10 @@ namespace Mono.Documentation.Updater.Formatters
|
|||
|
||||
return buf.ToString ();
|
||||
}
|
||||
|
||||
protected override void AppendFunctionPointerTypeName(StringBuilder buf, FunctionPointerType type, IAttributeParserContext context)
|
||||
{
|
||||
buf.Append("method");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -136,6 +136,11 @@ namespace Mono.Documentation.Updater.Formatters
|
|||
return buf.Append(string.Join(", ", parameters.Select(i => i.Name)));
|
||||
}
|
||||
|
||||
protected override StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
return buf.Append(parameter.Name);
|
||||
}
|
||||
|
||||
protected MethodDefinition GetConstructor(TypeDefinition type)
|
||||
{
|
||||
return type.GetConstructors()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Mono.Cecil;
|
||||
using Mono.Documentation.Updater.Formatters;
|
||||
using Mono.Documentation.Util;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -131,6 +132,69 @@ namespace Mono.Documentation.Updater
|
|||
return AppendArrayModifiers (buf, (ArrayType)type);
|
||||
}
|
||||
|
||||
protected virtual void AppendFunctionPointerTypeName(StringBuilder buf, FunctionPointerType type, IAttributeParserContext context)
|
||||
{
|
||||
buf.Append("delegate*");
|
||||
|
||||
var callingConvention = GetCallingConvention(type);
|
||||
if (callingConvention != MethodCallingConvention.Default.ToString())
|
||||
{
|
||||
buf.Append(" unmanaged");
|
||||
if (!string.IsNullOrEmpty(callingConvention))
|
||||
{
|
||||
buf.Append("[").Append(callingConvention).Append("]");
|
||||
}
|
||||
}
|
||||
|
||||
buf.Append("<");
|
||||
if (type.Parameters?.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < type.Parameters.Count; i++)
|
||||
{
|
||||
AppendParameter(buf, type.Parameters[i]);
|
||||
buf.Append(", ");
|
||||
}
|
||||
}
|
||||
AppendReturnTypeName(buf, type, true);
|
||||
buf.Append(">");
|
||||
}
|
||||
|
||||
private string GetCallingConvention(FunctionPointerType type)
|
||||
{
|
||||
var callingConvention = type.CallingConvention.ToString("D");
|
||||
// Cecil lib uses "9" to stands for "Unmanaged Ext"
|
||||
if (callingConvention != "9")
|
||||
{
|
||||
return NormalizeCallingConvention(type.CallingConvention);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
AssembleCallingConvention(type.ReturnType, buf);
|
||||
return buf.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private string NormalizeCallingConvention(MethodCallingConvention callingConvention)
|
||||
{
|
||||
if (callingConvention == MethodCallingConvention.C) return "Cdecl";
|
||||
var callConv = callingConvention.ToString().ToLower();
|
||||
return char.ToUpper(callConv[0]) + callConv.Substring(1);
|
||||
}
|
||||
|
||||
private void AssembleCallingConvention(TypeReference type, StringBuilder buf)
|
||||
{
|
||||
if (!(type is OptionalModifierType optionalModifierType)) return;
|
||||
|
||||
var modifier = optionalModifierType.ModifierType.FullName;
|
||||
if (modifier.StartsWith(Consts.CallConvPrefix))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(buf.ToString())) buf.Append(", ");
|
||||
buf.Append(modifier.Substring(Consts.CallConvPrefix.Length));
|
||||
AssembleCallingConvention(optionalModifierType.ElementType, buf);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldStripModFromTypeName
|
||||
{
|
||||
get => true;
|
||||
|
@ -168,6 +232,11 @@ namespace Mono.Documentation.Updater
|
|||
AppendPointerTypeName (interimBuilder, type, context);
|
||||
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
|
||||
}
|
||||
if (type is FunctionPointerType functionPointerType)
|
||||
{
|
||||
AppendFunctionPointerTypeName(interimBuilder, functionPointerType, context);
|
||||
return SetBuffer(buf, interimBuilder, useTypeProjection: useTypeProjection);
|
||||
}
|
||||
if (type is GenericParameter)
|
||||
{
|
||||
AppendTypeName (interimBuilder, type, context);
|
||||
|
@ -562,15 +631,14 @@ namespace Mono.Documentation.Updater
|
|||
}
|
||||
|
||||
|
||||
private StringBuilder AppendReturnTypeName (StringBuilder buf, MethodDefinition method)
|
||||
protected StringBuilder AppendReturnTypeName (StringBuilder buf, IMethodSignature method, bool noTrailingSpace = false)
|
||||
{
|
||||
var context = AttributeParserContext.Create (method.MethodReturnType);
|
||||
var isNullableType = context.IsNullable ();
|
||||
var returnTypeName = GetTypeName (method.ReturnType, context);
|
||||
buf.Append (returnTypeName);
|
||||
buf.Append (GetTypeNullableSymbol (method.ReturnType, isNullableType));
|
||||
buf.Append (" ");
|
||||
|
||||
buf.Append (noTrailingSpace ? "" : " ");
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -604,6 +672,11 @@ namespace Mono.Documentation.Updater
|
|||
return buf;
|
||||
}
|
||||
|
||||
protected virtual StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameterDef)
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected virtual StringBuilder AppendGenericMethodConstraints (StringBuilder buf, MethodDefinition method)
|
||||
{
|
||||
return buf;
|
||||
|
|
|
@ -562,7 +562,7 @@ namespace Mono.Documentation.Updater
|
|||
return buf.Append(end);
|
||||
}
|
||||
|
||||
private StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
protected override StringBuilder AppendParameter(StringBuilder buf, ParameterDefinition parameter)
|
||||
{
|
||||
if (parameter.IsOptional)
|
||||
{
|
||||
|
|
|
@ -483,6 +483,35 @@ namespace mdoc.Test
|
|||
Assert.AreEqual(expectedSignature, methodSignature);
|
||||
}
|
||||
|
||||
[TestCase("UnsafeCombine", "public static R UnsafeCombine<T1,T2,R> (delegate*<T1, T2, R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine1", "public static R UnsafeCombine1<T1,T2,R> (delegate* unmanaged[Cdecl]<T1, T2, R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine2", "public static R UnsafeCombine2<T1,T2,T3,R> (delegate* unmanaged[Stdcall]<ref T1, in T2, out T3, R> combinator, T1 left, T2 right, T3 outVar);")]
|
||||
[TestCase("UnsafeCombine3", "public static R UnsafeCombine3<T1,T2,R> (delegate* unmanaged[Fastcall]<T1, T2, ref R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine4", "public static R UnsafeCombine4<T1,T2,R> (delegate* unmanaged[Thiscall]<T1, T2, ref readonly R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine5", "public static void UnsafeCombine5 (delegate* unmanaged[Cdecl]<void> combinator);")]
|
||||
[TestCase("UnsafeCombine6", "public static void UnsafeCombine6 (delegate*<delegate* unmanaged[Fastcall]<string, int>, delegate*<string, int>> combinator);")]
|
||||
[TestCase("UnsafeCombine7", "public static delegate*<delegate* unmanaged[Thiscall]<string, int>, delegate*<string, int>> UnsafeCombine7 ();")]
|
||||
public void CSharpFuctionPointersTest(string methodName, string expectedSignature)
|
||||
{
|
||||
var method = GetMethod(typeof(SampleClasses.FunctionPointers), m => m.Name == methodName);
|
||||
var methodSignature = formatter.GetDeclaration(method);
|
||||
Assert.AreEqual(expectedSignature, methodSignature);
|
||||
}
|
||||
|
||||
[TestCase("UnsafeCombine1", "public static R UnsafeCombine1<T1,T2,R> (delegate* unmanaged<T1, T2, R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine2", "public static R UnsafeCombine2<T1,T2,R> (delegate* unmanaged[Cdecl, SuppressGCTransition]<T1, T2, R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine3", "public static R UnsafeCombine3<T1,T2,R> (delegate* unmanaged[Stdcall, MemberFunction]<T1, T2, R> combinator, T1 left, T2 right);")]
|
||||
[TestCase("UnsafeCombine4", "public static void UnsafeCombine4 (delegate*<delegate* unmanaged[Cdecl, Fastcall]<string, int>, delegate*<string, int>> combinator);")]
|
||||
[TestCase("UnsafeCombine5", "public static delegate* unmanaged[Cdecl, Fastcall]<delegate* unmanaged[Thiscall, MemberFunction]<string, int>, delegate*<string, int>> UnsafeCombine5 ();")]
|
||||
public void CSharpFuctionPointersUnmanagedExtTest(string methodName, string expectedSignature)
|
||||
{
|
||||
var functionPointersDllPath = "../../../../external/Test/FunctionPointersTest.dll";
|
||||
var type = GetType(functionPointersDllPath, "FunctionPointersTest.FunctionPointers");
|
||||
var method = GetMethod(type, m => m.Name == methodName);
|
||||
var methodSignature = formatter.GetDeclaration(method);
|
||||
Assert.AreEqual(expectedSignature, methodSignature);
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
string RealTypeName(string name){
|
||||
switch (name) {
|
||||
|
|
|
@ -48,6 +48,18 @@ namespace mdoc.Test
|
|||
|
||||
Assert.AreEqual("Mono_DocTest_Generic.GenericBase<U>", parameterType);
|
||||
}
|
||||
|
||||
[TestCase("UnsafeCombine", "delegate*<T1, T2, R>")]
|
||||
[TestCase("UnsafeCombineOverload", "delegate*<System.IntPtr, System.UIntPtr, R>")]
|
||||
public void Test_GetDocParameterType_CSharpFunctionPointer(string methodName, string expected)
|
||||
{
|
||||
var method = GetMethod(typeof(SampleClasses.FunctionPointers), methodName);
|
||||
|
||||
string parameterType = MDocUpdater.GetDocParameterType(method.Parameters[0].ParameterType);
|
||||
|
||||
Assert.AreEqual(expected, parameterType);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Test_GetNamespace_IgnoredNamespaceGeneric()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
using System;
|
||||
|
||||
namespace mdoc.Test.SampleClasses
|
||||
{
|
||||
public class FunctionPointers
|
||||
{
|
||||
public unsafe static R UnsafeCombine<T1, T2, R>(delegate*<T1, T2, R> combinator, T1 left, T2 right) =>
|
||||
combinator(left, right);
|
||||
|
||||
public unsafe static R UnsafeCombineOverload<R>(delegate*<IntPtr, UIntPtr, R> combinator, IntPtr left, UIntPtr right) =>
|
||||
combinator(left, right);
|
||||
|
||||
public unsafe static R UnsafeCombine1<T1, T2, R>(delegate* unmanaged[Cdecl]<T1, T2, R> combinator, T1 left, T2 right) =>
|
||||
combinator(left, right);
|
||||
|
||||
public unsafe static R UnsafeCombine2<T1, T2, T3, R>(delegate* unmanaged[Stdcall]<ref T1, in T2, out T3, R> combinator, T1 left, T2 right, T3 outVar) =>
|
||||
combinator(ref left, right, out outVar);
|
||||
|
||||
public unsafe static R UnsafeCombine3<T1, T2, R>(delegate* unmanaged[Fastcall]<T1, T2, ref R> combinator, T1 left, T2 right) =>
|
||||
combinator(left, right);
|
||||
|
||||
public unsafe static R UnsafeCombine4<T1, T2, R>(delegate* unmanaged[Thiscall]<T1, T2, ref readonly R> combinator, T1 left, T2 right) =>
|
||||
combinator(left, right);
|
||||
|
||||
public unsafe static void UnsafeCombine5(delegate* unmanaged[Cdecl]<void> combinator) => combinator();
|
||||
|
||||
public unsafe static void UnsafeCombine6(delegate*<delegate* unmanaged[Fastcall]<string, int>, delegate*<string, int>> combinator) => combinator(null);
|
||||
|
||||
public unsafe static delegate*<delegate* unmanaged[Thiscall]<string, int>, delegate*<string, int>> UnsafeCombine7() => throw null;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<RunPostBuildEvent>Always</RunPostBuildEvent>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="mdoc.Test.Cplusplus, Version=1.0.6709.28740, Culture=neutral, processorArchitecture=x86">
|
||||
|
|
Загрузка…
Ссылка в новой задаче