|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.CSharp.UnitTests.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
public partial class CodeGenMscorlibTests : EmitMetadataTestBase
{
[WorkItem(544591, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544591")]
[WorkItem(544609, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544609")]
[WorkItem(544595, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544595")]
[WorkItem(544596, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544596")]
[WorkItem(544624, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544624")]
[WorkItem(544592, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544592")]
[WorkItem(544927, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544927")]
[Fact]
public void CoreLibrary1()
{
var text =
@"namespace System
{
public class Object { }
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
public struct Byte { private Byte m_value; Byte Use(Byte b) { m_value = b; return m_value; } }
public struct Int16 { private Int16 m_value; Int16 Use(Int16 b) { m_value = b; return m_value; } }
public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } }
public struct Int64 { private Int64 m_value; Int64 Use(Int64 b) { m_value = b; return m_value; } }
public struct UInt16 { private UInt16 m_value; UInt16 Use(UInt16 b) { m_value = b; return m_value; } }
public struct UInt32 { private UInt32 m_value; UInt32 Use(UInt32 b) { m_value = b; return m_value; } }
public struct UInt64 { private UInt64 m_value; UInt64 Use(UInt64 b) { m_value = b; return m_value; } }
public struct Single { private Single m_value; Single Use(Single b) { m_value = b; return m_value; } }
public struct Double { private Double m_value; Double Use(Double b) { m_value = b; return m_value; } }
public struct Char { private Char m_value; Char Use(Char b) { m_value = b; return m_value; } }
public struct SByte { private SByte m_value; SByte Use(SByte b) { m_value = b; return m_value; } }
public struct UIntPtr { private UIntPtr m_value; UIntPtr Use(UIntPtr b) { m_value = b; return m_value; } }
public struct IntPtr {
unsafe private void* m_value;
public unsafe IntPtr(void* value) { this.m_value = value; }
unsafe void* Use() { return m_value; }
}
public class String { }
public class Array { }
public class Exception { }
public class Type { }
public abstract class Enum : ValueType { }
public abstract class Delegate { }
public abstract class MulticastDelegate : Delegate { }
public abstract class Attribute
{
protected Attribute() { }
}
public struct Nullable<T> where T : struct
{
private bool hasValue;
internal T value;
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public bool HasValue
{
get
{
return hasValue;
}
}
public T Value
{
get
{
return value;
}
}
public static implicit operator T?(T value)
{
return new Nullable<T>(value);
}
public static explicit operator T(T? value)
{
return value.Value;
}
}
public class ParamArrayAttribute : Attribute { }
public interface IDisposable { }
public struct RuntimeTypeHandle { }
public struct RuntimeFieldHandle { }
public struct TypedReference
{
public static TypedReference MakeTypedReference()
{
return default(TypedReference);
}
}
public struct ArgIterator
{
public TypedReference GetNextArg()
{
TypedReference result = new TypedReference ();
unsafe
{
FCallGetNextArg (&result); // 1
}
return result;
}
private static unsafe void FCallGetNextArg(void* result) { }
}
namespace Collections
{
public interface IEnumerable { }
public interface IEnumerator { }
}
namespace Runtime.InteropServices
{
public class OutAttribute : Attribute { }
}
namespace Reflection
{
public class DefaultMemberAttribute : Attribute { }
}
}";
var c = CreateEmptyCompilation(text, options: TestOptions.UnsafeReleaseDll);
c.VerifyDiagnostics(
// (100,34): warning CS8500: This takes the address of, gets the size of, or declares a pointer to a managed type ('TypedReference')
// FCallGetNextArg (&result); // 1
Diagnostic(ErrorCode.WRN_ManagedAddr, "&result").WithArguments("System.TypedReference").WithLocation(100, 34)
);
}
[WorkItem(544918, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/544918")]
[Fact]
public void CoreLibrary2()
{
var text =
@"class Program
{
public static void Main(string[] args)
{
}
[System.Security.Permissions.HostProtectionAttribute(UI = true)]
public void M()
{
}
}";
CreateCompilationWithMscorlib40(text).VerifyDiagnostics();
}
[WorkItem(546832, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546832")]
[Fact]
public void CoreLibrary3()
{
var text =
@"namespace System
{
public struct Nullable<T> where T : struct
{
public Nullable(T value) { }
public static explicit operator T(T? value) { return default(T); }
public static implicit operator T?(T value) { return default(T?); }
public bool HasValue { get { return false; } }
public T Value { get { return default(T); } }
public T GetValueOrDefault() { return default(T); }
public T GetValueOrDefault(T defaultValue) { return default(T); }
}
public class Object { }
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
}";
CreateEmptyCompilation(
text,
options: TestOptions.ReleaseDll)
.VerifyDiagnostics();
}
/// <summary>
/// Report CS0518 for missing System.Void
/// when generating synthesized .ctor.
/// </summary>
[WorkItem(530859, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530859")]
[Fact()]
public void NoVoidForSynthesizedCtor()
{
var source =
@"namespace System
{
public class Object { }
}";
var compilation = CreateEmptyCompilation(source, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute());
compilation.VerifyEmitDiagnostics(
Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion),
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Void")
);
}
/// <summary>
/// Report CS0656 for missing Decimal to int conversion.
/// </summary>
[WorkItem(530860, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530860")]
[WorkItem(39962, "https://github.com/dotnet/roslyn/issues/39962")]
[ConditionalFact(typeof(NoUsedAssembliesValidation))] // The test hook is blocked by https://github.com/dotnet/roslyn/issues/39962
public void NoDecimalConversion()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Decimal { }
}";
var compilation1 = CreateEmptyCompilation(source1, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"class C
{
static int M(decimal d)
{
return (int)d;
}
}";
var compilation2 = CreateEmptyCompilation(source2, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), references: new[] { reference1 });
// Should report "CS0656: Missing compiler required member 'System.Decimal.op_Explicit_ToInt32'".
// Instead, we report no errors and assert during emit.
// no errors for compat reasons.
compilation2.VerifyDiagnostics();
// The bug has been resolved as Won't Fix for being extremely niche scenario and being a compat concern.
// uncomment the following code if we are fixing this
//var verifier = CompileAndVerify(compilation2);
}
[Fact, WorkItem(3593, "https://github.com/dotnet/roslyn/issues/3593")]
public void NoTypedRef()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Decimal { }
}";
var compilation1 = CreateEmptyCompilation(source1, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"
public class C1
{
public static T Read<T>()
{
T result = default(T);
var refresult = __makeref(result);
// ... method body
return result;
}
}
";
var compilation2 = CreateEmptyCompilation(source2, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), references: new[] { reference1 });
compilation2.VerifyDiagnostics(
// (7,25): error CS0518: Predefined type 'System.TypedReference' is not defined or imported
// var refresult = __makeref(result);
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "__makeref(result)").WithArguments("System.TypedReference").WithLocation(7, 25)
);
}
[Fact, WorkItem(3746, "https://github.com/dotnet/roslyn/issues/3746")]
public void NoTypedRefBox()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Decimal { }
}";
var compilation1 = CreateEmptyCompilation(source1, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"
public class C1
{
public static object rrr;
public static T Read<T>() where T : new()
{
T result = new T();
var refresult = __makeref(result);
rrr = refresult;
return result;
}
}
";
var compilation2 = CreateEmptyCompilation(source2, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), references: new[] { reference1 });
compilation2.VerifyDiagnostics(
// (9,25): error CS0518: Predefined type 'System.TypedReference' is not defined or imported
// var refresult = __makeref(result);
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "__makeref(result)").WithArguments("System.TypedReference").WithLocation(9, 25)
);
}
[Fact, WorkItem(3746, "https://github.com/dotnet/roslyn/issues/3746")]
public void NoTypedRefBox1()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Decimal { }
public struct TypedReference { }
}";
var compilation1 = CreateEmptyCompilation(source1, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"
public class C1
{
public static object rrr;
public static T Read<T>() where T : new()
{
T result = new T();
var refresult = __makeref(result);
rrr = refresult;
rrr = (object)__makeref(result);
return result;
}
}
";
var compilation2 = CreateEmptyCompilation(source2, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), references: new[] { reference1 });
compilation2.VerifyDiagnostics(
// (10,15): error CS0029: Cannot implicitly convert type 'System.TypedReference' to 'object'
// rrr = refresult;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "refresult").WithArguments("System.TypedReference", "object").WithLocation(10, 15),
// (11,15): error CS0030: Cannot convert type 'System.TypedReference' to 'object'
// rrr = (object)__makeref(result);
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object)__makeref(result)").WithArguments("System.TypedReference", "object").WithLocation(11, 15)
);
}
[Fact()]
public void NoTypedRefBox_RefStruct()
{
var source1 =
@"namespace System
{
public class Object { }
public class String { }
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Boolean { }
public struct Decimal { }
public class Attribute { }
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
public class ObsoleteAttribute: Attribute
{
public ObsoleteAttribute(string message, bool error){}
}
// Make the type ref struct. Should work just fine.
public ref struct TypedReference { }
}";
var compilation1 = CreateEmptyCompilation(source1, assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"
public class C1
{
public static object rrr;
public static T Read<T>() where T : new()
{
T result = new T();
var refresult = __makeref(result);
rrr = refresult;
rrr = (object)__makeref(result);
return result;
}
}
";
var compilation2 = CreateEmptyCompilation(source2, new[] { reference1 });
compilation2.VerifyDiagnostics(
// (10,15): error CS0029: Cannot implicitly convert type 'System.TypedReference' to 'object'
// rrr = refresult;
Diagnostic(ErrorCode.ERR_NoImplicitConv, "refresult").WithArguments("System.TypedReference", "object").WithLocation(10, 15),
// (11,15): error CS0030: Cannot convert type 'System.TypedReference' to 'object'
// rrr = (object)__makeref(result);
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(object)__makeref(result)").WithArguments("System.TypedReference", "object").WithLocation(11, 15)
);
}
[WorkItem(530861, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530861")]
[Fact]
public void MissingStringLengthForEach()
{
var source1 =
@"namespace System
{
public class Object { }
public struct Void { }
public class ValueType { }
public struct Boolean { }
public class String : System.Collections.IEnumerable
{
public System.Collections.IEnumerator GetEnumerator() { return null; }
}
public interface IDisposable
{
void Dispose();
}
}
namespace System.Collections
{
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
}
}";
var compilation1 = CreateEmptyCompilation(source1, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), assemblyName: GetUniqueName());
var reference1 = MetadataReference.CreateFromStream(compilation1.EmitToStream());
var source2 =
@"class C
{
static void M(string s)
{
foreach (var c in s)
{
// comment
}
}
}";
var compilation2 = CreateEmptyCompilation(source2, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), references: new[] { reference1 });
compilation2.VerifyDiagnostics();
compilation2.Emit(new System.IO.MemoryStream()).Diagnostics.Verify(
// (5,9): error CS0656: Missing compiler required member 'System.String.get_Length'
// foreach (var c in s)
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"foreach (var c in s)
{
// comment
}").WithArguments("System.String", "get_Length"),
// (5,9): error CS0656: Missing compiler required member 'System.String.get_Chars'
// foreach (var c in s)
Diagnostic(ErrorCode.ERR_MissingPredefinedMember, @"foreach (var c in s)
{
// comment
}").WithArguments("System.String", "get_Chars")
);
}
[WorkItem(631443, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/631443")]
[Fact]
public void CoreLibrary4()
{
var text =
@"namespace System
{
public struct Nullable<T> where T : struct
{
public Nullable(T value) { }
public static explicit operator T(T? value) { return default(T); }
public static implicit operator T?(T value) { return default(T?); }
public bool HasValue { get { return false; } }
public T Value { get { return default(T); } }
public T GetValueOrDefault() { return default(T); }
public T GetValueOrDefault(T defaultValue) { return default(T); }
}
public class Object { }
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } }
public struct Char {
private Char m_value; Char Use(Char b) { m_value = b; return m_value; }
public static implicit operator string(char c) { return default(string); }
}
public class String {
public char CharAt(int i) { return default(char); }
}
internal class @program
{
string M(string s)
{
return s.CharAt(1);
}
}
}";
CreateEmptyCompilation(
text,
options: TestOptions.ReleaseDll)
.VerifyDiagnostics();
}
[Fact]
public void CoreLibraryInt32_m_value()
{
var text =
@"namespace System
{
public class Object
{
public virtual bool Equals(Object obj)
{
return this == obj;
}
public virtual int GetHashCode()
{
return 0;
}
}
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
public struct Int32
{
public Int32 m_value;
public int CompareTo(int value) {
// Need to use compare because subtraction will wrap
// to positive for very large neg numbers, etc.
if (m_value < value) return -1;
if (m_value > value) return 1;
return 0;
}
public override bool Equals(Object obj) {
if (!(obj is Int32)) {
return false;
}
return m_value == ((Int32)obj).m_value;
}
public override int GetHashCode()
{
// return m_value;
return m_value.m_value.m_value;
}
}
internal class @program
{
void Main()
{
int x = 42;
x = x.CompareTo(x);
}
}
}";
var comp = CreateEmptyCompilation(
text,
parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(),
options: TestOptions.ReleaseDll)
.VerifyDiagnostics();
//IMPORTANT: we should NOT load fields of self-containing structs like - "ldfld int int.m_value"
CompileAndVerify(comp, verify: Verification.Skipped).
VerifyIL("int.CompareTo(int)", @"
{
// Code size 16 (0x10)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldind.i4
IL_0002: ldarg.1
IL_0003: bge.s IL_0007
IL_0005: ldc.i4.m1
IL_0006: ret
IL_0007: ldarg.0
IL_0008: ldind.i4
IL_0009: ldarg.1
IL_000a: ble.s IL_000e
IL_000c: ldc.i4.1
IL_000d: ret
IL_000e: ldc.i4.0
IL_000f: ret
}
"
).
VerifyIL("int.Equals(object)", @"
{
// Code size 21 (0x15)
.maxstack 2
IL_0000: ldarg.1
IL_0001: isinst ""int""
IL_0006: brtrue.s IL_000a
IL_0008: ldc.i4.0
IL_0009: ret
IL_000a: ldarg.0
IL_000b: ldind.i4
IL_000c: ldarg.1
IL_000d: unbox.any ""int""
IL_0012: ceq
IL_0014: ret
}
"
).
VerifyIL("int.GetHashCode()", @"
{
// Code size 3 (0x3)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldind.i4
IL_0002: ret
}
"
);
}
[Fact]
public void Enum_GetHashCode()
{
var text =
@"
using System;
namespace System
{
public class Object
{
public virtual bool Equals(Object obj)
{
return this == obj;
}
public virtual int GetHashCode()
{
return 0;
}
}
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
public struct Int32
{
public Int32 m_value;
public override bool Equals(Object obj) {
if (!(obj is Int32)) {
return false;
}
return m_value == ((Int32)obj).m_value;
}
public override int GetHashCode()
{
return 1;
}
}
public abstract class Enum : ValueType
{
public override int GetHashCode()
{
return 42;
}
}
enum E1
{
e
}
}
internal class @program
{
void Main()
{
var i = 123;
var e = (E1)i;
var o = (object)i;
if (i.GetHashCode() == e.GetHashCode())
{
i = i / 0; // crash here
}
if (o.GetHashCode() != e.GetHashCode())
{
i = i / 0; // crash here
}
}
}
";
var comp = CreateEmptyCompilation(
text,
parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(),
options: TestOptions.ReleaseDll)
.VerifyDiagnostics();
//IMPORTANT: we should NOT delegate E1.GetHashCode() to int.GetHashCode()
// it is entirely possible that Enum.GetHashCode and int.GetHashCode
// have different implementations
// PEVerify:
// Error: Token 0x02000009 following ELEMENT_TYPE_CLASS (_VALUETYPE) in signature is a ValueType (Class,respectively).
// Error: Token 0x02000009 following ELEMENT_TYPE_CLASS(_VALUETYPE) in signature is a ValueType (Class, respectively).
// Type load failed.
// ILVerify: Failed to load type 'System.String' from assembly ...
CompileAndVerify(comp, verify: Verification.Fails).
VerifyIL("program.Main()",
@"
{
// Code size 62 (0x3e)
.maxstack 3
.locals init (int V_0, //i
System.E1 V_1) //e
IL_0000: ldc.i4.s 123
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: stloc.1
IL_0005: ldloc.0
IL_0006: box ""int""
IL_000b: ldloca.s V_0
IL_000d: call ""int int.GetHashCode()""
IL_0012: ldloca.s V_1
IL_0014: constrained. ""System.E1""
IL_001a: callvirt ""int object.GetHashCode()""
IL_001f: bne.un.s IL_0025
IL_0021: ldloc.0
IL_0022: ldc.i4.0
IL_0023: div
IL_0024: stloc.0
IL_0025: callvirt ""int object.GetHashCode()""
IL_002a: ldloca.s V_1
IL_002c: constrained. ""System.E1""
IL_0032: callvirt ""int object.GetHashCode()""
IL_0037: beq.s IL_003d
IL_0039: ldloc.0
IL_003a: ldc.i4.0
IL_003b: div
IL_003c: stloc.0
IL_003d: ret
}
"
);
}
[Fact]
public void CoreLibraryIntPtr_m_value()
{
var text =
@"namespace System
{
public class Object
{
public virtual bool Equals(Object obj)
{
return this == obj;
}
public virtual int GetHashCode()
{
return 0;
}
}
public abstract class ValueType { }
public struct Void { }
public struct Boolean { private Boolean m_value; Boolean Use(Boolean b) { m_value = b; return m_value; } }
public struct Int32 { private Int32 m_value; Int32 Use(Int32 b) { m_value = b; return m_value; } }
public struct Int64 { private Int64 m_value; Int64 Use(Int64 b) { m_value = b; return m_value; } }
public class Delegate{}
public struct IntPtr
{
unsafe private void* m_value;
public unsafe IntPtr(int value)
{
m_value = (void *)(long)value;
}
public unsafe override bool Equals(Object obj) {
if (obj is IntPtr) {
return (m_value == ((IntPtr)obj).m_value);
}
return false;
}
public unsafe static bool operator == (IntPtr value1, IntPtr value2)
{
return value1.m_value == value2.m_value;
}
public unsafe static bool operator != (IntPtr value1, IntPtr value2)
{
return value1.m_value != value2.m_value;
}
public unsafe override int GetHashCode() {
return unchecked((int)((long)m_value));
}
public unsafe static IntPtr Goo()
{
return new IntPtr(0);
}
public unsafe static bool Bar(IntPtr value1)
{
return value1.m_value == Goo().m_value;
}
}
internal class @program
{
void Main()
{
var x = new IntPtr(42);
}
}
}";
var comp = CreateEmptyCompilation(text, parseOptions: TestOptions.Regular.WithNoRefSafetyRulesAttribute(), options: TestOptions.UnsafeReleaseDll).VerifyDiagnostics();
//IMPORTANT: we should NOT load fields of clr-confusing structs off the field value.
// the field should be loaded off the reference like in
// the following snippet (note ldargA, not ldarg) -
// IL_0000: ldarga.s V_0
// IL_0002: ldfld ""void* System.IntPtr.m_value""
//
// it may seem redundant since in general we can load the filed off the value
// but see the bug see VSW #396011, JIT needs references when loading
// fields of certain clr-ambiguous structs (only possible when building mscorlib)
// PEVerify: Type load failed.
// ILVerify: Failed to load type 'System.String' from assembly ...
CompileAndVerify(comp, verify: Verification.Fails).
VerifyIL("System.IntPtr..ctor(int)", @"
{
// Code size 10 (0xa)
.maxstack 2
IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: conv.i8
IL_0003: conv.u
IL_0004: stfld ""void* System.IntPtr.m_value""
IL_0009: ret
}
"
).VerifyIL("System.IntPtr.Equals(object)", @"
{
// Code size 30 (0x1e)
.maxstack 2
IL_0000: ldarg.1
IL_0001: isinst ""System.IntPtr""
IL_0006: brfalse.s IL_001c
IL_0008: ldarg.0
IL_0009: ldfld ""void* System.IntPtr.m_value""
IL_000e: ldarg.1
IL_000f: unbox ""System.IntPtr""
IL_0014: ldfld ""void* System.IntPtr.m_value""
IL_0019: ceq
IL_001b: ret
IL_001c: ldc.i4.0
IL_001d: ret
}
"
).VerifyIL("System.IntPtr.GetHashCode()", @"
{
// Code size 9 (0x9)
.maxstack 1
IL_0000: ldarg.0
IL_0001: ldfld ""void* System.IntPtr.m_value""
IL_0006: conv.u8
IL_0007: conv.i4
IL_0008: ret
}
"
).VerifyIL("bool System.IntPtr.op_Equality(System.IntPtr, System.IntPtr)",
@"
{
// Code size 17 (0x11)
.maxstack 2
IL_0000: ldarga.s V_0
IL_0002: ldfld ""void* System.IntPtr.m_value""
IL_0007: ldarga.s V_1
IL_0009: ldfld ""void* System.IntPtr.m_value""
IL_000e: ceq
IL_0010: ret
}
").VerifyIL("bool System.IntPtr.op_Inequality(System.IntPtr, System.IntPtr)",
@"
{
// Code size 20 (0x14)
.maxstack 2
IL_0000: ldarga.s V_0
IL_0002: ldfld ""void* System.IntPtr.m_value""
IL_0007: ldarga.s V_1
IL_0009: ldfld ""void* System.IntPtr.m_value""
IL_000e: ceq
IL_0010: ldc.i4.0
IL_0011: ceq
IL_0013: ret
}
").VerifyIL("System.IntPtr.Bar(System.IntPtr)",
@"
{
// Code size 23 (0x17)
.maxstack 2
.locals init (System.IntPtr V_0)
IL_0000: ldarga.s V_0
IL_0002: ldfld ""void* System.IntPtr.m_value""
IL_0007: call ""System.IntPtr System.IntPtr.Goo()""
IL_000c: stloc.0
IL_000d: ldloca.s V_0
IL_000f: ldfld ""void* System.IntPtr.m_value""
IL_0014: ceq
IL_0016: ret
}
");
}
[Fact]
public void FixedHelpersOnStringAndArray()
{
var text =
@"
namespace System
{
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Char { }
public struct Boolean { }
public class Exception { }
public class Attribute { }
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
public class String
{
public ref char GetPinnableReference() => throw null;
}
public class Object
{
}
public class Array
{
public ref char GetPinnableReference() => throw null;
}
}
unsafe internal class @program
{
public static void Main()
{
fixed (char* p = ""A"")
{
*p = default;
}
}
public static void Main1()
{
fixed (char* p = new char[1])
{
*p = default;
}
}
}
";
var comp = CreateEmptyCompilation(text, options: TestOptions.UnsafeReleaseDll);
comp.VerifyDiagnostics();
var c = CompileAndVerify(comp, verify: Verification.Skipped);
c.VerifyIL("program.Main()", @"
{
// Code size 19 (0x13)
.maxstack 2
.locals init (pinned char& V_0)
IL_0000: ldstr ""A""
IL_0005: call ""ref char string.GetPinnableReference()""
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: conv.u
IL_000d: ldc.i4.0
IL_000e: stind.i2
IL_000f: ldc.i4.0
IL_0010: conv.u
IL_0011: stloc.0
IL_0012: ret
}
"
);
c.VerifyIL("program.Main1()", @"
{
// Code size 35 (0x23)
.maxstack 2
.locals init (char* V_0, //p
pinned char[] V_1)
IL_0000: ldc.i4.1
IL_0001: newarr ""char""
IL_0006: dup
IL_0007: stloc.1
IL_0008: brfalse.s IL_000f
IL_000a: ldloc.1
IL_000b: ldlen
IL_000c: conv.i4
IL_000d: brtrue.s IL_0014
IL_000f: ldc.i4.0
IL_0010: conv.u
IL_0011: stloc.0
IL_0012: br.s IL_001d
IL_0014: ldloc.1
IL_0015: ldc.i4.0
IL_0016: ldelema ""char""
IL_001b: conv.u
IL_001c: stloc.0
IL_001d: ldloc.0
IL_001e: ldc.i4.0
IL_001f: stind.i2
IL_0020: ldnull
IL_0021: stloc.1
IL_0022: ret
}
");
}
[Fact]
public void FixedHelperOnObject()
{
var text =
@"
namespace System
{
public struct Void { }
public class ValueType { }
public struct Int32 { }
public struct Char { }
public struct Boolean { }
public class Exception { }
public class Attribute { }
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
public class String
{
}
public class Object
{
public ref char GetPinnableReference() => throw null;
}
public class Array
{
}
}
namespace System.Runtime.CompilerServices
{
public class RuntimeHelpers
{
public static int OffsetToStringData => 0;
}
}
unsafe internal class @program
{
public static void Main()
{
fixed (char* p = ""A"")
{
*p = default;
}
fixed (char* p = new char[1])
{
*p = default;
}
fixed (char* p = 42)
{
*p = default;
}
}
}
";
var comp = CreateEmptyCompilation(text, options: TestOptions.UnsafeReleaseDll);
comp.VerifyDiagnostics();
CompileAndVerify(comp, verify: Verification.Skipped).
VerifyIL("program.Main()", @"
{
// Code size 83 (0x53)
.maxstack 2
.locals init (char* V_0, //p
pinned string V_1,
char* V_2, //p
pinned char[] V_3,
pinned char& V_4)
IL_0000: ldstr ""A""
IL_0005: stloc.1
IL_0006: ldloc.1
IL_0007: conv.u
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: brfalse.s IL_0014
IL_000c: ldloc.0
IL_000d: call ""int System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringData.get""
IL_0012: add
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldc.i4.0
IL_0016: stind.i2
IL_0017: ldnull
IL_0018: stloc.1
IL_0019: ldc.i4.1
IL_001a: newarr ""char""
IL_001f: dup
IL_0020: stloc.3
IL_0021: brfalse.s IL_0028
IL_0023: ldloc.3
IL_0024: ldlen
IL_0025: conv.i4
IL_0026: brtrue.s IL_002d
IL_0028: ldc.i4.0
IL_0029: conv.u
IL_002a: stloc.2
IL_002b: br.s IL_0036
IL_002d: ldloc.3
IL_002e: ldc.i4.0
IL_002f: ldelema ""char""
IL_0034: conv.u
IL_0035: stloc.2
IL_0036: ldloc.2
IL_0037: ldc.i4.0
IL_0038: stind.i2
IL_0039: ldnull
IL_003a: stloc.3
IL_003b: ldc.i4.s 42
IL_003d: box ""int""
IL_0042: call ""ref char object.GetPinnableReference()""
IL_0047: stloc.s V_4
IL_0049: ldloc.s V_4
IL_004b: conv.u
IL_004c: ldc.i4.0
IL_004d: stind.i2
IL_004e: ldc.i4.0
IL_004f: conv.u
IL_0050: stloc.s V_4
IL_0052: ret
}
"
);
}
}
}
|