Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/api-doc-tools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMin Huang <huangmin@microsoft.com>2022-04-18 04:55:57 +0300
committerGitHub <noreply@github.com>2022-04-18 04:55:57 +0300
commit93806a9b54abe8c61399d47f9c087c69fc07ef85 (patch)
tree31fd19aa204a0e98fc71100aa61df3d5353541cd
parentb5f62fcd1677c565fbf47cf8b062c5bba4d3c649 (diff)
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
-rw-r--r--external/Test/FunctionPointersTest.dllbin0 -> 6656 bytes
-rw-r--r--mdoc/Consts.cs2
-rw-r--r--mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs3
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs28
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs2
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs10
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/FSharpFormatter.cs3
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs7
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/JsFormatter.cs5
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs79
-rw-r--r--mdoc/Mono.Documentation/Updater/Formatters/VBFullMemberFormatter.cs2
-rw-r--r--mdoc/mdoc.Test/FormatterTests.cs29
-rw-r--r--mdoc/mdoc.Test/MDocUpdaterTests.cs12
-rw-r--r--mdoc/mdoc.Test/SampleClasses/FunctionPointers.cs31
-rw-r--r--mdoc/mdoc.Test/mdoc.Test.csproj1
15 files changed, 196 insertions, 18 deletions
diff --git a/external/Test/FunctionPointersTest.dll b/external/Test/FunctionPointersTest.dll
new file mode 100644
index 00000000..579f3cdc
--- /dev/null
+++ b/external/Test/FunctionPointersTest.dll
Binary files differ
diff --git a/mdoc/Consts.cs b/mdoc/Consts.cs
index 58ea9e65..aba2fd0f 100644
--- a/mdoc/Consts.cs
+++ b/mdoc/Consts.cs
@@ -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";
}
}
diff --git a/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs b/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
index 90f533ef..3b710a98 100644
--- a/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
+++ b/mdoc/Mono.Documentation/Updater/DocumentationEnumerator.cs
@@ -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;
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs
index 06368855..ec625447 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/CSharpFullMemberFormatter.cs
@@ -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)
{
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs
index 516c9a93..fbbc9e93 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/CppFormatters/CppFullMemberFormatter.cs
@@ -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)
{
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs
index 5c370b5d..69a36d02 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/DocTypeFullMemberFormatter.cs
@@ -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));
+ }
}
} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/FSharpFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/FSharpFormatter.cs
index f810f1dd..f4725a6c 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/FSharpFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/FSharpFormatter.cs
@@ -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)
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs
index ad30e238..b54e64d9 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/ILFullMemberFormatter.cs
@@ -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");
+ }
}
} \ No newline at end of file
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/JsFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/JsFormatter.cs
index dabceb3e..b7f4d25c 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/JsFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/JsFormatter.cs
@@ -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()
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs
index 6e1ca5bd..9e057319 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/MemberFormatter.cs
@@ -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;
diff --git a/mdoc/Mono.Documentation/Updater/Formatters/VBFullMemberFormatter.cs b/mdoc/Mono.Documentation/Updater/Formatters/VBFullMemberFormatter.cs
index 6d9761f8..dbe5890b 100644
--- a/mdoc/Mono.Documentation/Updater/Formatters/VBFullMemberFormatter.cs
+++ b/mdoc/Mono.Documentation/Updater/Formatters/VBFullMemberFormatter.cs
@@ -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)
{
diff --git a/mdoc/mdoc.Test/FormatterTests.cs b/mdoc/mdoc.Test/FormatterTests.cs
index b39d10c4..0c442f02 100644
--- a/mdoc/mdoc.Test/FormatterTests.cs
+++ b/mdoc/mdoc.Test/FormatterTests.cs
@@ -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) {
diff --git a/mdoc/mdoc.Test/MDocUpdaterTests.cs b/mdoc/mdoc.Test/MDocUpdaterTests.cs
index 6924df61..4f52a7d8 100644
--- a/mdoc/mdoc.Test/MDocUpdaterTests.cs
+++ b/mdoc/mdoc.Test/MDocUpdaterTests.cs
@@ -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()
{
diff --git a/mdoc/mdoc.Test/SampleClasses/FunctionPointers.cs b/mdoc/mdoc.Test/SampleClasses/FunctionPointers.cs
new file mode 100644
index 00000000..9e84c0b8
--- /dev/null
+++ b/mdoc/mdoc.Test/SampleClasses/FunctionPointers.cs
@@ -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;
+ }
+} \ No newline at end of file
diff --git a/mdoc/mdoc.Test/mdoc.Test.csproj b/mdoc/mdoc.Test/mdoc.Test.csproj
index ae540c68..422b12fe 100644
--- a/mdoc/mdoc.Test/mdoc.Test.csproj
+++ b/mdoc/mdoc.Test/mdoc.Test.csproj
@@ -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">