- Add IL based test for copy ctor to allow testing in Crossgen2 which doesn't yet support IJW
- Add api to determine proper EmbeddedSignatureData index for these
- And tests to ensure this remains functional
- Marshaller changes to recognize copy constructor cases
- Note that there isn't actual marshaller support. Instead the compiler can now able to detect when copy ctors should be used, and falls back to the runtime interop layer
// Value of <see cref="EmbeddedSignatureData.index" /> for any custom modifiers on the return type
public const string IndexOfCustomModifiersOnReturnType = "0.1.1.1";
+ // Value of <see cref="EmbeddedSignatureData.index" /> for any custom modifiers on
+ // SomeStruct when SomeStruct *, or SomeStruct & is the type of a parameter or return type
+ // Parameter index 0 represents the return type, and indices 1-n represent the parameters to the signature
+ public static string GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(int parameterIndex)
+ {
+ return $"0.1.1.2.{(parameterIndex + 1).ToStringInvariant()}.1";
+ }
+
public MethodSignature(MethodSignatureFlags flags, int genericParameterCount, TypeDesc returnType, TypeDesc[] parameters, EmbeddedSignatureData[] embeddedSignatureData = null)
{
_flags = flags;
}
}
+ private static bool HasCopyConstructorCustomModifier(int? parameterIndex,
+ EmbeddedSignatureData[] customModifierData)
+ {
+ if (!parameterIndex.HasValue || customModifierData == null)
+ return false;
+
+ string customModifierIndex = MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(parameterIndex.Value);
+ foreach (var customModifier in customModifierData)
+ {
+ if (customModifier.kind != EmbeddedSignatureDataKind.RequiredCustomModifier)
+ continue;
+
+ if (customModifier.index != customModifierIndex)
+ continue;
+
+ var customModifierType = customModifier.type as DefType;
+ if (customModifierType == null)
+ continue;
+
+ if ((customModifierType.Namespace == "System.Runtime.CompilerServices" && customModifierType.Name == "IsCopyConstructed") ||
+ (customModifierType.Namespace == "Microsoft.VisualC" && customModifierType.Name == "NeedsCopyConstructorModifier"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
internal static MarshallerKind GetMarshallerKind(
- TypeDesc type,
- MarshalAsDescriptor marshalAs,
- bool isReturn,
- bool isAnsi,
- MarshallerType marshallerType,
- out MarshallerKind elementMarshallerKind)
+ TypeDesc type,
+ int? parameterIndex,
+ EmbeddedSignatureData[] customModifierData,
+ MarshalAsDescriptor marshalAs,
+ bool isReturn,
+ bool isAnsi,
+ MarshallerType marshallerType,
+ out MarshallerKind elementMarshallerKind)
{
elementMarshallerKind = MarshallerKind.Invalid;
type = type.GetParameterType();
+ if (!type.IsPrimitive && type.IsValueType && marshallerType != MarshallerType.Field
+ && HasCopyConstructorCustomModifier(parameterIndex, customModifierData))
+ {
+ return MarshallerKind.BlittableValueClassWithCopyCtor;
+ }
+
// Compat note: CLR allows ref returning blittable structs for IJW
if (isReturn)
return MarshallerKind.Invalid;
else if (type.IsPointer)
{
if (nativeType == NativeTypeKind.Default)
+ {
+ var pointedAtType = type.GetParameterType();
+ if (!pointedAtType.IsPrimitive && !type.IsEnum && marshallerType != MarshallerType.Field
+ && HasCopyConstructorCustomModifier(parameterIndex, customModifierData))
+ {
+ return MarshallerKind.BlittableValueClassWithCopyCtor;
+ }
return MarshallerKind.BlittableValue;
+ }
else
return MarshallerKind.Invalid;
}
MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
field.FieldType,
+ parameterIndex : null,
+ customModifierData: null,
field.GetMarshalAsDescriptor(),
isReturn: false,
isAnsi: mdType.PInvokeStringFormat == PInvokeStringFormat.AnsiClass,
AsAnyA,
AsAnyW,
ComInterface,
+ BlittableValueClassWithCopyCtor,
Invalid
}
public enum MarshalDirection
/// <param name="parameterType">type of the parameter to marshal</param>
/// <returns>The created Marshaller</returns>
public static Marshaller CreateMarshaller(TypeDesc parameterType,
+ int? parameterIndex,
+ EmbeddedSignatureData[] customModifierData,
MarshallerType marshallerType,
MarshalAsDescriptor marshalAs,
MarshalDirection direction,
{
MarshallerKind elementMarshallerKind;
MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(parameterType,
+ parameterIndex,
+ customModifierData,
marshalAs,
isReturn,
flags.CharSet == CharSet.Ansi,
TypeDesc parameterType = (i == 0) ? methodSig.ReturnType : methodSig[i - 1]; //first item is the return type
marshallers[i] = CreateMarshaller(parameterType,
+ parameterIndex,
+ methodSig.GetEmbeddedSignatureData(),
MarshallerType.Argument,
parameterMetadata.MarshalAsDescriptor,
direction,
MarshallerKind marshallerKind = MarshalHelpers.GetMarshallerKind(
parameterType,
+ parameterIndex: i,
+ customModifierData: methodSig.GetEmbeddedSignatureData(),
parameterMetadata.MarshalAsDescriptor,
parameterMetadata.Return,
isAnsi: true,
{
ret
}
+
+ .method public hidebysig instance int32 modopt([CoreTestAssembly]System.Void) & Method3(int32 modopt(FooModifier)*, int32 modopt(FooModifier)*) cil managed
+ {
+ ret
+ }
}
.class private auto ansi beforefieldinit Atom
Assert.Equal("OptionalCustomModifier0.1.1.1CharOptionalCustomModifier0.1.1.2.1.1VoidOptionalCustomModifier0.1.2.1FooModifier", GetModOptMethodSignatureInfo(methodWithModOptAtStartOfSigAndAfterByRef));
}
+ [Fact]
+ public void TestSignatureMatchesModoptOnPointerOrRefModifiedType()
+ {
+ MetadataType modOptTester = _testModule.GetType("", "ModOptTester");
+ MethodSignature methodWithModOpt = modOptTester.GetMethods().Single(m => string.Equals(m.Name, "Method3")).Signature;
+ Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(0), methodWithModOpt.GetEmbeddedSignatureData()[0].index);
+ Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(1), methodWithModOpt.GetEmbeddedSignatureData()[1].index);
+ Assert.Equal(MethodSignature.GetIndexOfCustomModifierOnPointedAtTypeByParameterIndex(2), methodWithModOpt.GetEmbeddedSignatureData()[2].index);
+ }
+
[Fact]
public void TestSignatureMatches()
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using TestLibrary;
+
+static unsafe class CopyCtor
+{
+ public static unsafe int StructWithCtorTest(StructWithCtor* ptrStruct, ref StructWithCtor refStruct)
+ {
+ if (ptrStruct->_instanceField != 1)
+ return 1;
+ if (refStruct._instanceField != 2)
+ return 2;
+
+ if (StructWithCtor.CopyCtorCallCount != 2)
+ return 3;
+ if (StructWithCtor.DtorCallCount != 2)
+ return 4;
+
+
+ return 100;
+ }
+
+ [UnmanagedFunctionPointerAttribute(CallingConvention.StdCall)]
+ public delegate int TestDelegate(StructWithCtor* ptrStruct, ref StructWithCtor refStruct);
+
+ public static unsafe int Main()
+ {
+ TestDelegate del = (TestDelegate)StructWithCtorTest;
+ StructWithCtor s1 = new StructWithCtor();
+ StructWithCtor s2 = new StructWithCtor();
+ s1._instanceField = 1;
+ s2._instanceField = 2;
+ int returnVal = FunctionPointer.Call_FunctionPointer(Marshal.GetFunctionPointerForDelegate(del), &s1, ref s2);
+
+ GC.KeepAlive(del);
+
+ return returnVal;
+ }
+}
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="CopyCtorTest.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="CopyCtorUtil.ilproj" />
+ </ItemGroup>
+</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+.assembly extern mscorlib { }
+.assembly extern System.Runtime.CompilerServices.VisualC { }
+.assembly CopyCtorUtil { }
+
+.class public sealed sequential ansi beforefieldinit StructWithCtor
+ extends [mscorlib]System.ValueType
+{
+ .field public int32 _instanceField
+ .field public static int32 CopyCtorCallCount
+ .field public static int32 DtorCallCount
+
+ .method public specialname static void '<MarshalCopy>'(valuetype StructWithCtor* A_0,
+ valuetype StructWithCtor* A_1) cil managed
+ {
+ ldarg.0
+ ldarg.1
+ ldfld int32 StructWithCtor::_instanceField
+ stfld int32 StructWithCtor::_instanceField
+
+ ldsfld int32 StructWithCtor::CopyCtorCallCount
+ ldc.i4.1
+ add
+ stsfld int32 StructWithCtor::CopyCtorCallCount
+ ret
+ }
+
+ .method public specialname static void '<MarshalDestroy>'(valuetype StructWithCtor* A_0) cil managed
+ {
+ ldsfld int32 StructWithCtor::DtorCallCount
+ ldc.i4.1
+ add
+ stsfld int32 StructWithCtor::DtorCallCount
+ ret
+ }
+}
+
+.class public auto ansi beforefieldinit FunctionPointer
+ extends [mscorlib]System.Object
+{
+ .method hidebysig specialname rtspecialname
+ instance void .ctor() cil managed
+ {
+ ldarg.0
+ call instance void [mscorlib]System.Object::.ctor()
+ ret
+ }
+
+ .method public hidebysig static int32 Call_FunctionPointer(native int fptr,
+ valuetype StructWithCtor* arg1,
+ valuetype StructWithCtor& arg2) cil managed
+ {
+ .maxstack 8
+ ldarg.1
+ ldarg.2
+ ldarg.0
+ calli unmanaged stdcall int32 (valuetype StructWithCtor modreq([mscorlib]System.Runtime.CompilerServices.IsCopyConstructed)*, valuetype StructWithCtor modreq([mscorlib]System.Runtime.CompilerServices.IsCopyConstructed)&)
+ ret
+ }
+}
+
+
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk.IL">
+ <PropertyGroup>
+ <OutputType>library</OutputType>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="CopyCtorUtil.il" />
+ </ItemGroup>
+</Project>