diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 92a0774c3d8fa..1ee083781590c 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -6766,6 +6766,15 @@ internal static string ERR_NoNamespacePrivate { } } + /// + /// Looks up a localized string similar to Cannot compile net modules when using /refout or /refonly.. + /// + internal static string ERR_NoNetModuleOutputWhenRefOutOrRefOnly { + get { + return ResourceManager.GetString("ERR_NoNetModuleOutputWhenRefOutOrRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Cannot create an instance of the abstract class or interface '{0}'. /// @@ -6829,6 +6838,15 @@ internal static string ERR_NoPIANestedType { } } + /// + /// Looks up a localized string similar to Do not use refout when using refonly.. + /// + internal static string ERR_NoRefOutWhenRefOnly { + get { + return ResourceManager.GetString("ERR_NoRefOutWhenRefOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}': cannot override because '{1}' does not have an overridable set accessor. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 4a782f374669a..6f947e4563a11 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2288,6 +2288,12 @@ If such a class is used as a base class and if the deriving class defines a dest Merge conflict marker encountered + + Do not use refout when using refonly. + + + Cannot compile net modules when using /refout or /refonly. + Overloadable operator expected diff --git a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs index f83230991b3f1..eea38cd266404 100644 --- a/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs +++ b/src/Compilers/CSharp/Portable/CommandLine/CSharpCommandLineParser.cs @@ -67,6 +67,8 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar string outputDirectory = baseDirectory; ImmutableArray> pathMap = ImmutableArray>.Empty; string outputFileName = null; + string outputRefFilePath = null; + bool metadataOnly = false; string documentationPath = null; string errorLogPath = null; bool parseDocumentationComments = false; //Don't just null check documentationFileName because we want to do this even if the file name is invalid. @@ -379,6 +381,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar } continue; + case "out": if (string.IsNullOrWhiteSpace(value)) { @@ -391,6 +394,26 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar continue; + case "refout": + value = RemoveQuotesAndSlashes(value); + if (string.IsNullOrEmpty(value)) + { + AddDiagnostic(diagnostics, ErrorCode.ERR_NoFileSpec, arg); + } + else + { + outputRefFilePath = ParseGenericPathToFile(value, diagnostics, baseDirectory); + } + + continue; + + case "refonly": + if (value != null) + break; + + metadataOnly = true; + continue; + case "t": case "target": if (value == null) @@ -1141,6 +1164,16 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar diagnosticOptions[o.Key] = o.Value; } + if (metadataOnly && outputRefFilePath != null) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoRefOutWhenRefOnly); + } + + if (outputKind == OutputKind.NetModule && (metadataOnly || outputRefFilePath != null)) + { + AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly); + } + if (!IsScriptRunner && !sourceFilesSpecified && (outputKind.IsNetModule() || !resourcesOrModulesSpecified)) { AddDiagnostic(diagnostics, diagnosticOptions, ErrorCode.WRN_NoSources); @@ -1257,7 +1290,7 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar var emitOptions = new EmitOptions ( - metadataOnly: false, + metadataOnly: metadataOnly, debugInformationFormat: debugInformationFormat, pdbFilePath: null, // to be determined later outputNameOverride: null, // to be determined later @@ -1282,8 +1315,9 @@ internal sealed override CommandLineArguments CommonParse(IEnumerable ar Utf8Output = utf8output, CompilationName = compilationName, OutputFileName = outputFileName, + OutputRefFilePath = outputRefFilePath, PdbPath = pdbPath, - EmitPdb = emitPdb, + EmitPdb = emitPdb && !metadataOnly, SourceLink = sourceLink, OutputDirectory = outputDirectory, DocumentationPath = documentationPath, diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index f1c43315e2751..c6a3c6f9668f9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -2358,8 +2358,6 @@ internal override bool GenerateResourcesAndDocumentationComments( DiagnosticBag diagnostics, CancellationToken cancellationToken) { - Debug.Assert(!moduleBuilder.EmitOptions.EmitMetadataOnly); - // Use a temporary bag so we don't have to refilter pre-existing diagnostics. var resourceDiagnostics = DiagnosticBag.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 27c870578cf9c..611fdd2512c4d 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1460,5 +1460,7 @@ internal enum ErrorCode #endregion more stragglers for C# 7 ERR_Merge_conflict_marker_encountered = 8300, + ERR_NoRefOutWhenRefOnly = 8301, + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 8302, } } \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index a698a74eb27f9..6f90b59286f47 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -2784,6 +2784,34 @@ public void ParseOut() // error CS2005: Missing file specification for '/out:' option Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/out:")); + parsedArgs = DefaultParse(new[] { @"/refout:", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS2005: Missing file specification for '/refout:' option + Diagnostic(ErrorCode.ERR_NoFileSpec).WithArguments("/refout:")); + + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/refonly", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8301: Do not use refout when using refonly. + Diagnostic(ErrorCode.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)); + + parsedArgs = DefaultParse(new[] { "/refonly:incorrect", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS2007: Unrecognized option: '/refonly:incorrect' + Diagnostic(ErrorCode.ERR_BadSwitch).WithArguments("/refonly:incorrect").WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { @"/refout:ref.dll", "/target:module", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8302: Cannot compile net modules when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + + parsedArgs = DefaultParse(new[] { @"/refonly", "/target:module", "a.cs" }, baseDirectory); + parsedArgs.Errors.Verify( + // error CS8302: Cannot compile net modules when using /refout or /refonly. + Diagnostic(ErrorCode.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1) + ); + // Dev11 reports CS2007: Unrecognized option: '/out' parsedArgs = DefaultParse(new[] { @"/out", "a.cs" }, baseDirectory); parsedArgs.Errors.Verify( @@ -8845,6 +8873,164 @@ public void Version() } } + [Fact] + public void RefOut() + { + var dir = Temp.CreateDirectory(); + dir.CreateDirectory("ref"); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@" +class C +{ + /// Main method + public static void Main() + { + System.Console.Write(""Hello""); + } +}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.exe", "/refout:ref/a.dll", "/doc:doc.xml", "/deterministic", "a.cs" }); + + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + + var exe = Path.Combine(dir.Path, "a.exe"); + Assert.True(File.Exists(exe)); + + MetadataReaderUtils.VerifyPEMetadata(exe, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + var doc = Path.Combine(dir.Path, "doc.xml"); + Assert.True(File.Exists(doc)); + + var content = File.ReadAllText(doc); + var expectedDoc = +@" + + + a + + + + Main method + + +"; + Assert.Equal(expectedDoc, content.Trim()); + + var output = ProcessUtilities.RunAndGetOutput(exe, startFolder: dir.Path); + Assert.Equal("Hello", output.Trim()); + + var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")); + Assert.True(File.Exists(refDll)); + + // The types and members that are included needs further refinement. + // ReferenceAssemblyAttribute is missing. + // See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + output = ProcessUtilities.RunAndGetOutput(refDll, startFolder: dir.ToString(), expectedRetCode: -532462766); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + + [Fact] + public void RefOutWithError() + { + var dir = Temp.CreateDirectory(); + dir.CreateDirectory("ref"); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@"class C { public static void Main() { error(); } }"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.cs" }); + int exitCode = csc.Run(outWriter); + Assert.Equal(1, exitCode); + + var dll = Path.Combine(dir.Path, "a.dll"); + Assert.False(File.Exists(dll)); + + var refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")); + Assert.False(File.Exists(refDll)); + + Assert.Equal("a.cs(1,39): error CS0103: The name 'error' does not exist in the current context", outWriter.ToString().Trim()); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + + [Fact] + public void RefOnly() + { + var dir = Temp.CreateDirectory(); + + var src = dir.CreateFile("a.cs"); + src.WriteAllText(@" +class C +{ + /// Main method + public static void Main() + { + error(); // semantic error in method body + } +}"); + + var outWriter = new StringWriter(CultureInfo.InvariantCulture); + var csc = new MockCSharpCompiler(null, dir.Path, + new[] { "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/doc:doc.xml", "a.cs" }); + int exitCode = csc.Run(outWriter); + Assert.Equal(0, exitCode); + + var refDll = Path.Combine(dir.Path, "a.dll"); + Assert.True(File.Exists(refDll)); + + // The types and members that are included needs further refinement. + // ReferenceAssemblyAttribute is missing. + // See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + new[] { "TypeDef:", "TypeDef:C" }, + new[] { "MethodDef: Void Main()", "MethodDef: Void .ctor()" }, + new[] { "CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute" } + ); + + var pdb = Path.Combine(dir.Path, "a.pdb"); + Assert.False(File.Exists(pdb)); + + var doc = Path.Combine(dir.Path, "doc.xml"); + Assert.True(File.Exists(doc)); + + var content = File.ReadAllText(doc); + var expectedDoc = +@" + + + a + + + + Main method + + +"; + Assert.Equal(expectedDoc, content.Trim()); + + // Clean up temp files + CleanupAllGeneratedFiles(dir.Path); + } + public class QuotedArgumentTests { private void VerifyQuotedValid(string name, string value, T expected, Func getValue) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs index 222d01b278298..bd115c7776b04 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; @@ -40,7 +41,7 @@ public void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } emitResult.Diagnostics.Verify( @@ -148,7 +149,7 @@ namespace N.Foo; EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = comp.Emit(output, null, null, null); + emitResult = comp.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.False(emitResult.Success); @@ -236,6 +237,194 @@ public static void Main() } } + [Theory] + [InlineData("public int M() { return 1; }", "public int M() { return 2; }", true)] + [InlineData("public int M() { return 1; }", "public int M() { error(); }", true)] + [InlineData("", "private void M() { }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("internal void M() { }", "", false)] + [InlineData("public struct S { private int i; }", "public struct S { }", false)] + public void RefAssembly_InvariantToSomeChanges(string change1, string change2, bool expectMatch) + { + string sourceTemplate = @" +public class C +{ + CHANGE +} +"; + string name = GetUniqueName(); + string source1 = sourceTemplate.Replace("CHANGE", change1); + CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source1), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + ImmutableArray image1 = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + + var source2 = sourceTemplate.Replace("CHANGE", change2); + Compilation comp2 = CreateCompilationWithMscorlib(Parse(source2), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + ImmutableArray image2 = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(true)); + + if (expectMatch) + { + AssertEx.Equal(image1, image2); + } + else + { + AssertEx.NotEqual(image1, image2); + } + } + + [Theory] + [InlineData("public int M() { error(); }", true)] + [InlineData("public int M() { error() }", false)] // Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + [InlineData("public Error M() { return null; }", false)] // This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + public void RefAssembly_IgnoresSomeDiagnostics(string change, bool expectSuccess) + { + string sourceTemplate = @" +public class C +{ + CHANGE +} +"; + string source = sourceTemplate.Replace("CHANGE", change); + string name = GetUniqueName(); + CSharpCompilation comp1 = CreateCompilationWithMscorlib(Parse(source), + options: TestOptions.DebugDll.WithDeterministic(true), assemblyName: name); + + using (var output = new MemoryStream()) + { + var emitResult = comp1.Emit(output, options: EmitOptions.Default.WithEmitMetadataOnly(true)); + Assert.Equal(expectSuccess, emitResult.Success); + Assert.Equal(!expectSuccess, emitResult.Diagnostics.Any()); + } + } + + [Fact] + public void VerifyRefAssembly() + { + string source = @" +public abstract class PublicClass +{ + public void PublicMethod() { System.Console.Write(""Hello""); } + private void PrivateMethod() { System.Console.Write(""Hello""); } + protected void ProtectedMethod() { System.Console.Write(""Hello""); } + internal void InternalMethod() { System.Console.Write(""Hello""); } + public abstract void AbstractMethod(); +} +"; + CSharpCompilation comp = CreateCompilation(source, references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + var emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(true); + + var verifier = CompileAndVerify(comp, emitOptions: emitRefOnly, verify: true); + + // verify metadata (types, members, attributes) + var image = comp.EmitToImageReference(emitRefOnly); + var comp2 = CreateCompilation("", references: new[] { MscorlibRef, image }, + options: TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)); + AssemblySymbol assembly = comp2.SourceModule.GetReferencedAssemblySymbols().Last(); + var members = assembly.GlobalNamespace.GetMembers(); + AssertEx.SetEqual(members.Select(m => m.ToDisplayString()), + new[] { "", "PublicClass" }); + + AssertEx.SetEqual( + ((NamedTypeSymbol)assembly.GlobalNamespace.GetMember("PublicClass")).GetMembers() + .Select(m => m.ToTestDisplayString()), + new[] { "void PublicClass.PublicMethod()", "void PublicClass.PrivateMethod()", + "void PublicClass.InternalMethod()", "void PublicClass.ProtectedMethod()", + "void PublicClass.AbstractMethod()", "PublicClass..ctor()" }); + + AssertEx.SetEqual(assembly.GetAttributes().Select(a => a.AttributeClass.ToTestDisplayString()), + new[] { "System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute" }); + + var peImage = comp.EmitToArray(emitRefOnly); + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: true); + } + [Fact] + public void EmitMetadataOnly_DisallowPdbs() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var pdbOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, pdbOutput, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadataOnly_DisallowMetadataPeStream() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var metadataPeOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadata_DisallowOutputtingNetModule() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true).WithOutputKind(OutputKind.NetModule)); + + using (var output = new MemoryStream()) + using (var metadataPeOutput = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, metadataPeStream: metadataPeOutput, + options: EmitOptions.Default)); + } + } + + [Fact] + public void EmitMetadataOnly_DisallowOutputtingNetModule() + { + CSharpCompilation comp = CreateCompilation("", references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true).WithOutputKind(OutputKind.NetModule)); + + using (var output = new MemoryStream()) + { + Assert.Throws(() => comp.Emit(output, + options: EmitOptions.Default.WithEmitMetadataOnly(true))); + } + } + + [Fact] + public void EmitMetadata() + { + string source = @" +public abstract class PublicClass +{ + public void PublicMethod() { System.Console.Write(""Hello""); } +} +"; + CSharpCompilation comp = CreateCompilation(source, references: new[] { MscorlibRef }, + options: TestOptions.DebugDll.WithDeterministic(true)); + + using (var output = new MemoryStream()) + using (var pdbOutput = new MemoryStream()) + using (var metadataOutput = new MemoryStream()) + { + var result = comp.Emit(output, pdbOutput, metadataPeStream: metadataOutput); + Assert.True(result.Success); + Assert.NotEqual(0, output.Position); + Assert.NotEqual(0, pdbOutput.Position); + Assert.NotEqual(0, metadataOutput.Position); + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull: false); + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull: true); + } + + var peImage = comp.EmitToArray(); + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull: false); + } + /// /// Check that when we emit metadata only, we include metadata for /// compiler generate methods (e.g. the ones for implicit interface @@ -939,7 +1128,7 @@ public class Test EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.False(emitResult.Success); @@ -972,7 +1161,7 @@ public static void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.True(emitResult.Success); @@ -1010,7 +1199,7 @@ public static void Main() EmitResult emitResult; using (var output = new MemoryStream()) { - emitResult = compilation.Emit(output, null, null, null); + emitResult = compilation.Emit(output, pdbStream: null, xmlDocumentationStream: null, win32Resources: null); } Assert.True(emitResult.Success); @@ -2726,7 +2915,7 @@ public void BrokenPDBStream() var output = new MemoryStream(); var pdb = new BrokenStream(); pdb.BreakHow = BrokenStream.BreakHowType.ThrowOnSetLength; - var result = compilation.Emit(output, pdb); + var result = compilation.Emit(output, pdbStream: pdb); // error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' var err = result.Diagnostics.Single(); @@ -2737,7 +2926,7 @@ public void BrokenPDBStream() Assert.Equal(ioExceptionMessage, (string)err.Arguments[0]); pdb.Dispose(); - result = compilation.Emit(output, pdb); + result = compilation.Emit(output, pdbStream: pdb); // error CS0041: Unexpected error writing debug information -- 'Exception from HRESULT: 0x806D0004' err = result.Diagnostics.Single(); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs index 7af8a4edd7fee..12c92566cb4d3 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs @@ -66,8 +66,8 @@ public void BanVersionWildcards() references: new[] { MscorlibRef }, options: TestOptions.DebugDll.WithDeterministic(false)); - var resultDeterministic = compilationDeterministic.Emit(Stream.Null, Stream.Null); - var resultNonDeterministic = compilationNonDeterministic.Emit(Stream.Null, Stream.Null); + var resultDeterministic = compilationDeterministic.Emit(Stream.Null, pdbStream: Stream.Null); + var resultNonDeterministic = compilationNonDeterministic.Emit(Stream.Null, pdbStream: Stream.Null); Assert.False(resultDeterministic.Success); Assert.True(resultNonDeterministic.Success); diff --git a/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs index dc95a75b61541..a416a2b5979c8 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/ResourceTests.cs @@ -432,323 +432,321 @@ public void AddManagedResource() c1 = null; } + [Fact] public void AddResourceToModule() { - for (int metadataOnlyIfNonzero = 0; metadataOnlyIfNonzero < 2; metadataOnlyIfNonzero++) - { - var metadataOnly = metadataOnlyIfNonzero != 0; - Func emit; - emit = (c, s, r) => c.Emit(s, manifestResources: r, options: new EmitOptions(metadataOnly: metadataOnly)); + bool metadataOnly = false; + Func emit; + emit = (c, s, r) => c.Emit(s, manifestResources: r, options: new EmitOptions(metadataOnly: metadataOnly)); - var sourceTree = SyntaxFactory.ParseSyntaxTree(""); + var sourceTree = SyntaxFactory.ParseSyntaxTree(""); - // Do not name the compilation, a unique guid is used as a name by default. It prevents conflicts with other assemblies loaded via Assembly.ReflectionOnlyLoad. - var c1 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + // Do not name the compilation, a unique guid is used as a name by default. It prevents conflicts with other assemblies loaded via Assembly.ReflectionOnlyLoad. + var c1 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var resourceFileName = "RoslynResourceFile.foo"; - var output = new MemoryStream(); + var resourceFileName = "RoslynResourceFile.foo"; + var output = new MemoryStream(); - const string r1Name = "some.dotted.NAME"; - const string r2Name = "another.DoTtEd.NAME"; + const string r1Name = "some.dotted.NAME"; + const string r2Name = "another.DoTtEd.NAME"; - var arrayOfEmbeddedData = new byte[] { 1, 2, 3, 4, 5 }; - var resourceFileData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + var arrayOfEmbeddedData = new byte[] { 1, 2, 3, 4, 5 }; + var resourceFileData = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; - var result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) - }); + var result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false), - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) - }); + result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false), + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - result = emit(c1, output, - new ResourceDescription[] - { - new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) - }); + result = emit(c1, output, + new ResourceDescription[] + { + new ResourceDescription(r2Name, resourceFileName, () => new MemoryStream(resourceFileData), false) + }); - Assert.False(result.Success); - Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); + Assert.False(result.Success); + Assert.NotEmpty(result.Diagnostics.Where(x => x.Code == (int)ErrorCode.ERR_CantRefResource)); - var c_mod1 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + var c_mod1 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var output_mod1 = new MemoryStream(); - result = emit(c_mod1, output_mod1, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) - }); + var output_mod1 = new MemoryStream(); + result = emit(c_mod1, output_mod1, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true) + }); - Assert.True(result.Success); - var mod1 = ModuleMetadata.CreateFromImage(output_mod1.ToImmutable()); - var ref_mod1 = mod1.GetReference(); - Assert.Equal(ManifestResourceAttributes.Public, mod1.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); + Assert.True(result.Success); + var mod1 = ModuleMetadata.CreateFromImage(output_mod1.ToImmutable()); + var ref_mod1 = mod1.GetReference(); + Assert.Equal(ManifestResourceAttributes.Public, mod1.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); - { - var c2 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1 }, TestOptions.ReleaseDll); - var output2 = new MemoryStream(); - var result2 = c2.Emit(output2); + { + var c2 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1 }, TestOptions.ReleaseDll); + var output2 = new MemoryStream(); + var result2 = c2.Emit(output2); - Assert.True(result2.Success); - var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output2.ToArray()); + Assert.True(result2.Success); + var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output2.ToArray()); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod1.SourceModule.Name)) { - if (e.Name.Equals(c_mod1.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod1.ToArray()); - } + return assembly.LoadModule(e.Name, output_mod1.ToArray()); + } - return null; - }; + return null; + }; - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(1, resourceNames.Length); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(1, resourceNames.Length); - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(System.Reflection.ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(System.Reflection.ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - } + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + } - var c_mod2 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + var c_mod2 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); - var output_mod2 = new MemoryStream(); - result = emit(c_mod2, output_mod2, - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), true) - }); + var output_mod2 = new MemoryStream(); + result = emit(c_mod2, output_mod2, + new ResourceDescription[] + { + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), true), + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), true) + }); - Assert.True(result.Success); - var ref_mod2 = ModuleMetadata.CreateFromImage(output_mod2.ToImmutable()).GetReference(); + Assert.True(result.Success); + var ref_mod2 = ModuleMetadata.CreateFromImage(output_mod2.ToImmutable()).GetReference(); - { - var c3 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod2 }, TestOptions.ReleaseDll); - var output3 = new MemoryStream(); - var result3 = c3.Emit(output3); + { + var c3 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod2 }, TestOptions.ReleaseDll); + var output3 = new MemoryStream(); + var result3 = c3.Emit(output3); - Assert.True(result3.Success); - var assembly = Assembly.ReflectionOnlyLoad(output3.ToArray()); + Assert.True(result3.Success); + var assembly = Assembly.ReflectionOnlyLoad(output3.ToArray()); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod2.SourceModule.Name)) { - if (e.Name.Equals(c_mod2.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod2.ToArray()); - } - - return null; - }; - - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); - - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + return assembly.LoadModule(e.Name, output_mod2.ToArray()); + } - var c_mod3 = CSharpCompilation.Create( - Guid.NewGuid().ToString(), - new[] { sourceTree }, - new[] { MscorlibRef }, - TestOptions.ReleaseModule); + return null; + }; - var output_mod3 = new MemoryStream(); - result = emit(c_mod3, output_mod3, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); + + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); - Assert.True(result.Success); - var mod3 = ModuleMetadata.CreateFromImage(output_mod3.ToImmutable()); - var ref_mod3 = mod3.GetReference(); - Assert.Equal(ManifestResourceAttributes.Private, mod3.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod2.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } + + var c_mod3 = CSharpCompilation.Create( + Guid.NewGuid().ToString(), + new[] { sourceTree }, + new[] { MscorlibRef }, + TestOptions.ReleaseModule); + + var output_mod3 = new MemoryStream(); + result = emit(c_mod3, output_mod3, + new ResourceDescription[] { - var c4 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod3 }, TestOptions.ReleaseDll); - var output4 = new MemoryStream(); - var result4 = c4.Emit(output4, manifestResources: - new ResourceDescription[] - { - new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), false) - }); + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); - Assert.True(result4.Success); - var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output4.ToArray()); + Assert.True(result.Success); + var mod3 = ModuleMetadata.CreateFromImage(output_mod3.ToImmutable()); + var ref_mod3 = mod3.GetReference(); + Assert.Equal(ManifestResourceAttributes.Private, mod3.Module.GetEmbeddedResourcesOrThrow()[0].Attributes); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + var c4 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod3 }, TestOptions.ReleaseDll); + var output4 = new MemoryStream(); + var result4 = c4.Emit(output4, manifestResources: + new ResourceDescription[] { - if (e.Name.Equals(c_mod3.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod3.ToArray()); - } + new ResourceDescription(r1Name, () => new MemoryStream(arrayOfEmbeddedData), false) + }); - return null; - }; + Assert.True(result4.Success); + var assembly = System.Reflection.Assembly.ReflectionOnlyLoad(output4.ToArray()); - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => + { + if (e.Name.Equals(c_mod3.SourceModule.Name)) + { + return assembly.LoadModule(e.Name, output_mod3.ToArray()); + } - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile, rInfo.ResourceLocation); + return null; + }; - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded | ResourceLocation.ContainedInManifestFile, rInfo.ResourceLocation); - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); - { - var c5 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod3 }, TestOptions.ReleaseDll); - var output5 = new MemoryStream(); - var result5 = emit(c5, output5, null); + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } - Assert.True(result5.Success); - var assembly = Assembly.ReflectionOnlyLoad(output5.ToArray()); + { + var c5 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod3 }, TestOptions.ReleaseDll); + var output5 = new MemoryStream(); + var result5 = emit(c5, output5, null); - assembly.ModuleResolve += (object sender, ResolveEventArgs e) => - { - if (e.Name.Equals(c_mod1.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod1.ToArray()); - } - else if (e.Name.Equals(c_mod3.SourceModule.Name)) - { - return assembly.LoadModule(e.Name, output_mod3.ToArray()); - } - - return null; - }; - - string[] resourceNames = assembly.GetManifestResourceNames(); - Assert.Equal(2, resourceNames.Length); - - var rInfo = assembly.GetManifestResourceInfo(r1Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); - - var rData = assembly.GetManifestResourceStream(r1Name); - var rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(arrayOfEmbeddedData, rBytes); - - rInfo = assembly.GetManifestResourceInfo(r2Name); - Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); - Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); - - rData = assembly.GetManifestResourceStream(r2Name); - rBytes = new byte[rData.Length]; - rData.Read(rBytes, 0, (int)rData.Length); - Assert.Equal(resourceFileData, rBytes); - } + Assert.True(result5.Success); + var assembly = Assembly.ReflectionOnlyLoad(output5.ToArray()); + assembly.ModuleResolve += (object sender, ResolveEventArgs e) => { - var c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseDll); - var output6 = new MemoryStream(); - var result6 = emit(c6, output6, null); - - if (metadataOnly) + if (e.Name.Equals(c_mod1.SourceModule.Name)) { - Assert.True(result6.Success); + return assembly.LoadModule(e.Name, output_mod1.ToArray()); } - else + else if (e.Name.Equals(c_mod3.SourceModule.Name)) { - Assert.False(result6.Success); - result6.Diagnostics.Verify( - // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME") - ); + return assembly.LoadModule(e.Name, output_mod3.ToArray()); } - result6 = emit(c6, output6, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + return null; + }; - if (metadataOnly) - { - Assert.True(result6.Success); - } - else - { - Assert.False(result6.Success); - result6.Diagnostics.Verify( - // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME"), - // error CS1508: Resource identifier 'another.DoTtEd.NAME' has already been used in this assembly - Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("another.DoTtEd.NAME") - ); - } + string[] resourceNames = assembly.GetManifestResourceNames(); + Assert.Equal(2, resourceNames.Length); + + var rInfo = assembly.GetManifestResourceInfo(r1Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod1.SourceModule.Name, rInfo.FileName); + + var rData = assembly.GetManifestResourceStream(r1Name); + var rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(arrayOfEmbeddedData, rBytes); + + rInfo = assembly.GetManifestResourceInfo(r2Name); + Assert.Equal(ResourceLocation.Embedded, rInfo.ResourceLocation); + Assert.Equal(c_mod3.SourceModule.Name, rInfo.FileName); + + rData = assembly.GetManifestResourceStream(r2Name); + rBytes = new byte[rData.Length]; + rData.Read(rBytes, 0, (int)rData.Length); + Assert.Equal(resourceFileData, rBytes); + } - c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseModule); + { + var c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseDll); + var output6 = new MemoryStream(); + var result6 = emit(c6, output6, null); + + if (metadataOnly) + { + Assert.True(result6.Success); + } + else + { + Assert.False(result6.Success); + result6.Diagnostics.Verify( + // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME") + ); + } - result6 = emit(c6, output6, - new ResourceDescription[] - { - new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) - }); + result6 = emit(c6, output6, + new ResourceDescription[] + { + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); + if (metadataOnly) + { Assert.True(result6.Success); } + else + { + Assert.False(result6.Success); + result6.Diagnostics.Verify( + // error CS1508: Resource identifier 'some.dotted.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("some.dotted.NAME"), + // error CS1508: Resource identifier 'another.DoTtEd.NAME' has already been used in this assembly + Diagnostic(ErrorCode.ERR_ResourceNotUnique).WithArguments("another.DoTtEd.NAME") + ); + } + + c6 = CreateCompilationWithMscorlib(sourceTree, new[] { ref_mod1, ref_mod2 }, TestOptions.ReleaseModule); + + result6 = emit(c6, output6, + new ResourceDescription[] + { + new ResourceDescription(r2Name, () => new MemoryStream(resourceFileData), false) + }); + + Assert.True(result6.Success); } } diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index a0fa5bba7f79f..97817fcf5278a 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -133,6 +133,7 @@ public void SymWriterErrors() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -166,6 +167,7 @@ public void SymWriterErrors2() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), @@ -199,6 +201,7 @@ public void SymWriterErrors3() { var result = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, cancellationToken: default(CancellationToken), diff --git a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index b137cf9c561e6..a72cdd6b1281c 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -227,6 +227,13 @@ public ITaskItem OutputAssembly get { return (ITaskItem)_store[nameof(OutputAssembly)]; } } + [Output] + public ITaskItem OutputRefAssembly + { + set { _store[nameof(OutputRefAssembly)] = value; } + get { return (ITaskItem)_store[nameof(OutputRefAssembly)]; } + } + public string Platform { set { _store[nameof(Platform)] = value; } @@ -251,6 +258,12 @@ public ITaskItem[] References get { return (ITaskItem[])_store[nameof(References)]; } } + public bool RefOnly + { + set { _store[nameof(RefOnly)] = value; } + get { return _store.GetOrDefault(nameof(RefOnly), false); } + } + public bool ReportAnalyzer { set { _store[nameof(ReportAnalyzer)] = value; } @@ -705,6 +718,8 @@ protected internal virtual void AddResponseFileCommands(CommandLineBuilderExtens commandLine.AppendPlusOrMinusSwitch("/optimize", _store, nameof(Optimize)); commandLine.AppendSwitchIfNotNull("/pathmap:", PathMap); commandLine.AppendSwitchIfNotNull("/out:", OutputAssembly); + commandLine.AppendSwitchIfNotNull("/refout:", OutputRefAssembly); + commandLine.AppendWhenTrue("/refonly", _store, nameof(RefOnly)); commandLine.AppendSwitchIfNotNull("/ruleset:", CodeAnalysisRuleSet); commandLine.AppendSwitchIfNotNull("/errorlog:", ErrorLog); commandLine.AppendSwitchIfNotNull("/subsystemversion:", SubsystemVersion); diff --git a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs index 3f538e72d9d7e..bd3e3dd55c145 100644 --- a/src/Compilers/Core/MSBuildTaskTests/CscTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/CscTests.cs @@ -286,5 +286,23 @@ public void Embed() csc.EmbeddedFiles = MSBuildUtil.CreateTaskItems(); Assert.Equal(@"/debug:portable /out:test.exe test.cs", csc.GenerateResponseFileContents()); } + + [Fact] + public void RefOut() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.OutputRefAssembly = MSBuildUtil.CreateTaskItem("ref\\test.dll"); + Assert.Equal("/out:test.exe /refout:ref\\test.dll test.cs", csc.GenerateResponseFileContents()); + } + + [Fact] + public void RefOnly() + { + var csc = new Csc(); + csc.Sources = MSBuildUtil.CreateTaskItems("test.cs"); + csc.RefOnly = true; + Assert.Equal("/out:test.exe /refonly test.cs", csc.GenerateResponseFileContents()); + } } } diff --git a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs index 554cc2b11043e..dd0b5ca23fca7 100644 --- a/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs +++ b/src/Compilers/Core/MSBuildTaskTests/VbcTests.cs @@ -283,5 +283,23 @@ public void Embed() vbc.EmbeddedFiles = MSBuildUtil.CreateTaskItems(); Assert.Equal(@"/optionstrict:custom /debug:portable /out:test.exe test.vb", vbc.GenerateResponseFileContents()); } + + [Fact] + public void RefOut() + { + var vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("test.vb"); + vbc.OutputRefAssembly = MSBuildUtil.CreateTaskItem("ref\\test.dll"); + Assert.Equal("/optionstrict:custom /out:test.exe /refout:ref\\test.dll test.vb", vbc.GenerateResponseFileContents()); + } + + [Fact] + public void RefOnly() + { + var vbc = new Vbc(); + vbc.Sources = MSBuildUtil.CreateTaskItems("test.vb"); + vbc.RefOnly = true; + Assert.Equal("/optionstrict:custom /out:test.exe /refonly test.vb", vbc.GenerateResponseFileContents()); + } } } diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs index f7d3bbd3e231d..7dd8597b0b3b5 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.Designer.cs @@ -269,6 +269,15 @@ internal static string CannotEmbedInteropTypesFromModule { } } + /// + /// Looks up a localized string similar to Cannot target net module when emitting ref assembly.. + /// + internal static string CannotTargetNetModuleWhenEmittingRefAssembly { + get { + return ResourceManager.GetString("CannotTargetNetModuleWhenEmittingRefAssembly", resourceCulture); + } + } + /// /// Looks up a localized string similar to Can't create a module reference to an assembly.. /// @@ -847,6 +856,15 @@ internal static string KeyInPathMapEndsWithSeparator { } } + /// + /// Looks up a localized string similar to Metadata PE stream should not be given when emitting metadata only.. + /// + internal static string MetadataPeStreamUnexpectedWhenEmittingMetadataOnly { + get { + return ResourceManager.GetString("MetadataPeStreamUnexpectedWhenEmittingMetadataOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to MetadataReference '{0}' not found to remove.. /// @@ -1027,6 +1045,15 @@ internal static string PdbStreamUnexpectedWhenEmbedding { } } + /// + /// Looks up a localized string similar to PDB stream should not be given when emitting metadata only.. + /// + internal static string PdbStreamUnexpectedWhenEmittingMetadataOnly { + get { + return ResourceManager.GetString("PdbStreamUnexpectedWhenEmittingMetadataOnly", resourceCulture); + } + } + /// /// Looks up a localized string similar to PE image doesn't contain managed metadata.. /// diff --git a/src/Compilers/Core/Portable/CodeAnalysisResources.resx b/src/Compilers/Core/Portable/CodeAnalysisResources.resx index 3af8d6f2de563..928a36ef445d3 100644 --- a/src/Compilers/Core/Portable/CodeAnalysisResources.resx +++ b/src/Compilers/Core/Portable/CodeAnalysisResources.resx @@ -330,6 +330,15 @@ PDB stream should not be given when embedding PDB into the PE stream. + + PDB stream should not be given when emitting metadata only. + + + Metadata PE stream should not be given when emitting metadata only. + + + Cannot target net module when emitting ref assembly. + Invalid hash. diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs index 75c37feaf7c6e..ee5f34e2d4b71 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCommandLineArguments.cs @@ -85,6 +85,11 @@ public abstract class CommandLineArguments /// public string OutputFileName { get; internal set; } + /// + /// Path of the output ref assembly or null if not specified. + /// + public string OutputRefFilePath { get; internal set; } + /// /// Path of the PDB file or null if same as output binary path with .pdb extension. /// diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 8aeb8b3515313..21702d732cdd5 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -238,7 +238,7 @@ private static FileStream OpenFileForReadWithSmallBufferOptimization(string file // size, FileStream.Read still allocates the internal buffer. return new FileStream( filePath, - FileMode.Open, + FileMode.Open, FileAccess.Read, FileShare.ReadWrite, bufferSize: 1, @@ -546,7 +546,7 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella { return Failed; } - + bool reportAnalyzer = false; CancellationTokenSource analyzerCts = null; AnalyzerManager analyzerManager = null; @@ -736,11 +736,15 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella var peStreamProvider = new CompilerEmitStreamProvider(this, finalPeFilePath); var pdbStreamProviderOpt = emitPdbFile ? new CompilerEmitStreamProvider(this, finalPdbFilePath) : null; + string finalRefPeFilePath = Arguments.OutputRefFilePath; + var refPeStreamProviderOpt = finalRefPeFilePath != null ? new CompilerEmitStreamProvider(this, finalRefPeFilePath) : null; + try { success = compilation.SerializeToPeStream( moduleBeingBuilt, peStreamProvider, + refPeStreamProviderOpt, pdbStreamProviderOpt, testSymWriterFactory: null, diagnostics: diagnosticBag, @@ -750,6 +754,7 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella finally { peStreamProvider.Close(diagnosticBag); + refPeStreamProviderOpt?.Close(diagnosticBag); pdbStreamProviderOpt?.Close(diagnosticBag); } @@ -759,6 +764,10 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella { touchedFilesLogger.AddWritten(finalPdbFilePath); } + if (refPeStreamProviderOpt != null) + { + touchedFilesLogger.AddWritten(finalRefPeFilePath); + } touchedFilesLogger.AddWritten(finalPeFilePath); } } @@ -803,48 +812,9 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella } cancellationToken.ThrowIfCancellationRequested(); - - if (Arguments.TouchedFilesPath != null) + if (!WriteTouchedFiles(consoleOutput, touchedFilesLogger, finalXmlFilePath)) { - Debug.Assert(touchedFilesLogger != null); - - if (finalXmlFilePath != null) - { - touchedFilesLogger.AddWritten(finalXmlFilePath); - } - - string readFilesPath = Arguments.TouchedFilesPath + ".read"; - string writtenFilesPath = Arguments.TouchedFilesPath + ".write"; - - var readStream = OpenFile(readFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); - var writtenStream = OpenFile(writtenFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); - - if (readStream == null || writtenStream == null) - { - return Failed; - } - - string filePath = null; - try - { - filePath = readFilesPath; - using (var writer = new StreamWriter(readStream)) - { - touchedFilesLogger.WriteReadPaths(writer); - } - - filePath = writtenFilesPath; - using (var writer = new StreamWriter(writtenStream)) - { - touchedFilesLogger.WriteWrittenPaths(writer); - } - } - catch (Exception e) - { - Debug.Assert(filePath != null); - MessageProvider.ReportStreamWriteException(e, filePath, consoleOutput); - return Failed; - } + return Failed; } } finally @@ -873,6 +843,54 @@ internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, Cancella return Succeeded; } + private bool WriteTouchedFiles(TextWriter consoleOutput, TouchedFileLogger touchedFilesLogger, string finalXmlFilePath) + { + if (Arguments.TouchedFilesPath != null) + { + Debug.Assert(touchedFilesLogger != null); + + if (finalXmlFilePath != null) + { + touchedFilesLogger.AddWritten(finalXmlFilePath); + } + + string readFilesPath = Arguments.TouchedFilesPath + ".read"; + string writtenFilesPath = Arguments.TouchedFilesPath + ".write"; + + var readStream = OpenFile(readFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); + var writtenStream = OpenFile(writtenFilesPath, consoleOutput, mode: FileMode.OpenOrCreate); + + if (readStream == null || writtenStream == null) + { + return false; + } + + string filePath = null; + try + { + filePath = readFilesPath; + using (var writer = new StreamWriter(readStream)) + { + touchedFilesLogger.WriteReadPaths(writer); + } + + filePath = writtenFilesPath; + using (var writer = new StreamWriter(writtenStream)) + { + touchedFilesLogger.WriteWrittenPaths(writer); + } + } + catch (Exception e) + { + Debug.Assert(filePath != null); + MessageProvider.ReportStreamWriteException(e, filePath, consoleOutput); + return false; + } + } + + return true; + } + protected virtual ImmutableArray ResolveAdditionalFilesFromArguments(List diagnostics, CommonMessageProvider messageProvider, TouchedFileLogger touchedFilesLogger) { var builder = ImmutableArray.CreateBuilder(); diff --git a/src/Compilers/Core/Portable/Compilation/Compilation.cs b/src/Compilers/Core/Portable/Compilation/Compilation.cs index 4eeb2bfd0298b..a9ffb9d65fe35 100644 --- a/src/Compilers/Core/Portable/Compilation/Compilation.cs +++ b/src/Compilers/Core/Portable/Compilation/Compilation.cs @@ -1964,10 +1964,38 @@ public EmitResult Emit( cancellationToken); } + // 2.0 BACKCOMPAT OVERLOAD -- DO NOT TOUCH + public EmitResult Emit( + Stream peStream, + Stream pdbStream, + Stream xmlDocumentationStream, + Stream win32Resources, + IEnumerable manifestResources, + EmitOptions options, + IMethodSymbol debugEntryPoint, + Stream sourceLinkStream, + IEnumerable embeddedTexts, + CancellationToken cancellationToken) + { + return Emit( + peStream, + pdbStream, + xmlDocumentationStream, + win32Resources, + manifestResources, + options, + debugEntryPoint, + sourceLinkStream, + embeddedTexts, + metadataPeStream: null, + cancellationToken: cancellationToken); + } + /// /// Emit the IL for the compiled source code into the specified stream. /// /// Stream to which the compilation will be written. + /// Stream to which the metadata-only output will be written. /// Stream to which the compilation's debug info will be written. Null to forego PDB generation. /// Stream to which the compilation's XML documentation will be written. Null to forego XML generation. /// Stream from which the compilation's Win32 resources will be read (in RES format). @@ -2008,6 +2036,7 @@ public EmitResult Emit( IMethodSymbol debugEntryPoint = null, Stream sourceLinkStream = null, IEnumerable embeddedTexts = null, + Stream metadataPeStream = null, CancellationToken cancellationToken = default(CancellationToken)) { if (peStream == null) @@ -2031,6 +2060,28 @@ public EmitResult Emit( { throw new ArgumentException(CodeAnalysisResources.StreamMustSupportWrite, nameof(pdbStream)); } + + if (options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.PdbStreamUnexpectedWhenEmittingMetadataOnly, nameof(pdbStream)); + } + } + + if (metadataPeStream != null && options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.MetadataPeStreamUnexpectedWhenEmittingMetadataOnly, nameof(metadataPeStream)); + } + + if (this.Options.OutputKind == OutputKind.NetModule) + { + if (metadataPeStream != null) + { + throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(metadataPeStream)); + } + else if (options?.EmitMetadataOnly == true) + { + throw new ArgumentException(CodeAnalysisResources.CannotTargetNetModuleWhenEmittingRefAssembly, nameof(options.EmitMetadataOnly)); + } } if (win32Resources != null) @@ -2055,6 +2106,7 @@ public EmitResult Emit( throw new ArgumentException(CodeAnalysisResources.StreamMustSupportRead, nameof(sourceLinkStream)); } } + if (embeddedTexts != null && !embeddedTexts.IsEmpty()) { if (options == null || @@ -2067,6 +2119,7 @@ public EmitResult Emit( return Emit( peStream, + metadataPeStream, pdbStream, xmlDocumentationStream, win32Resources, @@ -2085,6 +2138,7 @@ public EmitResult Emit( /// internal EmitResult Emit( Stream peStream, + Stream metadataPeStream, Stream pdbStream, Stream xmlDocumentationStream, Stream win32Resources, @@ -2152,6 +2206,7 @@ internal EmitResult Emit( success = SerializeToPeStream( moduleBeingBuilt, new SimpleEmitStreamProvider(peStream), + (metadataPeStream != null) ? new SimpleEmitStreamProvider(metadataPeStream) : null, (pdbStream != null) ? new SimpleEmitStreamProvider(pdbStream) : null, testData?.SymWriterFactory, diagnostics, @@ -2318,6 +2373,7 @@ internal CommonPEModuleBuilder CheckOptionsAndCreateModuleBuilder( internal bool SerializeToPeStream( CommonPEModuleBuilder moduleBeingBuilt, EmitStreamProvider peStreamProvider, + EmitStreamProvider metadataPeStreamProvider, EmitStreamProvider pdbStreamProvider, Func testSymWriterFactory, DiagnosticBag diagnostics, @@ -2331,6 +2387,7 @@ internal bool SerializeToPeStream( DiagnosticBag metadataDiagnostics = null; DiagnosticBag pdbBag = null; Stream peStream = null; + Stream refPeStream = null; Stream portablePdbStream = null; bool deterministic = IsEmitDeterministic; @@ -2429,12 +2486,33 @@ internal bool SerializeToPeStream( return retStream; }; + Func getRefPeStream; + if (metadataPeStreamProvider != null) + { + getRefPeStream = () => + { + if (metadataDiagnostics.HasAnyErrors()) + { + return null; + } + + refPeStream = metadataPeStreamProvider.GetOrCreateStream(metadataDiagnostics); + Debug.Assert(refPeStream != null || metadataDiagnostics.HasAnyErrors()); + return refPeStream; + }; + } + else + { + getRefPeStream = null; + } + try { if (Cci.PeWriter.WritePeToStream( new EmitContext(moduleBeingBuilt, null, metadataDiagnostics), this.MessageProvider, getPeStream, + getRefPeStream, getPortablePdbStream, nativePdbWriter, pePdbPath, diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs index 8cc8d4d317326..7c8b2fdb0ec67 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DeltaMetadataWriter.cs @@ -55,7 +55,7 @@ public DeltaMetadataWriter( dynamicAnalysisDataWriterOpt: null, context: context, messageProvider: messageProvider, - allowMissingMethodBodies: false, + metadataOnly: false, deterministic: false, cancellationToken: cancellationToken) { diff --git a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs index ddca0955c2b60..47e8694c2b927 100644 --- a/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/FullMetadataWriter.cs @@ -37,7 +37,7 @@ internal sealed class FullMetadataWriter : MetadataWriter public static MetadataWriter Create( EmitContext context, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, bool hasPdbStream, CancellationToken cancellationToken) @@ -63,7 +63,7 @@ public static MetadataWriter Create( new DynamicAnalysisDataWriter(context.Module.DebugDocumentCount, context.Module.HintNumberOfMethodDefinitions) : null; - return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, allowMissingMethodBodies, deterministic, cancellationToken); + return new FullMetadataWriter(context, builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, messageProvider, metadataOnly, deterministic, cancellationToken); } private FullMetadataWriter( @@ -72,10 +72,10 @@ private FullMetadataWriter( MetadataBuilder debugBuilderOpt, DynamicAnalysisDataWriter dynamicAnalysisDataWriterOpt, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, CancellationToken cancellationToken) - : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, allowMissingMethodBodies, deterministic, cancellationToken) + : base(builder, debugBuilderOpt, dynamicAnalysisDataWriterOpt, context, messageProvider, metadataOnly, deterministic, cancellationToken) { // EDMAURER make some intelligent guesses for the initial sizes of these things. int numMethods = this.module.HintNumberOfMethodDefinitions; diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index c65fe050a3595..d54aee2c74a85 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -65,25 +65,27 @@ internal abstract partial class MetadataWriter private readonly int _numTypeDefsEstimate; private readonly bool _deterministic; - // If true, it is allowed to have methods not have bodies (for emitting metadata-only assembly) - internal readonly bool allowMissingMethodBodies; + internal readonly bool MetadataOnly; // A map of method body before token translation to RVA. Used for deduplication of small bodies. private readonly Dictionary, int> _smallMethodBodies; + private static readonly ImmutableArray ThrowNullIL = ImmutableArray.Create((byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw); + private const int ThrowNullMaxStack = 1; + protected MetadataWriter( MetadataBuilder metadata, MetadataBuilder debugMetadataOpt, DynamicAnalysisDataWriter dynamicAnalysisDataWriterOpt, EmitContext context, CommonMessageProvider messageProvider, - bool allowMissingMethodBodies, + bool metadataOnly, bool deterministic, CancellationToken cancellationToken) { this.module = context.Module; _deterministic = deterministic; - this.allowMissingMethodBodies = allowMissingMethodBodies; + this.MetadataOnly = metadataOnly; // EDMAURER provide some reasonable size estimates for these that will avoid // much of the reallocation that would occur when growing these from empty. @@ -2831,22 +2833,30 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr if (method.HasBody()) { - body = method.GetBody(Context); - Debug.Assert(body != null || allowMissingMethodBodies); - - if (body != null) + if (MetadataOnly) { - localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); - - // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) - bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); - - nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); + bodyOffset = SerializeThrowNullMethodBody(encoder); + localSignatureHandleOpt = default(StandaloneSignatureHandle); + body = null; } else { - bodyOffset = 0; - localSignatureHandleOpt = default(StandaloneSignatureHandle); + body = method.GetBody(Context); + + if (body != null) + { + localSignatureHandleOpt = this.SerializeLocalVariablesSignature(body); + + // TODO: consider parallelizing these (local signature tokens can be piped into IL serialization & debug info generation) + bodyOffset = SerializeMethodBody(encoder, body, localSignatureHandleOpt, ref mvidStringHandle, ref mvidStringFixup); + + nativePdbWriterOpt?.SerializeDebugInfo(body, localSignatureHandleOpt, customDebugInfoWriter); + } + else + { + bodyOffset = 0; + localSignatureHandleOpt = default(StandaloneSignatureHandle); + } } } else @@ -2863,7 +2873,7 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr } _dynamicAnalysisDataWriterOpt?.SerializeMethodDynamicAnalysisData(body); - + bodyOffsets[methodRid - 1] = bodyOffset; methodRid++; @@ -2872,40 +2882,84 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr return bodyOffsets; } - private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody methodBody, StandaloneSignatureHandle localSignatureHandleOpt, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup) - { - int ilLength = methodBody.IL.Length; - var exceptionRegions = methodBody.ExceptionRegions; - bool isSmallBody = ilLength < 64 && methodBody.MaxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; - - // Check if an identical method body has already been serialized. + private int SerializeThrowNullMethodBody(MethodBodyStreamEncoder encoder) + { + UserStringHandle mvidStringHandle = default(UserStringHandle); + Blob mvidStringFixup = default(Blob); + + int offset = SerializeMethodBody( + encoder, + ThrowNullIL, + ThrowNullMaxStack, + ImmutableArray.Empty, + default(StandaloneSignatureHandle), + MethodBodyAttributes.None, + ref mvidStringHandle, + ref mvidStringFixup); + + Debug.Assert(mvidStringHandle.Equals(default(UserStringHandle)) && mvidStringFixup.Equals(default(Blob))); + return offset; + } + + private int SerializeMethodBody( + MethodBodyStreamEncoder encoder, + IMethodBody methodBody, + StandaloneSignatureHandle localSignatureHandleOpt, + ref UserStringHandle mvidStringHandle, + ref Blob mvidStringFixup) + { + return SerializeMethodBody( + encoder, + methodBody.IL, + methodBody.MaxStack, + methodBody.ExceptionRegions, + localSignatureHandleOpt, + (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : MethodBodyAttributes.None), + ref mvidStringHandle, + ref mvidStringFixup); + } + + private int SerializeMethodBody( + MethodBodyStreamEncoder encoder, + ImmutableArray il, + ushort maxStack, + ImmutableArray exceptionRegions, + StandaloneSignatureHandle localSignatureHandleOpt, + MethodBodyAttributes methodBodyAttributes, + ref UserStringHandle mvidStringHandle, + ref Blob mvidStringFixup) + { + int ilLength = il.Length; + bool isSmallBody = ilLength < 64 && maxStack <= 8 && localSignatureHandleOpt.IsNil && exceptionRegions.Length == 0; + + // Check if an identical method body has already been serialized. // If so, use the RVA of the already serialized one. // Note that we don't need to rewrite the fake tokens in the body before looking it up. // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 int bodyOffset; - if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(methodBody.IL, out bodyOffset)) + if (!_deterministic && isSmallBody && _smallMethodBodies.TryGetValue(il, out bodyOffset)) { return bodyOffset; } var encodedBody = encoder.AddMethodBody( - codeSize: methodBody.IL.Length, - maxStack: methodBody.MaxStack, - exceptionRegionCount: exceptionRegions.Length, + codeSize: ilLength, + maxStack: maxStack, + exceptionRegionCount: exceptionRegions.Length, hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions), - localVariablesSignature: localSignatureHandleOpt, - attributes: (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : 0)); + localVariablesSignature: localSignatureHandleOpt, + attributes: methodBodyAttributes); // Don't do small body method caching during deterministic builds until this issue is fixed // https://github.com/dotnet/roslyn/issues/7595 if (isSmallBody && !_deterministic) { - _smallMethodBodies.Add(methodBody.IL, encodedBody.Offset); + _smallMethodBodies.Add(il, encodedBody.Offset); } - WriteInstructions(encodedBody.Instructions, methodBody.IL, ref mvidStringHandle, ref mvidStringFixup); + WriteInstructions(encodedBody.Instructions, il, ref mvidStringHandle, ref mvidStringFixup); SerializeMethodBodyExceptionHandlerTable(encodedBody.ExceptionRegions, exceptionRegions); return encodedBody.Offset; diff --git a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs index b2d4a571f38ac..ab65cf637ea89 100644 --- a/src/Compilers/Core/Portable/PEWriter/PeWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/PeWriter.cs @@ -26,21 +26,70 @@ public PeWritingException(Exception inner) internal static class PeWriter { - public static bool WritePeToStream( + internal static bool WritePeToStream( + EmitContext context, + CommonMessageProvider messageProvider, + Func getPeStream, + Func getMetadataPeStreamOpt, + Func getPortablePdbStreamOpt, + PdbWriter nativePdbWriterOpt, + string pdbPathOpt, + bool metadataOnly, + bool isDeterministic, + CancellationToken cancellationToken) + { + if (!WritePeToStream( + context, + messageProvider, + getPeStream, + getPortablePdbStreamOpt, + nativePdbWriterOpt, + pdbPathOpt, + metadataOnly, + isDeterministic, + cancellationToken)) + { + return false; + } + + // produce the secondary output (ref assembly) if needed + if (getMetadataPeStreamOpt != null) + { + Debug.Assert(!metadataOnly); + + if (!WritePeToStream( + context, + messageProvider, + getMetadataPeStreamOpt, + getPortablePdbStreamOpt: null, + nativePdbWriterOpt: null, + pdbPathOpt: null, + metadataOnly: true, + isDeterministic: isDeterministic, + cancellationToken: cancellationToken)) + { + return false; + } + } + + return true; + } + + internal static bool WritePeToStream( EmitContext context, CommonMessageProvider messageProvider, Func getPeStream, Func getPortablePdbStreamOpt, PdbWriter nativePdbWriterOpt, string pdbPathOpt, - bool allowMissingMethodBodies, + bool metadataOnly, bool isDeterministic, CancellationToken cancellationToken) { // If PDB writer is given, we have to have PDB path. Debug.Assert(nativePdbWriterOpt == null || pdbPathOpt != null); - var mdWriter = FullMetadataWriter.Create(context, messageProvider, allowMissingMethodBodies, isDeterministic, getPortablePdbStreamOpt != null, cancellationToken); + var mdWriter = FullMetadataWriter.Create(context, messageProvider, metadataOnly, isDeterministic, getPortablePdbStreamOpt != null, cancellationToken); var properties = context.Module.SerializationProperties; diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs index 8e4b4e3c166ff..b871a1565f161 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexer.cs @@ -99,7 +99,7 @@ protected override void RecordAssemblyReference(IAssemblyReference assemblyRefer protected override void ProcessMethodBody(IMethodDefinition method) { - if (method.HasBody()) + if (method.HasBody() && !metadataWriter.MetadataOnly) { var body = method.GetBody(Context); @@ -119,10 +119,6 @@ protected override void ProcessMethodBody(IMethodDefinition method) } } } - else if (!metadataWriter.allowMissingMethodBodies) - { - throw ExceptionUtilities.Unreachable; - } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 04a4963d5f804..8e0ebfab4b46c 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -9,12 +9,14 @@ *REMOVED*static Microsoft.CodeAnalysis.Text.SourceText.From(byte[] buffer, int length, System.Text.Encoding encoding = null, Microsoft.CodeAnalysis.Text.SourceHashAlgorithm checksumAlgorithm = Microsoft.CodeAnalysis.Text.SourceHashAlgorithm.Sha1, bool throwIfBinaryDetected = false) -> Microsoft.CodeAnalysis.Text.SourceText Microsoft.CodeAnalysis.CommandLineArguments.DisplayVersion.get -> bool Microsoft.CodeAnalysis.CommandLineArguments.EmbeddedFiles.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.CommandLineArguments.OutputRefFilePath.get -> string Microsoft.CodeAnalysis.CommandLineArguments.SourceLink.get -> string Microsoft.CodeAnalysis.Compilation.CreateAnonymousTypeSymbol(System.Collections.Immutable.ImmutableArray memberTypes, System.Collections.Immutable.ImmutableArray memberNames, System.Collections.Immutable.ImmutableArray memberIsReadOnly = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray memberLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateErrorNamespaceSymbol(Microsoft.CodeAnalysis.INamespaceSymbol container, string name) -> Microsoft.CodeAnalysis.INamespaceSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(Microsoft.CodeAnalysis.INamedTypeSymbol underlyingType, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Compilation.CreateTupleTypeSymbol(System.Collections.Immutable.ImmutableArray elementTypes, System.Collections.Immutable.ImmutableArray elementNames = default(System.Collections.Immutable.ImmutableArray), System.Collections.Immutable.ImmutableArray elementLocations = default(System.Collections.Immutable.ImmutableArray)) -> Microsoft.CodeAnalysis.INamedTypeSymbol -Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult +Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream = null, System.IO.Stream xmlDocumentationStream = null, System.IO.Stream win32Resources = null, System.Collections.Generic.IEnumerable manifestResources = null, Microsoft.CodeAnalysis.Emit.EmitOptions options = null, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint = null, System.IO.Stream sourceLinkStream = null, System.Collections.Generic.IEnumerable embeddedTexts = null, System.IO.Stream metadataPeStream = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Emit.EmitResult +Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.IO.Stream sourceLinkStream, System.Collections.Generic.IEnumerable embeddedTexts, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.Emit(System.IO.Stream peStream, System.IO.Stream pdbStream, System.IO.Stream xmlDocumentationStream, System.IO.Stream win32Resources, System.Collections.Generic.IEnumerable manifestResources, Microsoft.CodeAnalysis.Emit.EmitOptions options, Microsoft.CodeAnalysis.IMethodSymbol debugEntryPoint, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Emit.EmitResult Microsoft.CodeAnalysis.Compilation.GetUnreferencedAssemblyIdentities(Microsoft.CodeAnalysis.Diagnostic diagnostic) -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.CompilationOptions.WithConcurrentBuild(bool concurrent) -> Microsoft.CodeAnalysis.CompilationOptions diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index 0ff31b101c10c..0b8d40df5a51d 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -96,6 +96,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim noStdLib As Boolean = False Dim utf8output As Boolean = False Dim outputFileName As String = Nothing + Dim outputRefFileName As String = Nothing + Dim metadataOnly As Boolean = False Dim outputDirectory As String = baseDirectory Dim documentationPath As String = Nothing Dim errorLogPath As String = Nothing @@ -455,6 +457,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End If Continue For + Case "refout" + Dim unquoted = RemoveQuotesAndSlashes(value) + If String.IsNullOrEmpty(unquoted) Then + AddDiagnostic(diagnostics, ERRID.ERR_ArgumentRequired, name, ":") + Else + outputRefFileName = ParseGenericPathToFile(unquoted, diagnostics, baseDirectory) + End If + Continue For + + Case "refonly", "refonly+" + If value IsNot Nothing Then + AddDiagnostic(diagnostics, ERRID.ERR_SwitchNeedsBool, "refonly") + End If + + metadataOnly = True + Continue For + + Case "t", "target" value = RemoveQuotesAndSlashes(value) outputKind = ParseTarget(name, value, diagnostics) @@ -1194,6 +1214,14 @@ lVbRuntimePlus: specificDiagnosticOptions(item.Key) = item.Value Next + If metadataOnly AndAlso outputRefFileName IsNot Nothing Then + AddDiagnostic(diagnostics, ERRID.ERR_NoRefOutWhenRefOnly) + End If + + If outputKind = OutputKind.NetModule AndAlso (metadataOnly OrElse outputRefFileName IsNot Nothing) Then + AddDiagnostic(diagnostics, ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly) + End If + If Not IsScriptRunner AndAlso Not hasSourceFiles AndAlso managedResources.IsEmpty() Then ' VB displays help when there is nothing specified on the command line If flattenedArgs.Any Then @@ -1344,7 +1372,7 @@ lVbRuntimePlus: reportSuppressedDiagnostics:=reportSuppressedDiagnostics) Dim emitOptions = New EmitOptions( - metadataOnly:=False, + metadataOnly:=metadataOnly, debugInformationFormat:=debugInformationFormat, pdbFilePath:=Nothing, ' to be determined later outputNameOverride:=Nothing, ' to be determined later @@ -1376,6 +1404,7 @@ lVbRuntimePlus: .Utf8Output = utf8output, .CompilationName = compilationName, .OutputFileName = outputFileName, + .OutputRefFilePath = outputRefFileName, .OutputDirectory = outputDirectory, .DocumentationPath = documentationPath, .ErrorLogPath = errorLogPath, @@ -1403,7 +1432,7 @@ lVbRuntimePlus: .ScriptArguments = scriptArgs.AsImmutableOrEmpty(), .TouchedFilesPath = touchedFilesPath, .OutputLevel = outputLevel, - .EmitPdb = emitPdb, + .EmitPdb = emitPdb AndAlso Not metadataOnly, .SourceLink = sourceLink, .DefaultCoreLibraryReference = defaultCoreLibraryReference, .PreferredUILang = preferredUILang, diff --git a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb index d7431601b8c8f..2902afdc37514 100644 --- a/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb +++ b/src/Compilers/VisualBasic/Portable/Compilation/VisualBasicCompilation.vb @@ -2270,8 +2270,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic diagnostics As DiagnosticBag, cancellationToken As CancellationToken) As Boolean - Debug.Assert(Not moduleBuilder.EmitOptions.EmitMetadataOnly) - ' Use a temporary bag so we don't have to refilter pre-existing diagnostics. Dim resourceDiagnostics = DiagnosticBag.GetInstance() diff --git a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb index 1d21dcb6380ec..b3e8649f35eb1 100644 --- a/src/Compilers/VisualBasic/Portable/Errors/Errors.vb +++ b/src/Compilers/VisualBasic/Portable/Errors/Errors.vb @@ -1725,6 +1725,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ERR_BadAssemblyName = 37283 ERR_Merge_conflict_marker_encountered = 37284 + ERR_NoRefOutWhenRefOnly = 37285 + ERR_NoNetModuleOutputWhenRefOutOrRefOnly = 37286 '// WARNINGS BEGIN HERE WRN_UseOfObsoleteSymbol2 = 40000 diff --git a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb index d0a6f6a7876b2..1bc0c8a864e7d 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb +++ b/src/Compilers/VisualBasic/Portable/VBResources.Designer.vb @@ -8070,6 +8070,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Cannot compile net modules when using /refout or /refonly.. + ''' + Friend ReadOnly Property ERR_NoNetModuleOutputWhenRefOutOrRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoNetModuleOutputWhenRefOutOrRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Member '{0}' cannot be initialized in an object initializer expression because it is not a field or property.. ''' @@ -8142,6 +8151,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property + ''' + ''' Looks up a localized string similar to Do not use refout when using refonly.. + ''' + Friend ReadOnly Property ERR_NoRefOutWhenRefOnly() As String + Get + Return ResourceManager.GetString("ERR_NoRefOutWhenRefOnly", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to unable to open response file '{0}'. ''' diff --git a/src/Compilers/VisualBasic/Portable/VBResources.resx b/src/Compilers/VisualBasic/Portable/VBResources.resx index 766722e4338af..edebd886374e4 100644 --- a/src/Compilers/VisualBasic/Portable/VBResources.resx +++ b/src/Compilers/VisualBasic/Portable/VBResources.resx @@ -5467,4 +5467,10 @@ Merge conflict marker encountered + + Do not use refout when using refonly. + + + Cannot compile net modules when using /refout or /refonly. + \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index 7d1d9b1232cc1..f53308ffe3109 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -2765,6 +2765,26 @@ print Goodbye, World" Assert.Equal("MyBinary.dll", parsedArgs.CompilationOptions.ModuleName) Assert.Equal("C:\My Folder", parsedArgs.OutputDirectory) + parsedArgs = DefaultParse({"/refout:", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("refout", ":").WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/refonly", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly:incorrect", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_SwitchNeedsBool).WithArguments("refonly").WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/target:module", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + + parsedArgs = DefaultParse({"/refonly", "/target:module", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify( + Diagnostic(ERRID.ERR_NoNetModuleOutputWhenRefOutOrRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/out:C:\""My Folder""\MyBinary.dll", "/t:library", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify( Diagnostic(ERRID.FTL_InputFileNameTooLong).WithArguments("C:""My Folder\MyBinary.dll").WithLocation(1, 1)) @@ -2901,6 +2921,12 @@ print Goodbye, World" parsedArgs = DefaultParse({"/OUT:", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("out", ":")) + parsedArgs = DefaultParse({"/REFOUT:", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_ArgumentRequired).WithArguments("refout", ":")) + + parsedArgs = DefaultParse({"/refout:ref.dll", "/refonly", "a.vb"}, baseDirectory) + parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_NoRefOutWhenRefOnly).WithLocation(1, 1)) + parsedArgs = DefaultParse({"/out+", "a.vb"}, baseDirectory) parsedArgs.Errors.Verify(Diagnostic(ERRID.WRN_BadSwitch).WithArguments("/out+")) ' TODO: Dev11 reports ERR_ArgumentRequired @@ -7973,6 +7999,175 @@ End Class Next End Sub + + Public Sub RefOut() + Dim dir = Temp.CreateDirectory() + dir.CreateDirectory("ref") + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText(" +Class C + ''' Main method + Public Shared Sub Main() + System.Console.Write(""Hello"") + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim vbc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.exe", "/refout:ref/a.dll", "/doc:doc.xml", "/deterministic", "a.vb"}) + + Dim exitCode = vbc.Run(outWriter) + Assert.Equal(0, exitCode) + + Dim exe = Path.Combine(dir.Path, "a.exe") + Assert.True(File.Exists(exe)) + + MetadataReaderUtils.VerifyPEMetadata(exe, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + Dim doc = Path.Combine(dir.Path, "doc.xml") + Assert.True(File.Exists(doc)) + + Dim content = File.ReadAllText(doc) + Dim expectedDoc = +" + + + +a + + + + + Main method + + +" + Assert.Equal(expectedDoc, content.Trim()) + + Dim output = ProcessUtilities.RunAndGetOutput(exe, startFolder:=dir.Path) + Assert.Equal("Hello", output.Trim()) + + Dim refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")) + Assert.True(File.Exists(refDll)) + + ' The types and members that are included needs further refinement. + ' ReferenceAssemblyAttribute is missing. + ' See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + output = ProcessUtilities.RunAndGetOutput(refDll, startFolder:=dir.ToString(), expectedRetCode:=-532462766) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + + + Public Sub RefOutWithError() + Dim dir = Temp.CreateDirectory() + dir.CreateDirectory("ref") + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText( +"Class C + Public Shared Sub Main() + Bad() + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim csc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.dll", "/refout:ref/a.dll", "/deterministic", "a.vb"}) + + Dim exitCode = csc.Run(outWriter) + Assert.Equal(1, exitCode) + + Dim vb = Path.Combine(dir.Path, "a.vb") + + Dim dll = Path.Combine(dir.Path, "a.dll") + Assert.False(File.Exists(dll)) + + Dim refDll = Path.Combine(dir.Path, Path.Combine("ref", "a.dll")) + Assert.False(File.Exists(refDll)) + + Assert.Equal( +$"{vb}(3) : error BC30451: 'Bad' is not declared. It may be inaccessible due to its protection level. + + Bad() + ~~~", +outWriter.ToString().Trim()) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + + + Public Sub RefOnly() + Dim dir = Temp.CreateDirectory() + + Dim src = dir.CreateFile("a.vb") + src.WriteAllText( +"Class C + ''' Main method + Public Shared Sub Main() + Bad() + End Sub +End Class") + + Dim outWriter = New StringWriter(CultureInfo.InvariantCulture) + Dim csc = New MockVisualBasicCompiler(Nothing, dir.Path, + {"/define:_MYTYPE=""Empty"" ", "/nologo", "/out:a.dll", "/refonly", "/debug", "/deterministic", "/doc:doc.xml", "a.vb"}) + + Dim exitCode = csc.Run(outWriter) + Assert.Equal(0, exitCode) + + Dim refDll = Path.Combine(dir.Path, "a.dll") + Assert.True(File.Exists(refDll)) + + ' The types and members that are included needs further refinement. + ' ReferenceAssemblyAttribute is missing. + ' See issue https://github.com/dotnet/roslyn/issues/17612 + MetadataReaderUtils.VerifyPEMetadata(refDll, + {"TypeDef:", "TypeDef:C"}, + {"MethodDef: Void Main()", "MethodDef: Void .ctor()"}, + {"CompilationRelaxationsAttribute", "RuntimeCompatibilityAttribute", "DebuggableAttribute", "STAThreadAttribute"} + ) + + Dim pdb = Path.Combine(dir.Path, "a.pdb") + Assert.False(File.Exists(pdb)) + + Dim doc = Path.Combine(dir.Path, "doc.xml") + Assert.True(File.Exists(doc)) + + Dim content = File.ReadAllText(doc) + Dim expectedDoc = +" + + + +a + + + + + Main method + + +" + Assert.Equal(expectedDoc, content.Trim()) + + ' Clean up temp files + CleanupAllGeneratedFiles(dir.Path) + End Sub + 'Output with known but different extension diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb index ff3349e10f719..775744fdbbce5 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/CompilationEmitTests.vb @@ -260,7 +260,7 @@ End Namespace ) - Dim emitResult As emitResult + Dim emitResult As EmitResult Dim mdOnlyImage As Byte() Using output = New MemoryStream() @@ -297,6 +297,208 @@ End Class End Using End Sub + + Public Sub RefAssembly_InvariantToSomeChanges() + + RefAssembly_InvariantToSomeChanges( +"Public Function M() As Integer + Return 1 +End Function", +"Public Function M() As Integer + Return 2 +End Function", True) + + RefAssembly_InvariantToSomeChanges( +"Public Function M() As Integer + Return 1 +End Function", +"Public Function M() As Integer +End Function", True) + + RefAssembly_InvariantToSomeChanges( +"", +"Private Sub M() +End Sub", False) ' Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + + RefAssembly_InvariantToSomeChanges( +"Friend Sub M() +End Sub", +"", False) + + RefAssembly_InvariantToSomeChanges( +"Public Structure S + Private Dim i As Integer +End Structure", +"Public Structure S +End Structure", False) + + End Sub + + Private Sub RefAssembly_InvariantToSomeChanges(change1 As String, change2 As String, expectMatch As Boolean) + Dim sourceTemplate As String = " +Public Class C + CHANGE +End Class" + + Dim name As String = GetUniqueName() + Dim source1 As String = sourceTemplate.Replace("CHANGE", change1) + Dim comp1 = CreateCompilationWithMscorlib(source1, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + Dim image1 As ImmutableArray(Of Byte) = comp1.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + + Dim source2 = sourceTemplate.Replace("CHANGE", change2) + Dim comp2 = CreateCompilationWithMscorlib(source2, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + Dim image2 As ImmutableArray(Of Byte) = comp2.EmitToArray(EmitOptions.Default.WithEmitMetadataOnly(True)) + + If expectMatch Then + AssertEx.Equal(image1, image2) + Else + AssertEx.NotEqual(image1, image2) + End If + End Sub + + + Public Sub RefAssembly_IgnoresSomeDiagnostics() + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Integer +End Function", True) + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Integer + Error( +End Function", False) ' Should be true. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + + RefAssembly_IgnoresSomeDiagnostics( +"Public Function M() As Error +End Function", False) ' This may get relaxed. See follow-up issue https://github.com/dotnet/roslyn/issues/17612 + End Sub + + Private Sub RefAssembly_IgnoresSomeDiagnostics(change As String, expectSuccess As Boolean) + Dim sourceTemplate As String = " +Public Class C + CHANGE +End Class" + + Dim name As String = GetUniqueName() + Dim source As String = sourceTemplate.Replace("CHANGE", change) + Dim comp = CreateCompilationWithMscorlib(source, + options:=TestOptions.DebugDll.WithDeterministic(True), assemblyName:=name) + + Using output As New MemoryStream() + Dim EmitResult = comp.Emit(output, options:=EmitOptions.Default.WithEmitMetadataOnly(True)) + Assert.Equal(expectSuccess, EmitResult.Success) + Assert.Equal(Not expectSuccess, EmitResult.Diagnostics.Any()) + End Using + + End Sub + + + Public Sub VerifyRefAssembly() + Dim source = " +Public MustInherit Class PublicClass + Public Sub PublicMethod() + System.Console.Write(""Hello"") + End Sub + Private Sub PrivateMethod() + System.Console.Write(""Hello"") + End Sub + Protected Sub ProtectedMethod() + System.Console.Write(""Hello"") + End Sub + Friend Sub InternalMethod() + System.Console.Write(""Hello"") + End Sub + Public MustOverride Sub AbstractMethod() +End Class" + Dim comp As Compilation = CreateCompilation(source, references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Dim emitRefOnly = EmitOptions.Default.WithEmitMetadataOnly(True) + + Dim verifier = CompileAndVerify(comp, emitOptions:=emitRefOnly, verify:=True) + + ' verify metadata (types, members, attributes) + Dim image = comp.EmitToImageReference(emitRefOnly) + Dim comp2 = CreateCompilation("", references:={MscorlibRef, image}, + options:=TestOptions.DebugDll.WithMetadataImportOptions(MetadataImportOptions.All)) + Dim assembly As AssemblySymbol = comp2.SourceModule.GetReferencedAssemblySymbols().Last() + Dim members = assembly.GlobalNamespace.GetMembers() + AssertEx.SetEqual(members.Select(Function(m) m.ToDisplayString()), + {"", "PublicClass"}) + + AssertEx.SetEqual( + DirectCast(assembly.GlobalNamespace.GetMember("PublicClass"), NamedTypeSymbol).GetMembers(). + Select(Function(m) m.ToTestDisplayString()), + {"Sub PublicClass.PublicMethod()", "Sub PublicClass.PrivateMethod()", + "Sub PublicClass.InternalMethod()", "Sub PublicClass.ProtectedMethod()", + "Sub PublicClass.AbstractMethod()", "Sub PublicClass..ctor()"}) + + AssertEx.SetEqual(assembly.GetAttributes().Select(Function(a) a.AttributeClass.ToTestDisplayString()), + {"System.Runtime.CompilerServices.CompilationRelaxationsAttribute", + "System.Runtime.CompilerServices.RuntimeCompatibilityAttribute", + "System.Diagnostics.DebuggableAttribute"}) + + Dim peImage = comp.EmitToArray(emitRefOnly) + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=True) + End Sub + + + Public Sub EmitMetadataOnly_DisallowPdbs() + Dim comp = CreateCompilation("", references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using pdbOutput = New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, pdbOutput, + options:=EmitOptions.Default.WithEmitMetadataOnly(True))) + End Using + End Using + End Sub + + + Public Sub EmitMetadataOnly_DisallowMetadataPeStream() + Dim comp = CreateCompilation("", references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using metadataPeOutput As New MemoryStream() + Assert.Throws(Of ArgumentException)(Function() comp.Emit(output, metadataPeStream:=metadataPeOutput, + options:=EmitOptions.Default.WithEmitMetadataOnly(True))) + End Using + End Using + End Sub + + + Public Sub EmitMetadata() + Dim source = +"Public MustInherit Class PublicClass + Public Sub PublicMethod + System.Console.Write(""Hello"") + End Sub +End Class " + Dim comp = CreateCompilation(source, references:={MscorlibRef}, + options:=TestOptions.DebugDll.WithDeterministic(True)) + + Using output As New MemoryStream() + Using pdbOutput As New MemoryStream() + Using metadataOutput As New MemoryStream() + Dim result = comp.Emit(output, pdbOutput, metadataPeStream:=metadataOutput) + Assert.True(result.Success) + Assert.NotEqual(0, output.Position) + Assert.NotEqual(0, pdbOutput.Position) + Assert.NotEqual(0, metadataOutput.Position) + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(output.GetBuffer()), expectEmptyOrThrowNull:=False) + MetadataReaderUtils.VerifyMethodBodies(ImmutableArray.CreateRange(metadataOutput.GetBuffer()), expectEmptyOrThrowNull:=True) + End Using + End Using + End Using + + Dim peImage = comp.EmitToArray() + MetadataReaderUtils.VerifyMethodBodies(peImage, expectEmptyOrThrowNull:=False) + End Sub + Public Sub Bug4344() @@ -573,21 +775,21 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=Nothing) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly, peHeaders.CorHeader.Flags) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X86)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly Or CorFlags.Requires32Bit, peHeaders.CorHeader.Flags) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CorFlags.ILOnly, peHeaders.CorHeader.Flags) Assert.True(peHeaders.Requires64Bits) Assert.True(peHeaders.RequiresAmdInstructionSet) compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu32BitPreferred)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.False(peHeaders.Requires64Bits) Assert.False(peHeaders.RequiresAmdInstructionSet) Assert.Equal(CorFlags.ILOnly Or CorFlags.Requires32Bit Or CorFlags.Prefers32Bit, peHeaders.CorHeader.Flags) @@ -606,7 +808,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.AnyCpu)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -631,7 +833,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -667,7 +869,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.Arm)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -692,7 +894,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.AnyCpu)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.False(peHeaders.Requires64Bits) @@ -728,7 +930,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary).WithPlatform(Platform.X64)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.True(peHeaders.Requires64Bits) @@ -748,7 +950,7 @@ End Module ' test an exe as well: compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.ConsoleApplication).WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) 'interesting COFF bits Assert.True(peHeaders.Requires64Bits) @@ -804,7 +1006,7 @@ End Module Dim compilation = CreateCompilationWithMscorlib(source, options:=New VisualBasicCompilationOptions(OutputKind.WindowsRuntimeApplication)) - Dim peHeaders = New peHeaders(compilation.EmitToStream()) + Dim peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(CType(&H9540, UShort), peHeaders.PEHeader.DllCharacteristics) 'DYNAMIC_BASE | NX_COMPAT | NO_SEH | TERMINAL_SERVER_AWARE | HIGH_ENTROPY_VA (0x20) End Sub @@ -837,15 +1039,15 @@ End Module ' default for 32 bit compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseExe.WithPlatform(Platform.X86)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H400000UL, peHeaders.PEHeader.ImageBase) compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseExe.WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H140000000UL, peHeaders.PEHeader.ImageBase) compilation = CreateCompilationWithMscorlib(source, options:=TestOptions.ReleaseDll.WithPlatform(Platform.X64)) - peHeaders = New peHeaders(compilation.EmitToStream()) + peHeaders = New PEHeaders(compilation.EmitToStream()) Assert.Equal(&H180000000UL, peHeaders.PEHeader.ImageBase) End Sub @@ -867,7 +1069,7 @@ End Module Assert.Equal(1024, peHeaders.PEHeader.FileAlignment) compilation = CreateCompilationWithMscorlib(source) - peHeaders = New peHeaders(compilation.EmitToStream(options:=New EmitOptions(fileAlignment:=4096))) + peHeaders = New PEHeaders(compilation.EmitToStream(options:=New EmitOptions(fileAlignment:=4096))) Assert.Equal(4096, peHeaders.PEHeader.FileAlignment) End Sub @@ -1743,7 +1945,7 @@ end namespace Dim sourceAssembly = DirectCast(assembly, SourceAssemblySymbol) Dim compilation = sourceAssembly.DeclaringCompilation - Dim emitOptions = New emitOptions(outputNameOverride:=sourceAssembly.Name) + Dim emitOptions = New EmitOptions(outputNameOverride:=sourceAssembly.Name) Dim assemblyBuilder = New PEAssemblyBuilder(sourceAssembly, emitOptions, OutputKind.DynamicallyLinkedLibrary, GetDefaultModulePropertiesForSerialization(), SpecializedCollections.EmptyEnumerable(Of ResourceDescription), Nothing) Dim assemblySecurityAttributes As IEnumerable(Of Cci.SecurityAttribute) = assemblyBuilder.GetSourceAssemblySecurityAttributes() diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs index cda9d235db5cc..cc1ba417828aa 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/EvaluationContext.cs @@ -263,7 +263,7 @@ internal override CompileResult CompileExpression( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); @@ -352,7 +352,7 @@ internal override CompileResult CompileAssignment( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); @@ -396,7 +396,7 @@ internal override ReadOnlyCollection CompileGetLocals( getPortablePdbStreamOpt: null, nativePdbWriterOpt: null, pdbPathOpt: null, - allowMissingMethodBodies: false, + metadataOnly: false, isDeterministic: false, cancellationToken: default(CancellationToken)); diff --git a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs index 2d4096440e450..64257ff58dd09 100644 --- a/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs +++ b/src/ExpressionEvaluator/Core/Test/ExpressionCompiler/ExpressionCompilerTestHelpers.cs @@ -789,7 +789,7 @@ internal static void EmitCorLibWithAssemblyReferences( () => peStream, () => pdbStream, null, null, - allowMissingMethodBodies: true, + metadataOnly: true, isDeterministic: false, cancellationToken: default(CancellationToken)); diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb index 619b89fb23c8b..2dc724c6a37fd 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb +++ b/src/ExpressionEvaluator/VisualBasic/Source/ExpressionCompiler/EvaluationContext.vb @@ -398,7 +398,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) @@ -442,7 +442,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) @@ -486,7 +486,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator getPortablePdbStreamOpt:=Nothing, nativePdbWriterOpt:=Nothing, pdbPathOpt:=Nothing, - allowMissingMethodBodies:=False, + metadataOnly:=False, isDeterministic:=False, cancellationToken:=Nothing) diff --git a/src/Test/Utilities/Portable/Assert/AssertEx.cs b/src/Test/Utilities/Portable/Assert/AssertEx.cs index 55f36d0a7b1bd..b52bb6578990c 100644 --- a/src/Test/Utilities/Portable/Assert/AssertEx.cs +++ b/src/Test/Utilities/Portable/Assert/AssertEx.cs @@ -210,6 +210,24 @@ public static void Equal(IEnumerable expected, IEnumerable actual, IEqu } } + public static void NotEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null, string message = null, + string itemSeparator = null, Func itemInspector = null) + { + if (ReferenceEquals(expected, actual)) + { + Fail("expected and actual references are identical\r\n" + message); + } + + if (expected == null || actual == null) + { + return; + } + else if (SequenceEqual(expected, actual, comparer)) + { + Fail("expected and actual sequences match\r\n" + message); + } + } + private static bool SequenceEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer = null) { var enumerator1 = expected.GetEnumerator(); diff --git a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs index acb32e390b463..f3bcd50536fbf 100644 --- a/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs +++ b/src/Test/Utilities/Portable/Compilation/CompilationExtensions.cs @@ -45,6 +45,7 @@ internal static ImmutableArray EmitToArray( var emitResult = compilation.Emit( peStream: peStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs index bee66b9519f56..b767804c589bb 100644 --- a/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs +++ b/src/Test/Utilities/Portable/Compilation/IRuntimeEnvironment.cs @@ -235,6 +235,7 @@ EmitOptions emitOptions { result = compilation.Emit( executableStream, + metadataPeStream: null, pdbStream: pdbStream, xmlDocumentationStream: null, win32Resources: null, diff --git a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs index 025cf50f99b6d..5e1f8db49ddf5 100644 --- a/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs +++ b/src/Test/Utilities/Portable/Metadata/MetadataReaderUtils.cs @@ -265,9 +265,147 @@ public static string Dump(this MetadataReader reader, EntityHandle handle) { case HandleKind.AssemblyReference: return "AssemblyRef:" + reader.GetString(reader.GetAssemblyReference((AssemblyReferenceHandle)handle).Name); + case HandleKind.TypeDefinition: + return "TypeDef:" + reader.GetString(reader.GetTypeDefinition((TypeDefinitionHandle)handle).Name); + case HandleKind.MethodDefinition: + var method = reader.GetMethodDefinition((MethodDefinitionHandle)handle); + var blob = reader.GetBlobReader(method.Signature); + var decoder = new SignatureDecoder(ConstantSignatureVisualizer.Instance, reader, genericContext: null); + var signature = decoder.DecodeMethodSignature(ref blob); + var parameters = signature.ParameterTypes.Join(", "); + return $"MethodDef: {signature.ReturnType} {reader.GetString(method.Name)}({parameters})"; default: return handle.Kind.ToString(); } } + + private sealed class ConstantSignatureVisualizer : ISignatureTypeProvider + { + public static readonly ConstantSignatureVisualizer Instance = new ConstantSignatureVisualizer(); + + public string GetArrayType(string elementType, ArrayShape shape) + { + return elementType + "[" + new string(',', shape.Rank) + "]"; + } + + public string GetByReferenceType(string elementType) + { + return elementType + "&"; + } + + public string GetFunctionPointerType(MethodSignature signature) + { + return "method-ptr"; + } + + public string GetGenericInstantiation(string genericType, ImmutableArray typeArguments) + { + return genericType + "{" + string.Join(", ", typeArguments) + "}"; + } + + public string GetGenericMethodParameter(object genericContext, int index) + { + return "!!" + index; + } + + public string GetGenericTypeParameter(object genericContext, int index) + { + return "!" + index; + } + + public string GetModifiedType(string modifier, string unmodifiedType, bool isRequired) + { + return (isRequired ? "modreq" : "modopt") + "(" + modifier + ") " + unmodifiedType; + } + + public string GetPinnedType(string elementType) + { + return "pinned " + elementType; + } + + public string GetPointerType(string elementType) + { + return elementType + "*"; + } + + public string GetPrimitiveType(PrimitiveTypeCode typeCode) + { + return typeCode.ToString(); + } + + public string GetSZArrayType(string elementType) + { + return elementType + "[]"; + } + + public string GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) + { + var typeDef = reader.GetTypeDefinition(handle); + var name = reader.GetString(typeDef.Name); + return typeDef.Namespace.IsNil ? name : reader.GetString(typeDef.Namespace) + "." + name; + } + + public string GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) + { + var typeRef = reader.GetTypeReference(handle); + var name = reader.GetString(typeRef.Name); + return typeRef.Namespace.IsNil ? name : reader.GetString(typeRef.Namespace) + "." + name; + } + + public string GetTypeFromSpecification(MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) + { + var sigReader = reader.GetBlobReader(reader.GetTypeSpecification(handle).Signature); + return new SignatureDecoder(Instance, reader, genericContext).DecodeType(ref sigReader); + } + } + + internal static void VerifyPEMetadata(string pePath, string[] types, string[] methods, string[] attributes) + { + using (var peStream = File.OpenRead(pePath)) + using (var refPeReader = new PEReader(peStream)) + { + var metadataReader = refPeReader.GetMetadataReader(); + + AssertEx.SetEqual(metadataReader.TypeDefinitions.Select(t => metadataReader.Dump(t)), types); + AssertEx.SetEqual(metadataReader.MethodDefinitions.Select(t => metadataReader.Dump(t)), methods); + + AssertEx.SetEqual( + metadataReader.CustomAttributes.Select(a => metadataReader.GetCustomAttribute(a).Constructor) + .Select(c => metadataReader.GetMemberReference((MemberReferenceHandle)c).Parent) + .Select(p => metadataReader.GetTypeReference((TypeReferenceHandle)p).Name) + .Select(n => metadataReader.GetString(n)), + attributes); + } + } + + /// + /// Asserts that all the method bodies in the PE are empty or `throw null`, if expectEmptyOrThrowNull is true. + /// Asserts that none of the methods bodies are empty or `throw null`, if expectEmptyOrThrowNull is false. + /// + internal static void VerifyMethodBodies(ImmutableArray peImage, bool expectEmptyOrThrowNull) + { + using (var peReader = new PEReader(peImage)) + { + var metadataReader = peReader.GetMetadataReader(); + foreach (var method in metadataReader.MethodDefinitions) + { + var rva = metadataReader.GetMethodDefinition(method).RelativeVirtualAddress; + if (rva != 0) + { + var il = peReader.GetMethodBody(rva).GetILBytes(); + var throwNull = new[] { (byte)ILOpCode.Ldnull, (byte)ILOpCode.Throw }; + + if (expectEmptyOrThrowNull) + { + AssertEx.Equal(throwNull, il); + } + else + { + AssertEx.NotEqual(throwNull, il); + } + } + } + } + } } }