未验证 提交 7f0fdd08 编写于 作者: O Omar Tawfik 提交者: GitHub

Marshalling methods with `in` parameters should have [In] attribute (#23031)

* Marshalling methods with `in` parameters should have [In] attribute

* Apply [In] on all `in` parameters

* More tests

* nit
上级 18cc4e45
......@@ -943,20 +943,11 @@ internal override bool IsMetadataOptional
}
}
internal sealed override bool IsMetadataIn => GetDecodedWellKnownAttributeData()?.HasInAttribute == true;
internal sealed override bool IsMetadataIn
=> base.IsMetadataIn || GetDecodedWellKnownAttributeData()?.HasInAttribute == true;
internal sealed override bool IsMetadataOut
{
get
{
if (this.RefKind == RefKind.Out)
{
return true;
}
return GetDecodedWellKnownAttributeData()?.HasOutAttribute == true;
}
}
=> base.IsMetadataOut || GetDecodedWellKnownAttributeData()?.HasOutAttribute == true;
internal sealed override MarshalPseudoCustomAttributeData MarshallingInformation
=> GetDecodedWellKnownAttributeData()?.MarshallingInformation;
......
......@@ -262,5 +262,9 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r
AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeIsReadOnlyAttribute(this));
}
}
internal override bool IsMetadataIn => RefKind == RefKind.In;
internal override bool IsMetadataOut => RefKind == RefKind.Out;
}
}
......@@ -62,16 +62,6 @@ internal override bool IsExtensionMethodThis
get { return false; }
}
internal override bool IsMetadataIn
{
get { return false; }
}
internal override bool IsMetadataOut
{
get { return RefKind == RefKind.Out; }
}
internal override bool IsIDispatchConstant
{
get { return false; }
......
......@@ -50,15 +50,9 @@ public override RefKind RefKind
get { return _refKind; }
}
internal override bool IsMetadataIn
{
get { return false; }
}
internal override bool IsMetadataIn => RefKind == RefKind.In;
internal override bool IsMetadataOut
{
get { return _refKind == RefKind.Out; }
}
internal override bool IsMetadataOut => RefKind == RefKind.Out;
internal override MarshalPseudoCustomAttributeData MarshallingInformation
{
......
......@@ -2485,5 +2485,415 @@ public void PEHeaders2()
Assert.Equal(0x2000, sections[0].VirtualAddress);
Assert.Equal(832, sections[0].VirtualSize);
}
[Fact]
public void InParametersShouldHaveMetadataIn_TypeMethods()
{
var text = @"
using System.Runtime.InteropServices;
class T
{
public void M(in int a, [In]in int b, [In]int c, int d) {}
}";
Action<ModuleSymbol> verifier = module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("T").GetMethod("M").GetParameters();
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
};
CompileAndVerify(text, sourceSymbolValidator: verifier, symbolValidator: verifier);
}
[Fact]
public void InParametersShouldHaveMetadataIn_IndexerMethods()
{
var text = @"
using System.Runtime.InteropServices;
class T
{
public int this[in int a, [In]in int b, [In]int c, int d] => 0;
}";
Action<ModuleSymbol> verifier = module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("T").GetMethod("get_Item").GetParameters();
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
};
CompileAndVerify(text, sourceSymbolValidator: verifier, symbolValidator: verifier);
}
[Fact]
public void InParametersShouldHaveMetadataIn_Delegates()
{
var text = @"
using System.Runtime.InteropServices;
public delegate void D(in int a, [In]in int b, [In]int c, int d);
public class C
{
public void M()
{
N((in int a, in int b, int c, int d) => {});
}
public void N(D lambda) { }
}
";
CompileAndVerify(text,
options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All),
sourceSymbolValidator: module =>
{
var parameters = module.ContainingAssembly.GetTypeByMetadataName("D").DelegateInvokeMethod.Parameters;
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
},
symbolValidator: module =>
{
var delegateParameters = module.ContainingAssembly.GetTypeByMetadataName("D").DelegateInvokeMethod.Parameters;
Assert.Equal(4, delegateParameters.Length);
Assert.True(delegateParameters[0].IsMetadataIn);
Assert.True(delegateParameters[1].IsMetadataIn);
Assert.True(delegateParameters[2].IsMetadataIn);
Assert.False(delegateParameters[3].IsMetadataIn);
var lambdaParameters = module.GlobalNamespace.GetTypeMember("C").GetTypeMember("<>c").GetMethod("<M>b__0_0").Parameters;
Assert.Equal(4, lambdaParameters.Length);
Assert.True(lambdaParameters[0].IsMetadataIn);
Assert.True(lambdaParameters[1].IsMetadataIn);
Assert.False(lambdaParameters[2].IsMetadataIn);
Assert.False(lambdaParameters[3].IsMetadataIn);
});
}
[Fact]
public void InParametersShouldHaveMetadataIn_LocalFunctions()
{
var text = @"
using System.Runtime.InteropServices;
public class C
{
public void M()
{
void local(in int a, int c) { }
}
}
";
CompileAndVerify(text, options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All), symbolValidator: module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("C").GetMember("<M>g__local|0_0").GetParameters();
Assert.Equal(2, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.False(parameters[1].IsMetadataIn);
});
}
[Fact]
public void InParametersShouldHaveMetadataIn_ExternMethods()
{
var text = @"
using System.Runtime.InteropServices;
class T
{
[DllImport(""Other.dll"")]
public static extern void M(in int a, [In]in int b, [In]int c, int d);
}";
Action<ModuleSymbol> verifier = module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("T").GetMethod("M").GetParameters();
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
};
CompileAndVerify(text, sourceSymbolValidator: verifier, symbolValidator: verifier);
}
[Fact]
public void InParametersShouldHaveMetadataIn_NoPIA()
{
var comAssembly = CreateStandardCompilation(@"
using System;
using System.Runtime.InteropServices;
[assembly: ImportedFromTypeLib(""test.dll"")]
[assembly: Guid(""6681dcd6-9c3e-4c3a-b04a-aef3ee85c2cf"")]
[ComImport()]
[Guid(""6681dcd6-9c3e-4c3a-b04a-aef3ee85c2cf"")]
public interface T
{
void M(in int a, [In]in int b, [In]int c, int d);
}");
CompileAndVerify(comAssembly, symbolValidator: module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("T").GetMethod("M").GetParameters();
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
});
var code = @"
class User
{
public void M(T obj)
{
obj.M(1, 2, 3, 4);
}
}";
CompileAndVerify(
source: code,
options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All),
additionalRefs: new[] { comAssembly.EmitToImageReference(embedInteropTypes: true) },
symbolValidator: module =>
{
var parameters = module.GlobalNamespace.GetTypeMember("T").GetMethod("M").GetParameters();
Assert.Equal(4, parameters.Length);
Assert.True(parameters[0].IsMetadataIn);
Assert.True(parameters[1].IsMetadataIn);
Assert.True(parameters[2].IsMetadataIn);
Assert.False(parameters[3].IsMetadataIn);
});
}
[Fact]
public void ExtendingInParametersFromParentWithoutInAttributeWorksWithoutErrors()
{
var reference = CompileIL(@"
.class private auto ansi sealed beforefieldinit Microsoft.CodeAnalysis.EmbeddedAttribute extends [mscorlib]System.Attribute
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
.custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = (01 00 00 00)
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Attribute::.ctor()
IL_0006: nop
IL_0007: ret
}
}
.class private auto ansi sealed beforefieldinit System.Runtime.CompilerServices.IsReadOnlyAttribute extends [mscorlib]System.Attribute
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (01 00 00 00)
.custom instance void Microsoft.CodeAnalysis.EmbeddedAttribute::.ctor() = (01 00 00 00)
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Attribute::.ctor()
IL_0006: nop
IL_0007: ret
}
}
.class public auto ansi beforefieldinit Parent extends [mscorlib]System.Object
{
.method public hidebysig newslot virtual instance void M (
int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute) a,
int32& modreq([mscorlib]System.Runtime.InteropServices.InAttribute) b,
int32 c,
int32 d) cil managed
{
.param [1] .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (01 00 00 00)
.param [2] .custom instance void System.Runtime.CompilerServices.IsReadOnlyAttribute::.ctor() = (01 00 00 00)
.maxstack 8
IL_0000: nop
IL_0001: ldstr ""Parent called""
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
}
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void[mscorlib] System.Object::.ctor()
IL_0006: nop
IL_0007: ret
}
}");
var comp = CreateStandardCompilation(@"
using System;
using System.Runtime.InteropServices;
public class Child : Parent
{
public override void M(in int a, [In]in int b, [In]int c, int d)
{
base.M(a, b, c, d);
Console.WriteLine(""Child called"");
}
}
public static class Program
{
public static void Main()
{
var obj = new Child();
obj.M(1, 2, 3, 4);
}
}", new[] { reference }, TestOptions.ReleaseExe);
var parentParameters = comp.GetTypeByMetadataName("Parent").GetMethod("M").GetParameters();
Assert.Equal(4, parentParameters.Length);
Assert.False(parentParameters[0].IsMetadataIn);
Assert.False(parentParameters[1].IsMetadataIn);
Assert.False(parentParameters[2].IsMetadataIn);
Assert.False(parentParameters[3].IsMetadataIn);
var expectedOutput =
@"Parent called
Child called";
CompileAndVerify(comp, expectedOutput: expectedOutput, symbolValidator: module =>
{
var childParameters = module.ContainingAssembly.GetTypeByMetadataName("Child").GetMethod("M").GetParameters();
Assert.Equal(4, childParameters.Length);
Assert.True(childParameters[0].IsMetadataIn);
Assert.True(childParameters[1].IsMetadataIn);
Assert.True(childParameters[2].IsMetadataIn);
Assert.False(childParameters[3].IsMetadataIn);
});
}
[Fact]
public void GeneratingProxyForVirtualMethodInParentCopiesMetadataBitsCorrectly_OutAttribute()
{
var reference = CreateStandardCompilation(@"
using System.Runtime.InteropServices;
public class Parent
{
public void M(out int a, [Out] int b) => throw null;
}");
CompileAndVerify(reference, symbolValidator: module =>
{
var sourceParentParameters = module.GlobalNamespace.GetTypeMember("Parent").GetMethod("M").GetParameters();
Assert.Equal(2, sourceParentParameters.Length);
Assert.True(sourceParentParameters[0].IsMetadataOut);
Assert.True(sourceParentParameters[1].IsMetadataOut);
});
var source = @"
using System.Runtime.InteropServices;
public interface IParent
{
void M(out int a, [Out] int b);
}
public class Child : Parent, IParent
{
}";
CompileAndVerify(
source: source,
additionalRefs: new[] { reference.EmitToImageReference() },
options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All),
symbolValidator: module =>
{
var interfaceParameters = module.GlobalNamespace.GetTypeMember("IParent").GetMethod("M").GetParameters();
Assert.Equal(2, interfaceParameters.Length);
Assert.True(interfaceParameters[0].IsMetadataOut);
Assert.True(interfaceParameters[1].IsMetadataOut);
var proxyChildParameters = module.GlobalNamespace.GetTypeMember("Child").GetMethod("IParent.M").GetParameters();
Assert.Equal(2, proxyChildParameters.Length);
Assert.True(proxyChildParameters[0].IsMetadataOut);
Assert.False(proxyChildParameters[1].IsMetadataOut); // User placed attributes are not copied.
});
}
[Fact]
public void GeneratingProxyForVirtualMethodInParentCopiesMetadataBitsCorrectly_InAttribute()
{
var reference = CreateStandardCompilation(@"
using System.Runtime.InteropServices;
public class Parent
{
public void M(in int a, [In] int b) => throw null;
}");
CompileAndVerify(reference, symbolValidator: module =>
{
var sourceParentParameters = module.GlobalNamespace.GetTypeMember("Parent").GetMethod("M").GetParameters();
Assert.Equal(2, sourceParentParameters.Length);
Assert.True(sourceParentParameters[0].IsMetadataIn);
Assert.True(sourceParentParameters[1].IsMetadataIn);
});
var source = @"
using System.Runtime.InteropServices;
public interface IParent
{
void M(in int a, [In] int b);
}
public class Child : Parent, IParent
{
}";
CompileAndVerify(
source: source,
additionalRefs: new[] { reference.EmitToImageReference() },
options: TestOptions.ReleaseDll.WithMetadataImportOptions(MetadataImportOptions.All),
symbolValidator: module =>
{
var interfaceParameters = module.GlobalNamespace.GetTypeMember("IParent").GetMethod("M").GetParameters();
Assert.Equal(2, interfaceParameters.Length);
Assert.True(interfaceParameters[0].IsMetadataIn);
Assert.True(interfaceParameters[1].IsMetadataIn);
var proxyChildParameters = module.GlobalNamespace.GetTypeMember("Child").GetMethod("IParent.M").GetParameters();
Assert.Equal(2, proxyChildParameters.Length);
Assert.True(proxyChildParameters[0].IsMetadataIn);
Assert.False(proxyChildParameters[1].IsMetadataIn); // User placed attributes are not copied.
});
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册