// 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. using System; using System.Collections.Immutable; using System.Linq; using System.Reflection.Metadata; using Microsoft.CodeAnalysis.CodeGen; using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.ExpressionEvaluator; using Microsoft.CodeAnalysis.ExpressionEvaluator.UnitTests; using Microsoft.VisualStudio.Debugger.Evaluation; using Roslyn.Test.PdbUtilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.ExpressionEvaluator.UnitTests { public class WinMdTests : ExpressionCompilerTestBase { /// /// Handle runtime assemblies rather than Windows.winmd /// (compile-time assembly) since those are the assemblies /// loaded in the debuggee. /// [WorkItem(981104, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/981104")] [ConditionalFact(typeof(OSVersionWin8))] public void Win8RuntimeAssemblies() { var source = @"class C { static void M(Windows.Storage.StorageFolder f, Windows.Foundation.Collections.PropertySet p) { } }"; var compilation0 = CreateEmptyCompilation( source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName(), references: WinRtRefs); var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage", "Windows.Foundation.Collections"); Assert.True(runtimeAssemblies.Length >= 2); // no reference to Windows.winmd WithRuntimeInstance(compilation0, new[] { MscorlibRef }.Concat(runtimeAssemblies), runtime => { var context = CreateMethodContext(runtime, "C.M"); string error; var testData = new CompilationTestData(); context.CompileExpression("(p == null) ? f : null", out error, testData); Assert.Null(error); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: brfalse.s IL_0005 IL_0003: ldnull IL_0004: ret IL_0005: ldarg.0 IL_0006: ret }"); }); } [ConditionalFact(typeof(OSVersionWin8))] public void Win8RuntimeAssemblies_ExternAlias() { var source = @"extern alias X; class C { static void M(X::Windows.Storage.StorageFolder f) { } }"; var compilation0 = CreateEmptyCompilation( source, options: TestOptions.DebugDll, assemblyName: ExpressionCompilerUtilities.GenerateUniqueName(), references: WinRtRefs.Select(r => r.Display == "Windows" ? r.WithAliases(new[] { "X" }) : r)); var runtimeAssemblies = ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage"); Assert.True(runtimeAssemblies.Length >= 1); // no reference to Windows.winmd WithRuntimeInstance(compilation0, new[] { MscorlibRef }.Concat(runtimeAssemblies), runtime => { var context = CreateMethodContext(runtime, "C.M"); string error; var testData = new CompilationTestData(); context.CompileExpression("X::Windows.Storage.FileProperties.PhotoOrientation.Unspecified", out error, testData); Assert.Null(error); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: ret }"); }); } [Fact] public void Win8OnWin8() { CompileTimeAndRuntimeAssemblies( ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.Windows)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.WindowsData)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.WindowsStorage)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), "Windows.Storage"); } [Fact] public void Win8OnWin10() { CompileTimeAndRuntimeAssemblies( ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.Windows)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.WindowsData)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.WindowsStorage)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_3(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), "Windows.Storage"); } [WorkItem(1108135, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1108135")] [Fact] public void Win10OnWin10() { CompileTimeAndRuntimeAssemblies( ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.WindowsData)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.WindowsStorage)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), ImmutableArray.Create( MscorlibRef, AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.Windows)).GetReference(), AssemblyMetadata.CreateFromImage(ToVersion1_4(TestResources.ExpressionCompiler.LibraryA)).GetReference(), AssemblyMetadata.CreateFromImage(TestResources.ExpressionCompiler.LibraryB).GetReference()), "Windows"); } private void CompileTimeAndRuntimeAssemblies( ImmutableArray compileReferences, ImmutableArray runtimeReferences, string storageAssemblyName) { var source = @"class C { static void M(LibraryA.A a, LibraryB.B b, Windows.Data.Text.TextSegment t, Windows.Storage.StorageFolder f) { } }"; var compilation0 = CreateEmptyCompilation(source, compileReferences, TestOptions.DebugDll); WithRuntimeInstance(compilation0, runtimeReferences, runtime => { var context = CreateMethodContext(runtime, "C.M"); string error; var testData = new CompilationTestData(); context.CompileExpression("(object)a ?? (object)b ?? (object)t ?? f", out error, testData); Assert.Null(error); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 17 (0x11) .maxstack 2 IL_0000: ldarg.0 IL_0001: dup IL_0002: brtrue.s IL_0010 IL_0004: pop IL_0005: ldarg.1 IL_0006: dup IL_0007: brtrue.s IL_0010 IL_0009: pop IL_000a: ldarg.2 IL_000b: dup IL_000c: brtrue.s IL_0010 IL_000e: pop IL_000f: ldarg.3 IL_0010: ret }"); testData = new CompilationTestData(); var result = context.CompileExpression("default(Windows.Storage.StorageFolder)", out error, testData); Assert.Null(error); var methodData = testData.GetMethodData("<>x.<>m0"); methodData.VerifyIL( @"{ // Code size 2 (0x2) .maxstack 1 IL_0000: ldnull IL_0001: ret }"); // Check return type is from runtime assembly. var assemblyReference = AssemblyMetadata.CreateFromImage(result.Assembly).GetReference(); var compilation = CSharpCompilation.Create( assemblyName: ExpressionCompilerUtilities.GenerateUniqueName(), references: runtimeReferences.Concat(ImmutableArray.Create(assemblyReference))); var assembly = ImmutableArray.CreateRange(result.Assembly); using (var metadata = ModuleMetadata.CreateFromImage(ImmutableArray.CreateRange(assembly))) { var reader = metadata.MetadataReader; var typeDef = reader.GetTypeDef("<>x"); var methodHandle = reader.GetMethodDefHandle(typeDef, "<>m0"); var module = (PEModuleSymbol)compilation.GetMember("<>x").ContainingModule; var metadataDecoder = new MetadataDecoder(module); SignatureHeader signatureHeader; BadImageFormatException metadataException; var parameters = metadataDecoder.GetSignatureForMethod(methodHandle, out signatureHeader, out metadataException); Assert.Equal(5, parameters.Length); var actualReturnType = parameters[0].Type; Assert.Equal(TypeKind.Class, actualReturnType.TypeKind); // not error var expectedReturnType = compilation.GetMember("Windows.Storage.StorageFolder"); Assert.Equal(expectedReturnType, actualReturnType); Assert.Equal(storageAssemblyName, actualReturnType.ContainingAssembly.Name); } }); } /// /// Assembly-qualified name containing "ContentType=WindowsRuntime", /// and referencing runtime assembly. /// [WorkItem(1116143, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1116143")] [ConditionalFact(typeof(OSVersionWin8), typeof(IsRelease))] // https://github.com/dotnet/roslyn/issues/25702 public void AssemblyQualifiedName() { var source = @"class C { static void M(Windows.Storage.StorageFolder f, Windows.Foundation.Collections.PropertySet p) { } }"; var compilation = CreateEmptyCompilation(source, WinRtRefs, TestOptions.DebugDll); WithRuntimeInstance(compilation, new[] { MscorlibRef }.Concat(ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Storage", "Windows.Foundation.Collections")), runtime => { var context = CreateMethodContext(runtime, "C.M"); var aliases = ImmutableArray.Create( VariableAlias("s", "Windows.Storage.StorageFolder, Windows.Storage, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime"), VariableAlias("d", "Windows.Foundation.DateTime, Windows.Foundation, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime")); string error; var testData = new CompilationTestData(); context.CompileExpression( "(object)s.Attributes ?? d.UniversalTime", DkmEvaluationFlags.TreatAsExpression, aliases, out error, testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 55 (0x37) .maxstack 2 IL_0000: ldstr ""s"" IL_0005: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetObjectByAlias(string)"" IL_000a: castclass ""Windows.Storage.StorageFolder"" IL_000f: callvirt ""Windows.Storage.FileAttributes Windows.Storage.StorageFolder.Attributes.get"" IL_0014: box ""Windows.Storage.FileAttributes"" IL_0019: dup IL_001a: brtrue.s IL_0036 IL_001c: pop IL_001d: ldstr ""d"" IL_0022: call ""object Microsoft.VisualStudio.Debugger.Clr.IntrinsicMethods.GetObjectByAlias(string)"" IL_0027: unbox.any ""Windows.Foundation.DateTime"" IL_002c: ldfld ""long Windows.Foundation.DateTime.UniversalTime"" IL_0031: box ""long"" IL_0036: ret }"); }); } [WorkItem(1117084, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1117084")] [Fact] public void OtherFrameworkAssembly() { var source = @"class C { static void M(Windows.UI.Xaml.FrameworkElement f) { } }"; var compilation = CreateEmptyCompilation(source, WinRtRefs, TestOptions.DebugDll); WithRuntimeInstance(compilation, new[] { MscorlibRef }.Concat(ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.Foundation", "Windows.UI", "Windows.UI.Xaml")), runtime => { var context = CreateMethodContext(runtime, "C.M"); string error; ResultProperties resultProperties; ImmutableArray missingAssemblyIdentities; var testData = new CompilationTestData(); var result = context.CompileExpression( "f.RenderSize", DkmEvaluationFlags.TreatAsExpression, NoAliases, DebuggerDiagnosticFormatter.Instance, out resultProperties, out error, out missingAssemblyIdentities, EnsureEnglishUICulture.PreferredOrNull, testData); var expectedAssemblyIdentity = WinRtRefs.Single(r => r.Display == "System.Runtime.WindowsRuntime.dll").GetAssemblyIdentity(); Assert.Equal(expectedAssemblyIdentity, missingAssemblyIdentities.Single()); }); } [WorkItem(1154988, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1154988")] [ConditionalFact(typeof(OSVersionWin8))] public void WinMdAssemblyReferenceRequiresRedirect() { var source = @"class C : Windows.UI.Xaml.Controls.UserControl { static void M(C c) { } }"; var compilation = CreateEmptyCompilation(source, WinRtRefs, TestOptions.DebugDll); WithRuntimeInstance(compilation, new[] { MscorlibRef }.Concat(ExpressionCompilerTestHelpers.GetRuntimeWinMds("Windows.UI", "Windows.UI.Xaml")), runtime => { string errorMessage; var testData = new CompilationTestData(); ExpressionCompilerTestHelpers.CompileExpressionWithRetry( runtime.Modules.SelectAsArray(m => m.MetadataBlock), "c.Dispatcher", ImmutableArray.Empty, (metadataBlocks, _) => { return CreateMethodContext(runtime, "C.M"); }, (AssemblyIdentity assembly, out uint size) => { // Compilation should succeed without retry if we redirect assembly refs correctly. // Throwing so that we don't loop forever (as we did before fix)... throw ExceptionUtilities.Unreachable; }, out errorMessage, out testData); Assert.Null(errorMessage); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt ""Windows.UI.Core.CoreDispatcher Windows.UI.Xaml.DependencyObject.Dispatcher.get"" IL_0006: ret }"); }); } private static byte[] ToVersion1_3(byte[] bytes) { return ExpressionCompilerTestHelpers.ToVersion1_3(bytes); } private static byte[] ToVersion1_4(byte[] bytes) { return ExpressionCompilerTestHelpers.ToVersion1_4(bytes); } } }