|
// 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 System;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.ExpressionEvaluator;
using Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.VisualStudio.Debugger.Clr;
using Microsoft.VisualStudio.Debugger.Evaluation;
using Microsoft.VisualStudio.Debugger.Evaluation.ClrCompilation;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests
{
public class TupleTests : ExpressionCompilerTestBase
{
[Fact]
public void Literal()
{
var source =
@"class C
{
static void M()
{
(int, int) o;
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
var result = context.CompileExpression("(A: 1, B: 2)", out error, testData);
Assert.Null(error);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { "A", "B" }, tupleElementNames);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = (MethodSymbol)methodData.Method;
Assert.True(method.ReturnType.IsTupleType);
CheckAttribute(result.Assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: true);
methodData.VerifyIL(
@"{
// Code size 8 (0x8)
.maxstack 2
.locals init (System.ValueTuple<int, int> V_0) //o
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj ""System.ValueTuple<int, int>..ctor(int, int)""
IL_0007: ret
}");
});
}
[Fact]
public void DuplicateValueTupleBetweenMscorlibAndLibrary()
{
var versionTemplate = @"[assembly: System.Reflection.AssemblyVersion(""{0}.0.0.0"")]";
var corlib_cs = @"
namespace System
{
public class Object { }
public struct Int32 { }
public struct Boolean { }
public class String { }
public class ValueType { }
public struct Void { }
public class Attribute { }
}
namespace System.Reflection
{
public class AssemblyVersionAttribute : Attribute
{
public AssemblyVersionAttribute(String version) { }
}
}";
string valuetuple_cs = @"
namespace System
{
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 item1, T2 item2) => (Item1, Item2) = (item1, item2);
}
}";
var parseOptions = TestOptions.Regular.WithNoRefSafetyRulesAttribute();
var corlibWithoutVT = CreateEmptyCompilation(new[] { Parse(String.Format(versionTemplate, "1") + corlib_cs, options: parseOptions) }, assemblyName: "corlib");
corlibWithoutVT.VerifyDiagnostics();
var corlibWithoutVTRef = corlibWithoutVT.EmitToImageReference();
var corlibWithVT = CreateEmptyCompilation(new[] { Parse(String.Format(versionTemplate, "2") + corlib_cs + valuetuple_cs, options: parseOptions) }, assemblyName: "corlib");
corlibWithVT.VerifyDiagnostics();
var source =
@"class C
{
static (int, int) M()
{
(int, int) t = (1, 2);
return t;
}
}
";
var app = CreateEmptyCompilation(source + valuetuple_cs, references: new[] { corlibWithoutVTRef }, parseOptions: parseOptions, options: TestOptions.DebugDll);
app.VerifyDiagnostics();
// Create EE context with app assembly (including ValueTuple) and a more recent corlib (also including ValueTuple)
var runtime = CreateRuntimeInstance(new[] { app.ToModuleInstance(), corlibWithVT.ToModuleInstance() });
var evalContext = CreateMethodContext(runtime, "C.M");
string error;
var testData = new CompilationTestData();
var compileResult = evalContext.CompileExpression("(1, 2)", out error, testData);
Assert.Null(error);
using (ModuleMetadata block = ModuleMetadata.CreateFromStream(new MemoryStream(compileResult.Assembly)))
{
var reader = block.MetadataReader;
var appRef = app.Assembly.Identity.Name;
AssertEx.SetEqual(new[] { "corlib 2.0", appRef + " 0.0" }, reader.DumpAssemblyReferences());
AssertEx.SetEqual(new[] {
"Object, System, AssemblyReference:corlib",
"ValueTuple`2, System, AssemblyReference:" + appRef // ValueTuple comes from app, not corlib
},
reader.DumpTypeReferences());
}
}
[Fact]
public void TupleElementNamesAttribute_NotAvailable()
{
var source =
@"namespace System
{
public struct ValueTuple<T1, T2>
{
public T1 Item1;
public T2 Item2;
public ValueTuple(T1 _1, T2 _2)
{
Item1 = _1;
Item2 = _2;
}
}
}
class C
{
static void M()
{
(int, int) o;
}
}";
var comp = CreateCompilationWithMscorlib40(source, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
var result = context.CompileExpression("(A: 1, B: 2)", out error, testData);
Assert.Null(error);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
Assert.Null(customTypeInfo);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = (MethodSymbol)methodData.Method;
Assert.True(method.ReturnType.IsTupleType);
CheckAttribute(result.Assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: false);
methodData.VerifyIL(
@" {
// Code size 8 (0x8)
.maxstack 2
.locals init (System.ValueTuple<int, int> V_0) //o
IL_0000: ldc.i4.1
IL_0001: ldc.i4.2
IL_0002: newobj ""System.ValueTuple<int, int>..ctor(int, int)""
IL_0007: ret
}");
});
}
[Fact]
public void Local()
{
var source =
@"class C
{
static void M()
{
(int A\u1234, int \u1234B) o = (1, 2);
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = locals[0].GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { "A\u1234", "\u1234B" }, tupleElementNames);
var method = (MethodSymbol)testData.GetExplicitlyDeclaredMethods().Single().Value.Method;
CheckAttribute(assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: true);
Assert.True(method.ReturnType.IsTupleType);
VerifyLocal(testData, typeName, locals[0], "<>m0", "o", expectedILOpt:
string.Format(@"{{
// Code size 2 (0x2)
.maxstack 1
.locals init (System.ValueTuple<int, int> V_0) //o
IL_0000: ldloc.0
IL_0001: ret
}}", '\u1234'));
locals.Free();
});
}
[Fact]
public void Constant()
{
var source =
@"class A<T>
{
internal class B<U>
{
}
}
class C
{
static (object, object) F;
static void M()
{
const A<(int, int A)>.B<(object B, object)>[] c = null;
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var assembly = context.CompileGetLocals(locals, argumentsOnly: false, typeName: out typeName, testData: testData);
Assert.Equal(1, locals.Count);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = locals[0].GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(new[] { null, "A", "B", null }, tupleElementNames);
var method = (MethodSymbol)testData.GetExplicitlyDeclaredMethods().Single().Value.Method;
CheckAttribute(assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: true);
var returnType = method.ReturnType;
Assert.False(returnType.IsTupleType);
Assert.True(returnType.ContainsTuple());
VerifyLocal(testData, typeName, locals[0], "<>m0", "c", expectedFlags: DkmClrCompilationResultFlags.ReadOnlyResult, expectedILOpt:
@"{
// Code size 2 (0x2)
.maxstack 1
IL_0000: ldnull
IL_0001: ret
}");
locals.Free();
});
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/13803")]
public void LongTupleLocalElement_NoNames()
{
var source =
@"class C
{
static void M()
{
var x = (1, 2, 3, 4, 5, 6, 7, 8);
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { SystemRuntimeFacadeRef, ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { MscorlibRef, SystemCoreRef, SystemRuntimeFacadeRef, ValueTupleRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
context.CompileExpression("x.Item4 + x.Item8", out error, testData);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 19 (0x13)
.maxstack 2
.locals init (System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>> V_0) //x
IL_0000: ldloc.0
IL_0001: ldfld ""int System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>>.Item4""
IL_0006: ldloc.0
IL_0007: ldfld ""System.ValueTuple<int> System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>>.Rest""
IL_000c: ldfld ""int System.ValueTuple<int>.Item1""
IL_0011: add
IL_0012: ret
}");
});
}
[Fact]
public void LongTupleLocalElement_Names()
{
var source =
@"class C
{
static void M()
{
var x = (1, 2, Three: 3, Four: 4, 5, 6, 7, Eight: 8);
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { SystemRuntimeFacadeRef, ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { MscorlibRef, SystemCoreRef, SystemRuntimeFacadeRef, ValueTupleRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
context.CompileExpression("x.Item8 + x.Eight", out error, testData);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 24 (0x18)
.maxstack 2
.locals init (System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>> V_0) //x
IL_0000: ldloc.0
IL_0001: ldfld ""System.ValueTuple<int> System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>>.Rest""
IL_0006: ldfld ""int System.ValueTuple<int>.Item1""
IL_000b: ldloc.0
IL_000c: ldfld ""System.ValueTuple<int> System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int>>.Rest""
IL_0011: ldfld ""int System.ValueTuple<int>.Item1""
IL_0016: add
IL_0017: ret
}");
});
}
[ConditionalFact(typeof(IsRelease), Reason = "https://github.com/dotnet/roslyn/issues/25702")]
public void DeclareLocal()
{
var source =
@"class C
{
static void M()
{
var x = (1, 2);
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var testData = new CompilationTestData();
string error;
ResultProperties resultProperties;
ImmutableArray<AssemblyIdentity> missingAssemblyIdentities;
var result = context.CompileExpression(
"(int A, int B) y = x;",
DkmEvaluationFlags.None,
NoAliases,
DebuggerDiagnosticFormatter.Instance,
out resultProperties,
out error,
out missingAssemblyIdentities,
EnsureEnglishUICulture.PreferredOrNull,
testData);
Assert.Null(error);
Assert.Equal(DkmClrCompilationResultFlags.PotentialSideEffect | DkmClrCompilationResultFlags.ReadOnlyResult, resultProperties.Flags);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = result.GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Null(tupleElementNames);
var methodData = testData.GetMethodData("<>x.<>m0");
var method = (MethodSymbol)methodData.Method;
CheckAttribute(result.Assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: false);
methodData.VerifyIL(
@"{
// Code size 64 (0x40)
.maxstack 6
.locals init (System.ValueTuple<int, int> V_0) //x
IL_0000: ldtoken ""System.ValueTuple<int, int>""
IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)""
IL_000a: ldstr ""y""
IL_000f: ldstr ""108766ce-df68-46ee-b761-0dcb7ac805f1""
IL_0014: newobj ""System.Guid..ctor(string)""
IL_0019: ldc.i4.5
IL_001a: newarr ""byte""
IL_001f: dup
IL_0020: ldtoken ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=5 <PrivateImplementationDetails>.845151BC3876B3B783409FD71AF3665D783D8036161B4A2D2ACD27E1A0FCEDF7""
IL_0025: call ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
IL_002a: call ""void Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.CreateVariable(System.Type, string, System.Guid, byte[])""
IL_002f: ldstr ""y""
IL_0034: call ""System.ValueTuple<int, int> Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetVariableAddress<System.ValueTuple<int, int>>(string)""
IL_0039: ldloc.0
IL_003a: stobj ""System.ValueTuple<int, int>""
IL_003f: ret
}");
});
}
[ConditionalFact(typeof(IsRelease), Reason = "https://github.com/dotnet/roslyn/issues/25702")]
[WorkItem("https://github.com/dotnet/roslyn/issues/13589")]
public void AliasElement()
{
var source =
@"class C
{
static (int, int) F;
static void M()
{
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { ValueTupleRef, SystemRuntimeFacadeRef, MscorlibRef }, runtime =>
{
var context = CreateMethodContext(
runtime,
"C.M");
// (int A, (int, int D) B)[] t;
var aliasElementNames = new ReadOnlyCollection<string>(new[] { "A", "B", null, "D" });
var alias = new Alias(
DkmClrAliasKind.Variable,
"t",
"t",
"System.ValueTuple`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.ValueTuple`2[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51]][], System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51",
CustomTypeInfo.PayloadTypeId,
CustomTypeInfo.Encode(null, aliasElementNames));
var locals = ArrayBuilder<LocalAndMethod>.GetInstance();
string typeName;
var diagnostics = DiagnosticBag.GetInstance();
var testData = new CompilationTestData();
var assembly = context.CompileGetLocals(
locals,
argumentsOnly: false,
aliases: ImmutableArray.Create(alias),
diagnostics: diagnostics,
typeName: out typeName,
testData: testData);
diagnostics.Verify();
diagnostics.Free();
Assert.Equal(1, locals.Count);
ReadOnlyCollection<byte> customTypeInfo;
var customTypeInfoId = locals[0].GetCustomTypeInfo(out customTypeInfo);
ReadOnlyCollection<byte> dynamicFlags;
ReadOnlyCollection<string> tupleElementNames;
CustomTypeInfo.Decode(customTypeInfoId, customTypeInfo, out dynamicFlags, out tupleElementNames);
Assert.Equal(aliasElementNames, tupleElementNames);
var method = (MethodSymbol)testData.GetExplicitlyDeclaredMethods().Single().Value.Method;
CheckAttribute(assembly, method, AttributeDescription.TupleElementNamesAttribute, expected: true);
var returnType = (TypeSymbol)method.ReturnType;
Assert.False(returnType.IsTupleType);
Assert.True(((ArrayTypeSymbol)returnType).ElementType.IsTupleType);
VerifyLocal(testData, typeName, locals[0], "<>m0", "t", expectedILOpt:
@"{
// Code size 16 (0x10)
.maxstack 1
IL_0000: ldstr ""t""
IL_0005: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetObjectByAlias(string)""
IL_000a: castclass ""System.ValueTuple<int, System.ValueTuple<int, int>>[]""
IL_000f: ret
}");
locals.Free();
});
}
[ConditionalFact(typeof(IsRelease), Reason = "https://github.com/dotnet/roslyn/issues/25702")]
[WorkItem("https://github.com/dotnet/roslyn/issues/13803")]
public void AliasElement_NoNames()
{
var source =
@"class C
{
static (int, int) F;
static void M()
{
}
}";
var comp = CreateCompilationWithMscorlib40(source, new[] { SystemRuntimeFacadeRef, ValueTupleRef }, options: TestOptions.DebugDll);
WithRuntimeInstance(comp, new[] { MscorlibRef, SystemCoreRef, SystemRuntimeFacadeRef, ValueTupleRef }, runtime =>
{
var context = CreateMethodContext(runtime, "C.M");
var alias = new Alias(
DkmClrAliasKind.Variable,
"x",
"x",
"System.ValueTuple`8[" +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.ValueTuple`2[" +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]," +
"[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], " +
"System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51]], " +
"System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51",
Guid.Empty,
null);
ResultProperties resultProperties;
string error;
ImmutableArray<AssemblyIdentity> missingAssemblyIdentities;
var testData = new CompilationTestData();
context.CompileExpression(
"x.Item4 + x.Item8",
DkmEvaluationFlags.TreatAsExpression,
ImmutableArray.Create(alias),
DebuggerDiagnosticFormatter.Instance,
out resultProperties,
out error,
out missingAssemblyIdentities,
null,
testData);
testData.GetMethodData("<>x.<>m0").VerifyIL(
@"{
// Code size 47 (0x2f)
.maxstack 2
IL_0000: ldstr ""x""
IL_0005: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetObjectByAlias(string)""
IL_000a: unbox.any ""System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int, int>>""
IL_000f: ldfld ""int System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int, int>>.Item4""
IL_0014: ldstr ""x""
IL_0019: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetObjectByAlias(string)""
IL_001e: unbox.any ""System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int, int>>""
IL_0023: ldfld ""System.ValueTuple<int, int> System.ValueTuple<int, int, int, int, int, int, int, System.ValueTuple<int, int>>.Rest""
IL_0028: ldfld ""int System.ValueTuple<int, int>.Item1""
IL_002d: add
IL_002e: ret
}");
});
}
}
}
|