From dfe146dc692ab2f30d575f12246528334f64587c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Tue, 19 May 2020 07:55:16 -0700 Subject: [PATCH] Add IAssemblySymbol.GetForwardedTypes API. (#44307) Closes #36274 --- .../Portable/Emitter/Model/PEModuleBuilder.cs | 75 +++++++++++------- .../CSharp/Portable/Symbols/AssemblySymbol.cs | 2 + .../Symbols/Metadata/PE/PEAssemblySymbol.cs | 5 ++ .../Portable/Symbols/MissingAssemblySymbol.cs | 5 ++ .../Symbols/PublicModel/AssemblySymbol.cs | 6 ++ .../Retargeting/RetargetingAssemblySymbol.cs | 8 ++ .../Symbols/Source/SourceAssemblySymbol.cs | 5 ++ .../Test/Emit/Emit/DeterministicTests.cs | 40 +++++++++- .../Symbols/Metadata/PE/TypeForwarders.cs | 25 +++++- .../Test/Symbol/Symbols/MockAssemblySymbol.cs | 5 ++ .../Core/Portable/PublicAPI.Unshipped.txt | 1 + .../Core/Portable/Symbols/IAssemblySymbol.cs | 6 ++ .../Test/Utilities/VisualBasic/MockSymbols.vb | 4 + .../Portable/Emit/PEModuleBuilder.vb | 76 +++++++++++-------- .../Portable/Symbols/AssemblySymbol.vb | 6 ++ .../Symbols/Metadata/PE/PEAssemblySymbol.vb | 4 + .../Portable/Symbols/MissingAssemblySymbol.vb | 4 + .../Retargeting/RetargetingAssemblySymbol.vb | 6 ++ .../Symbols/Source/SourceAssemblySymbol.vb | 4 + .../Test/Emit/Emit/DeterministicTests.vb | 46 +++++++++-- .../Metadata/PE/MissingTypeReferences.vb | 2 + .../Metadata/PE/TypeForwarders.vb | 13 ++++ 22 files changed, 281 insertions(+), 67 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index f20cdb0f6af..539389a1902 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -492,16 +492,29 @@ private ImmutableArray CalculateExportedTypes() } } + Debug.Assert(OutputKind.IsNetModule() == sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule()); + GetForwardedTypes(sourceAssembly, builder); + + return builder.ToImmutableAndFree(); + } + +#nullable enable + /// + /// Returns a set of top-level forwarded types + /// + internal static HashSet GetForwardedTypes(SourceAssemblySymbol sourceAssembly, ArrayBuilder? builder) + { var seenTopLevelForwardedTypes = new HashSet(); GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builder); - if (!OutputKind.IsNetModule()) + if (!sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule()) { GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builder); } - return builder.ToImmutableAndFree(); + return seenTopLevelForwardedTypes; } +#nullable restore private void ReportExportedTypeNameCollisions(ImmutableArray exportedTypes, DiagnosticBag diagnostics) { @@ -568,10 +581,11 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e } } +#nullable enable private static void GetForwardedTypes( HashSet seenTopLevelTypes, CommonAssemblyWellKnownAttributeData wellKnownAttributeData, - ArrayBuilder builder) + ArrayBuilder? builder) { if (wellKnownAttributeData?.ForwardedTypes?.Count > 0) { @@ -579,7 +593,12 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e var stack = ArrayBuilder<(NamedTypeSymbol type, int parentIndex)>.GetInstance(); // Hashset enumeration is not guaranteed to be deterministic. Emitting in the order of fully qualified names. - var orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes.OrderBy(t => t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)); + IEnumerable orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes; + + if (builder is object) + { + orderedForwardedTypes = orderedForwardedTypes.OrderBy(t => t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)); + } foreach (NamedTypeSymbol forwardedType in orderedForwardedTypes) { @@ -590,34 +609,37 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e // level, we need to de-dup the original definitions before emitting. if (!seenTopLevelTypes.Add(originalDefinition)) continue; - // Return all nested types. - // Note the order: depth first, children in reverse order (to match dev10, not a requirement). - Debug.Assert(stack.Count == 0); - stack.Push((originalDefinition, -1)); - - while (stack.Count > 0) + if (builder is object) { - var (type, parentIndex) = stack.Pop(); + // Return all nested types. + // Note the order: depth first, children in reverse order (to match dev10, not a requirement). + Debug.Assert(stack.Count == 0); + stack.Push((originalDefinition, -1)); - // In general, we don't want private types to appear in the ExportedTypes table. - // BREAK: dev11 emits these types. The problem was discovered in dev10, but failed - // to meet the bar Bug: Dev10/258038 and was left as-is. - if (type.DeclaredAccessibility == Accessibility.Private) + while (stack.Count > 0) { - // NOTE: this will also exclude nested types of type - continue; - } + var (type, parentIndex) = stack.Pop(); + + // In general, we don't want private types to appear in the ExportedTypes table. + // BREAK: dev11 emits these types. The problem was discovered in dev10, but failed + // to meet the bar Bug: Dev10/258038 and was left as-is. + if (type.DeclaredAccessibility == Accessibility.Private) + { + // NOTE: this will also exclude nested types of type + continue; + } - // NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. + // NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. - int index = builder.Count; - builder.Add(new Cci.ExportedType(type, parentIndex, isForwarder: true)); + int index = builder.Count; + builder.Add(new Cci.ExportedType(type, parentIndex, isForwarder: true)); - // Iterate backwards so they get popped in forward order. - ImmutableArray nested = type.GetTypeMembers(); // Ordered. - for (int i = nested.Length - 1; i >= 0; i--) - { - stack.Push((nested[i], index)); + // Iterate backwards so they get popped in forward order. + ImmutableArray nested = type.GetTypeMembers(); // Ordered. + for (int i = nested.Length - 1; i >= 0; i--) + { + stack.Push((nested[i], index)); + } } } } @@ -625,6 +647,7 @@ private void ReportExportedTypeNameCollisions(ImmutableArray e stack.Free(); } } +#nullable restore internal IEnumerable GetReferencedAssembliesUsedSoFar() { diff --git a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs index f0b18195a98..d713a56f30a 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs @@ -382,6 +382,8 @@ internal ErrorTypeSymbol CreateMultipleForwardingErrorTypeSymbol(ref MetadataTyp return new MissingMetadataTypeSymbol.TopLevel(forwardingModule, ref emittedName, diagnosticInfo); } + internal abstract IEnumerable GetAllTopLevelForwardedTypes(); + /// /// Lookup declaration for predefined CorLib type in this Assembly. /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs index 1f9ef8ebbe6..39996c81d0f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEAssemblySymbol.cs @@ -150,6 +150,11 @@ public override ImmutableArray GetAttributes() return this.PrimaryModule.GetAssembliesForForwardedType(ref emittedName); } + internal override IEnumerable GetAllTopLevelForwardedTypes() + { + return this.PrimaryModule.GetForwardedTypes(); + } + internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetection(ref MetadataTypeName emittedName, ConsList visitedAssemblies) { // Check if it is a forwarded type. diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs index e6a63bd7461..c31e30857fe 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingAssemblySymbol.cs @@ -190,5 +190,10 @@ public override bool MightContainExtensionMethods } public override AssemblyMetadata GetMetadata() => null; + + internal sealed override IEnumerable GetAllTopLevelForwardedTypes() + { + return SpecializedCollections.EmptyEnumerable(); + } } } diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/AssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/AssemblySymbol.cs index 24d991b8ea8..8ad1c08d407 100644 --- a/src/Compilers/CSharp/Portable/Symbols/PublicModel/AssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/AssemblySymbol.cs @@ -55,6 +55,12 @@ INamedTypeSymbol IAssemblySymbol.ResolveForwardedType(string fullyQualifiedMetad return UnderlyingAssemblySymbol.ResolveForwardedType(fullyQualifiedMetadataName).GetPublicSymbol(); } + ImmutableArray IAssemblySymbol.GetForwardedTypes() + { + return UnderlyingAssemblySymbol.GetAllTopLevelForwardedTypes().Select(t => t.GetPublicSymbol()). + OrderBy(t => t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)).AsImmutable(); + } + bool IAssemblySymbol.GivesAccessTo(IAssemblySymbol assemblyWantingAccess) { if (Equals(this, assemblyWantingAccess)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs index c382918a6fe..b07849eceb7 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.cs @@ -289,6 +289,14 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti return this.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName); } + internal override IEnumerable GetAllTopLevelForwardedTypes() + { + foreach (NamedTypeSymbol underlying in _underlyingAssembly.GetAllTopLevelForwardedTypes()) + { + yield return this.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName); + } + } + public override AssemblyMetadata GetMetadata() => _underlyingAssembly.GetMetadata(); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs index 15017289ff6..f986826852c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceAssemblySymbol.cs @@ -2665,6 +2665,11 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti return null; } + internal override IEnumerable GetAllTopLevelForwardedTypes() + { + return PEModuleBuilder.GetForwardedTypes(this, builder: null); + } + public override AssemblyMetadata GetMetadata() => null; protected override ISymbol CreateISymbol() diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs index 39d8971f615..affec9427a8 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DeterministicTests.cs @@ -14,6 +14,8 @@ using System.Reflection.PortableExecutable; using Roslyn.Test.Utilities; using Xunit; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit { @@ -290,8 +292,8 @@ public class GenericType {} public class GenericType {} } "; - var forwardedToCompilation = CreateEmptyCompilation(forwardedToCode); - var forwardedToReference = new CSharpCompilationReference(forwardedToCompilation); + var forwardedToCompilation1 = CreateCompilation(forwardedToCode, assemblyName: "ForwardedTo"); + var forwardedToReference1 = new CSharpCompilationReference(forwardedToCompilation1); var forwardingCode = @" using System.Runtime.CompilerServices; @@ -308,7 +310,8 @@ public class GenericType {} [assembly: TypeForwardedTo(typeof(Namespace3.GenericType))] "; - var forwardingCompilation = CreateCompilation(forwardingCode, new MetadataReference[] { forwardedToReference }); + var forwardingCompilation = CreateCompilation(forwardingCode, new MetadataReference[] { forwardedToReference1 }); + var forwardingReference = new CSharpCompilationReference(forwardingCompilation); var sortedFullNames = new string[] { @@ -325,6 +328,14 @@ public class GenericType {} "Namespace4.Embedded.Type2" }; + Action metadataValidator = module => + { + var assembly = module.ContainingAssembly; + Assert.Equal(sortedFullNames, getNamesOfForwardedTypes(assembly)); + }; + + CompileAndVerify(forwardingCompilation, symbolValidator: metadataValidator, sourceSymbolValidator: metadataValidator, verify: Verification.Skipped); + using (var stream = forwardingCompilation.EmitToStream()) { using (var block = ModuleMetadata.CreateFromStream(stream)) @@ -333,6 +344,29 @@ public class GenericType {} Assert.Equal(sortedFullNames, metadataFullNames); } } + + var forwardedToCompilation2 = CreateCompilation(forwardedToCode, assemblyName: "ForwardedTo"); + var forwardedToReference2 = new CSharpCompilationReference(forwardedToCompilation2); + + var withRetargeting = CreateCompilation("", new MetadataReference[] { forwardedToReference2, forwardingReference }); + + var retargeting = (RetargetingAssemblySymbol)withRetargeting.GetReferencedAssemblySymbol(forwardingReference); + Assert.Equal(sortedFullNames, getNamesOfForwardedTypes(retargeting)); + + foreach (var type in getForwardedTypes(retargeting)) + { + Assert.Same(forwardedToCompilation2.Assembly.GetPublicSymbol(), type.ContainingAssembly); + } + + static IEnumerable getNamesOfForwardedTypes(AssemblySymbol assembly) + { + return getForwardedTypes(assembly).Select(t => t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)); + } + + static ImmutableArray getForwardedTypes(AssemblySymbol assembly) + { + return assembly.GetPublicSymbol().GetForwardedTypes(); + } } [ConditionalFact(typeof(ClrOnly), Reason = "Static execution is runtime defined and this tests Clr behavior only")] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs index 8d9114c0946..5c7a2d9c37f 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Metadata/PE/TypeForwarders.cs @@ -753,6 +753,8 @@ static void Main() // (2,7): error CS0012: The type 'Base' is defined in an assembly that is not referenced. You must add a reference to assembly 'pe3, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // class Test : Derived Diagnostic(ErrorCode.ERR_NoTypeDef, "Derived").WithArguments("Base", "pe3, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")); + + Assert.Empty(comp3.GetReferencedAssemblySymbol(ref2).Modules[0].ReferencedAssemblySymbols.OfType().First().GetPublicSymbol().GetForwardedTypes()); } [Fact] @@ -1459,7 +1461,10 @@ private void CheckForwarderEmit(string source1, string source2, params string[] var assembly = module.ContainingAssembly; // Attributes should not actually be emitted. - Assert.Equal(0, assembly.GetAttributes(AttributeDescription.TypeForwardedToAttribute).Count()); + if (module is PEModuleSymbol) + { + Assert.Equal(0, assembly.GetAttributes(AttributeDescription.TypeForwardedToAttribute).Count()); + } var topLevelTypes = new HashSet(); @@ -1484,9 +1489,11 @@ private void CheckForwarderEmit(string source1, string source2, params string[] Assert.NotEqual(TypeKind.Error, type.TypeKind); Assert.Equal("Asm1", type.ContainingAssembly.Name); } + + Assert.Equal(topLevelTypes.OrderBy(s => s), GetNamesOfForwardedTypes(assembly)); }; - var verifier2 = CompileAndVerify(comp2, symbolValidator: metadataValidator); + var verifier2 = CompileAndVerify(comp2, symbolValidator: metadataValidator, sourceSymbolValidator: metadataValidator); using (ModuleMetadata metadata = ModuleMetadata.CreateFromImage(verifier2.EmittedAssemblyData)) { @@ -1503,6 +1510,16 @@ private void CheckForwarderEmit(string source1, string source2, params string[] } } + private static IEnumerable GetNamesOfForwardedTypes(AssemblySymbol assembly) + { + return GetNamesOfForwardedTypes(assembly.GetPublicSymbol()); + } + + private static IEnumerable GetNamesOfForwardedTypes(IAssemblySymbol assembly) + { + return assembly.GetForwardedTypes().Select(t => t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)); + } + [WorkItem(545911, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545911")] [ConditionalFact(typeof(DesktopOnly), typeof(ClrOnly))] public void EmitForwarder_ModuleInReferencedAssembly() @@ -1592,6 +1609,8 @@ public class CF1 var modCompilation = CreateCompilation(mod, references: new[] { new CSharpCompilationReference(forwardedTypesCompilation) }, options: TestOptions.ReleaseModule); var modRef1 = modCompilation.EmitToImageReference(); + Assert.Equal(new[] { "CF1" }, GetNamesOfForwardedTypes(modCompilation.Assembly)); + string app = @" public class Test { } @@ -1599,6 +1618,8 @@ public class Test { } var appCompilation = CreateCompilation(app, references: new[] { modRef1, new CSharpCompilationReference(forwardedTypesCompilation) }, options: TestOptions.ReleaseDll); + Assert.Equal(new[] { "CF1" }, GetNamesOfForwardedTypes(appCompilation.Assembly)); + var module = (PEModuleSymbol)appCompilation.Assembly.Modules[1]; var metadata = module.Module; diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs index 541d0343ddd..a4b48ac861e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/MockAssemblySymbol.cs @@ -110,5 +110,10 @@ internal override NamedTypeSymbol TryLookupForwardedMetadataTypeWithCycleDetecti } public override AssemblyMetadata GetMetadata() => null; + + internal override IEnumerable GetAllTopLevelForwardedTypes() + { + throw new NotImplementedException(); + } } } diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 30bc51c9506..43944cd0a1d 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,6 +1,7 @@ Microsoft.CodeAnalysis.Compilation.CreateNativeIntegerTypeSymbol(bool signed) -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.AssemblyLoader.get -> Microsoft.CodeAnalysis.IAnalyzerAssemblyLoader Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference.Equals(Microsoft.CodeAnalysis.Diagnostics.AnalyzerFileReference other) -> bool +Microsoft.CodeAnalysis.IAssemblySymbol.GetForwardedTypes() -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.INamedTypeSymbol.NativeIntegerUnderlyingType.get -> Microsoft.CodeAnalysis.INamedTypeSymbol Microsoft.CodeAnalysis.ITypeSymbol.IsNativeIntegerType.get -> bool Microsoft.CodeAnalysis.OperationKind.BinaryPattern = 110 -> Microsoft.CodeAnalysis.OperationKind diff --git a/src/Compilers/Core/Portable/Symbols/IAssemblySymbol.cs b/src/Compilers/Core/Portable/Symbols/IAssemblySymbol.cs index 15adae6fa1c..1e929a3bf53 100644 --- a/src/Compilers/Core/Portable/Symbols/IAssemblySymbol.cs +++ b/src/Compilers/Core/Portable/Symbols/IAssemblySymbol.cs @@ -5,6 +5,7 @@ #nullable enable using System.Collections.Generic; +using System.Collections.Immutable; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis @@ -76,6 +77,11 @@ public interface IAssemblySymbol : ISymbol /// INamedTypeSymbol? ResolveForwardedType(string fullyQualifiedMetadataName); + /// + /// Returns type symbols for top-level (non-nested) types forwarded by this assembly. + /// + ImmutableArray GetForwardedTypes(); + /// /// If this symbol represents a metadata assembly returns the underlying . /// diff --git a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb index 8ac0918de3c..f7bcbbb4569 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/MockSymbols.vb @@ -832,6 +832,10 @@ Friend Class MockAssemblySymbol Return Nothing End Function + Friend Overrides Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + Throw New NotImplementedException() + End Function + Public Overrides Function GetMetadata() As AssemblyMetadata Return Nothing End Function diff --git a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb index dd93683ad4a..4b24ef0f290 100644 --- a/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb +++ b/src/Compilers/VisualBasic/Portable/Emit/PEModuleBuilder.vb @@ -404,14 +404,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Next End If + Debug.Assert(OutputKind.IsNetModule() = sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule()) + GetForwardedTypes(sourceAssembly, builder) + Return builder.ToImmutableAndFree() + End Function + + ''' + ''' Returns a set of top-level forwarded types + ''' + Friend Shared Function GetForwardedTypes(sourceAssembly As SourceAssemblySymbol, builderOpt As ArrayBuilder(Of Cci.ExportedType)) As HashSet(Of NamedTypeSymbol) Dim seenTopLevelForwardedTypes = New HashSet(Of NamedTypeSymbol)() - GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builder) + GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builderOpt) - If Not OutputKind.IsNetModule() Then - GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builder) + If Not sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule() Then + GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builderOpt) End If - Return builder.ToImmutableAndFree() + Return seenTopLevelForwardedTypes End Function Private Sub ReportExportedTypeNameCollisions(exportedTypes As ImmutableArray(Of Cci.ExportedType), diagnostics As DiagnosticBag) @@ -506,14 +515,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Private Shared Sub GetForwardedTypes( seenTopLevelTypes As HashSet(Of NamedTypeSymbol), wellKnownAttributeData As CommonAssemblyWellKnownAttributeData(Of NamedTypeSymbol), - builder As ArrayBuilder(Of Cci.ExportedType)) + builderOpt As ArrayBuilder(Of Cci.ExportedType)) If wellKnownAttributeData?.ForwardedTypes?.Count > 0 Then ' (type, index of the parent exported type in builder, or -1 if the type is a top-level type) Dim stack = ArrayBuilder(Of (type As NamedTypeSymbol, parentIndex As Integer)).GetInstance() ' Hashset enumeration is not guaranteed to be deterministic. Emitting in the order of fully qualified names. - Dim orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes.OrderBy(Function(t) t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)) + Dim orderedForwardedTypes As IEnumerable(Of NamedTypeSymbol) = wellKnownAttributeData.ForwardedTypes + + If builderOpt IsNot Nothing Then + orderedForwardedTypes = orderedForwardedTypes.OrderBy(Function(t) t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)) + End If For Each forwardedType As NamedTypeSymbol In orderedForwardedTypes Dim originalDefinition As NamedTypeSymbol = forwardedType.OriginalDefinition @@ -524,31 +537,32 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Emit Continue For End If - ' Return all nested types. - ' Note the order: depth first, children in reverse order (to match dev10, not a requirement). - Debug.Assert(stack.Count = 0) - stack.Push((originalDefinition, -1)) - - While stack.Count > 0 - Dim entry = stack.Pop() - - ' In general, we don't want private types to appear in the ExportedTypes table. - If entry.type.DeclaredAccessibility = Accessibility.Private Then - ' NOTE: this will also exclude nested types of curr. - Continue While - End If - - ' NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. - - Dim index = builder.Count - builder.Add(New Cci.ExportedType(entry.type, entry.parentIndex, isForwarder:=True)) - - ' Iterate backwards so they get popped in forward order. - Dim nested As ImmutableArray(Of NamedTypeSymbol) = entry.type.GetTypeMembers() ' Ordered. - For i As Integer = nested.Length - 1 To 0 Step -1 - stack.Push((nested(i), index)) - Next - End While + If builderOpt IsNot Nothing Then + ' Return all nested types. + ' Note the order: depth first, children in reverse order (to match dev10, not a requirement). + Debug.Assert(stack.Count = 0) + stack.Push((originalDefinition, -1)) + + While stack.Count > 0 + Dim entry = stack.Pop() + + ' In general, we don't want private types to appear in the ExportedTypes table. + If entry.type.DeclaredAccessibility = Accessibility.Private Then + ' NOTE: this will also exclude nested types of curr. + Continue While + End If + + ' NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection. + Dim index = builderOpt.Count + builderOpt.Add(New Cci.ExportedType(entry.type, entry.parentIndex, isForwarder:=True)) + + ' Iterate backwards so they get popped in forward order. + Dim nested As ImmutableArray(Of NamedTypeSymbol) = entry.type.GetTypeMembers() ' Ordered. + For i As Integer = nested.Length - 1 To 0 Step -1 + stack.Push((nested(i), index)) + Next + End While + End If Next stack.Free() diff --git a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb index acc2ee9a842..4d2c7724149 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/AssemblySymbol.vb @@ -310,6 +310,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return New MissingMetadataTypeSymbol.TopLevelWithCustomErrorInfo(forwardingModule, emittedName, diagnosticInfo) End Function + Friend MustOverride Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + ''' ''' Lookup declaration for predefined CorLib type in this Assembly. Only valid if this ''' assembly is the Cor Library @@ -643,6 +645,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return Me.ResolveForwardedType(metadataName) End Function + Private Function IAssemblySymbol_GetForwardedTypes() As ImmutableArray(Of INamedTypeSymbol) Implements IAssemblySymbol.GetForwardedTypes + Return ImmutableArrayExtensions.AsImmutable(Of INamedTypeSymbol)(GetAllTopLevelForwardedTypes().OrderBy(Function(t) t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat))) + End Function + Private Function IAssemblySymbol_GetTypeByMetadataName(metadataName As String) As INamedTypeSymbol Implements IAssemblySymbol.GetTypeByMetadataName Return Me.GetTypeByMetadataName(metadataName) End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb index 8fec711e6b7..120294e4402 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Metadata/PE/PEAssemblySymbol.vb @@ -163,6 +163,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Metadata.PE Return PrimaryModule.GetAssembliesForForwardedType(emittedName, ignoreCase, matchedName) End Function + Friend Overrides Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + Return PrimaryModule.GetForwardedTypes() + End Function + Friend Overrides Function TryLookupForwardedMetadataTypeWithCycleDetection(ByRef emittedName As MetadataTypeName, visitedAssemblies As ConsList(Of AssemblySymbol), ignoreCase As Boolean) As NamedTypeSymbol ' Check if it is a forwarded type. Dim matchedName As String = Nothing diff --git a/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb index 6ff91f8c51e..d1298b0a078 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/MissingAssemblySymbol.vb @@ -142,6 +142,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Return result End Function + Friend NotOverridable Overrides Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + Return SpecializedCollections.EmptyEnumerable(Of NamedTypeSymbol)() + End Function + Friend Overrides Function GetDeclaredSpecialType(type As SpecialType) As NamedTypeSymbol Throw ExceptionUtilities.Unreachable End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb index 4b80a1d068b..d74fa93184c 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Retargeting/RetargetingAssemblySymbol.vb @@ -255,6 +255,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols.Retargeting Return Me.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName) End Function + Friend Overrides Iterator Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + For Each underlying As NamedTypeSymbol In UnderlyingAssembly.GetAllTopLevelForwardedTypes() + Yield Me.RetargetingTranslator.Retarget(underlying, RetargetOptions.RetargetPrimitiveTypesByName) + Next + End Function + Public Overrides Function GetMetadata() As AssemblyMetadata Return _underlyingAssembly.GetMetadata() End Function diff --git a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb index ca8acbbee19..849e4db7b68 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/Source/SourceAssemblySymbol.vb @@ -435,6 +435,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols Debug.Assert(lazyNetModuleAttributesBag.IsSealed) End Sub + Friend Overrides Function GetAllTopLevelForwardedTypes() As IEnumerable(Of NamedTypeSymbol) + Return Emit.PEModuleBuilder.GetForwardedTypes(Me, builderOpt:=Nothing) + End Function + Private Sub EnsureNetModuleAttributesAreBound() If _lazyNetModuleAttributesBag Is Nothing Then LoadAndValidateNetModuleAttributes(_lazyNetModuleAttributesBag) diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/DeterministicTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/DeterministicTests.vb index ae909213633..691ba58eed4 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/DeterministicTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/DeterministicTests.vb @@ -9,6 +9,7 @@ Imports System.Collections.Immutable Imports System.Threading Imports Microsoft.CodeAnalysis.Test.Utilities Imports Roslyn.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Emit @@ -168,11 +169,11 @@ namespace Namespace3 { public class GenericType {} } " - Dim forwardedToCompilation = CreateCSharpCompilation( - forwardedToCode, + Dim forwardedToCompilation1 = CreateCSharpCompilation( + forwardedToCode, assemblyName:="ForwardedTo", compilationOptions:=New CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) - Dim forwardedToReference = forwardedToCompilation.EmitToImageReference() + Dim forwardedToReference1 = forwardedToCompilation1.EmitToImageReference() Dim forwardingCode = " using System.Runtime.CompilerServices; @@ -192,7 +193,7 @@ using System.Runtime.CompilerServices; "ForwardingAssembly", forwardingCode, compilationOptions:=New CSharp.CSharpCompilationOptions(OutputKind.NetModule), - referencedAssemblies:={MscorlibRef, SystemRef, forwardedToReference}) + referencedAssemblies:={MscorlibRef, SystemRef, forwardedToReference1}) Dim forwardingNetModuleReference = forwardingNetModule.EmitToImageReference() @@ -200,7 +201,8 @@ using System.Runtime.CompilerServices; assemblyName:="ForwardingAssembly", source:=String.Empty, options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary), - references:={forwardingNetModuleReference, forwardedToReference}) + references:={forwardingNetModuleReference, forwardedToReference1}) + Dim forwardingReference = new VisualBasicCompilationReference(forwardingCompilation) Dim sortedFullNames = { @@ -217,14 +219,48 @@ using System.Runtime.CompilerServices; "Namespace4.Embedded.Type2" } + Dim metadataValidator As Action(Of ModuleSymbol) = Sub(m) + Dim assembly = m.ContainingAssembly + Assert.Equal(sortedFullNames, GetNamesOfForwardedTypes(assembly)) + End Sub + + CompileAndVerify(forwardingCompilation, symbolValidator:=metadataValidator, sourceSymbolValidator:=metadataValidator, verify:=Verification.Skipped) + Using stream = forwardingCompilation.EmitToStream() Using block = ModuleMetadata.CreateFromStream(stream) Dim metadataFullNames = MetadataValidation.GetExportedTypesFullNames(block.MetadataReader) Assert.Equal(sortedFullNames, metadataFullNames) End Using End Using + + Dim forwardedToCompilation2 = CreateCSharpCompilation( + forwardedToCode, assemblyName:="ForwardedTo", + compilationOptions:=New CSharp.CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)) + + Dim forwardedToReference2 = forwardedToCompilation2.EmitToImageReference() + + Dim withRetargeting = CreateCompilationWithMscorlib40( + source:=String.Empty, + options:=New VisualBasicCompilationOptions(OutputKind.DynamicallyLinkedLibrary), + references:={forwardedToReference2, forwardingReference}) + + Dim retargeting = DirectCast(withRetargeting.GetReferencedAssemblySymbol(forwardingReference), Retargeting.RetargetingAssemblySymbol) + Dim orwardedToAssembly2 = withRetargeting.GetReferencedAssemblySymbol(forwardedToReference2) + Assert.Equal(sortedFullNames, GetNamesOfForwardedTypes(retargeting)) + + For Each t In GetForwardedTypes(retargeting) + Assert.Same(orwardedToAssembly2, t.ContainingAssembly) + Next End Sub + Private Shared Function GetNamesOfForwardedTypes(assembly As AssemblySymbol) As IEnumerable(Of String) + Return GetForwardedTypes(assembly).Select(Function(t) t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)) + End Function + + Private Shared Function GetForwardedTypes(assembly As AssemblySymbol) As IEnumerable(Of INamedTypeSymbol) + Return DirectCast(assembly, IAssemblySymbol).GetForwardedTypes() + End Function + Public Sub TestInterfacesPartialClassDeterminism() ' When we have parts of a class in different trees, diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/MissingTypeReferences.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/MissingTypeReferences.vb index 0f699af7379..2cc82883319 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/MissingTypeReferences.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/MissingTypeReferences.vb @@ -478,6 +478,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Symbols.Metadata.PE Assert.True(missingAssembly.Equals(missingAssembly)) Assert.NotEqual(New Object(), missingAssembly) Assert.False(missingAssembly.Equals(Nothing)) + + Assert.Empty(DirectCast(missingAssembly, IAssemblySymbol).GetForwardedTypes()) End Sub diff --git a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/TypeForwarders.vb b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/TypeForwarders.vb index 2194355cd45..b2b02d5e63c 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/TypeForwarders.vb +++ b/src/Compilers/VisualBasic/Test/Symbol/SymbolsTests/Metadata/PE/TypeForwarders.vb @@ -1035,6 +1035,8 @@ End class appCompilation = CreateCompilationWithMscorlib40AndReferences(app, {modRef1, New VisualBasicCompilationReference(forwardedTypesCompilation)}, TestOptions.ReleaseDll) + Assert.Equal({"CF1"}, GetNamesOfForwardedTypes(appCompilation)) + peModule = DirectCast(appCompilation.Assembly.Modules(1), PEModuleSymbol) metadata = peModule.Module @@ -1051,6 +1053,7 @@ End class Dim metadataReader1 = DirectCast(m, PEModuleSymbol).Module.GetMetadataReader() Assert.Equal(1, metadataReader1.GetTableRowCount(TableIndex.ExportedType)) ValidateExportedTypeRow(metadataReader1.ExportedTypes.First(), metadataReader1, "CF1") + Assert.Equal({"CF1"}, GetNamesOfForwardedTypes(m.ContainingAssembly)) ' Attributes should not actually be emitted. Assert.Equal(0, m.ContainingAssembly.GetAttributes(AttributeDescription.TypeForwardedToAttribute).Count()) @@ -1131,6 +1134,14 @@ BC30652: Reference required to assembly 'ForwarderTargetAssembly, Version=0.0.0. ) End Sub + Private Shared Function GetNamesOfForwardedTypes(appCompilation As VisualBasicCompilation) As IEnumerable(Of String) + Return GetNamesOfForwardedTypes(appCompilation.Assembly) + End Function + + Private Shared Function GetNamesOfForwardedTypes(assembly As AssemblySymbol) As IEnumerable(Of String) + Return DirectCast(assembly, IAssemblySymbol).GetForwardedTypes().Select(Function(t) t.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat)) + End Function + Private Shared Sub ValidateExportedTypeRow(exportedTypeHandle As ExportedTypeHandle, reader As MetadataReader, expectedFullName As String) Dim exportedTypeRow As ExportedType = reader.GetExportedType(exportedTypeHandle) Dim split = expectedFullName.Split("."c) @@ -1212,11 +1223,13 @@ End class Dim appCompilation = CreateCompilationWithMscorlib40AndReferences(app, {modRef, New VisualBasicCompilationReference(forwardedTypesCompilation)}, TestOptions.ReleaseDll) + Assert.Equal({"CF1"}, GetNamesOfForwardedTypes(appCompilation)) ' Exported types in .NET module cause PEVerify to fail. CompileAndVerify(appCompilation, verify:=Verification.Fails, symbolValidator:=Sub(m) Dim peReader1 = DirectCast(m, PEModuleSymbol).Module.GetMetadataReader() + Assert.Equal({"CF1"}, GetNamesOfForwardedTypes(m.ContainingAssembly)) Assert.Equal(2, peReader1.GetTableRowCount(TableIndex.ExportedType)) ValidateExportedTypeRow(peReader1.ExportedTypes.First(), peReader1, "CF1") ValidateExportedTypeRow(peReader1.ExportedTypes(1), peReader1, "CF1+CF2") -- GitLab