提交 92b4ad67 编写于 作者: T Tomas Matousek

Dedup import scopes based on value equality instead of reference equality

上级 0ea15464
......@@ -263,7 +263,8 @@ public virtual ConsList<LocalSymbol> ImplicitlyTypedLocalsBeingBound
}
/// <summary>
/// The Imports for all containing namespace declarations (innermost-to-outermost, including global).
/// The imports for all containing namespace declarations (innermost-to-outermost, including global),
/// or null if there are none.
/// </summary>
internal virtual ImportChain ImportChain
{
......
......@@ -7,6 +7,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
internal sealed class ImportChain : Cci.IImportScope
{
public readonly Imports Imports;
......@@ -22,6 +23,11 @@ public ImportChain(Imports imports, ImportChain parentOpt)
ParentOpt = parentOpt;
}
private string GetDebuggerDisplay()
{
return $"{Imports.GetDebuggerDisplay()} ^ {ParentOpt?.GetHashCode() ?? 0}";
}
ImmutableArray<Cci.UsedNamespaceOrType> Cci.IImportScope.GetUsedNamespaces()
{
// The imports should have been translated during code gen.
......
......@@ -16,6 +16,7 @@ namespace Microsoft.CodeAnalysis.CSharp
/// <summary>
/// Represents symbols imported to the binding scope via using namespace, using alias, and extern alias.
/// </summary>
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
internal sealed class Imports
{
internal static readonly Imports Empty = new Imports(
......@@ -53,6 +54,15 @@ internal sealed class Imports
this.ExternAliases = externs;
}
internal string GetDebuggerDisplay()
{
return string.Join("; ",
UsingAliases.OrderBy(x => x.Value.UsingDirective.Location.SourceSpan.Start).Select(ua => $"{ua.Key} = {ua.Value.Alias.Target}").Concat(
Usings.Select(u => u.NamespaceOrType.ToString())).Concat(
ExternAliases.Select(ea => $"extern alias {ea.Alias.Name}")));
}
public static Imports FromSyntax(
CSharpSyntaxNode declarationSyntax,
InContainerBinder binder,
......
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Roslyn.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit
{
......@@ -37,7 +38,7 @@ private ImmutableArray<byte> EmitDeterministic(string source, Platform platform,
{
var options = (optimize ? TestOptions.ReleaseExe : TestOptions.DebugExe).WithPlatform(platform).WithDeterministic(true);
var compilation = CreateCompilation(source, assemblyName: "DeterminismTest", references: new[] { MscorlibRef }, options: options);
var compilation = CreateCompilation(source, assemblyName: "DeterminismTest", references: new[] { MscorlibRef, SystemCoreRef, CSharpRef }, options: options);
// The resolution of the PE header time date stamp is seconds, and we want to make sure that has an opportunity to change
// between calls to Emit.
......@@ -101,6 +102,34 @@ public void Simple()
Assert.NotEqual(mvid3, mvid7);
}
const string CompareAllBytesEmitted_Source = @"
using System;
using System.Linq;
using System.Collections.Generic;
namespace N
{
using I4 = System.Int32;
class Program
{
public static IEnumerable<int> F()
{
I4 x = 1;
yield return 1;
yield return x;
}
public static void Main(string[] args)
{
dynamic x = 1;
const int a = 1;
F().ToArray();
Console.WriteLine(x + a);
}
}
}";
[Fact]
public void CompareAllBytesEmitted_Release()
{
......@@ -111,17 +140,12 @@ public void CompareAllBytesEmitted_Release()
DebugInformationFormat.Embedded
})
{
var source =
@"class Program
{
public static void Main(string[] args) {}
}";
var result1 = EmitDeterministic(source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: true);
var result2 = EmitDeterministic(source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: true);
var result1 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: true);
var result2 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: true);
AssertEx.Equal(result1, result2);
var result3 = EmitDeterministic(source, Platform.X64, pdbFormat, optimize: true);
var result4 = EmitDeterministic(source, Platform.X64, pdbFormat, optimize: true);
var result3 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.X64, pdbFormat, optimize: true);
var result4 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.X64, pdbFormat, optimize: true);
AssertEx.Equal(result3, result4);
}
}
......@@ -136,17 +160,12 @@ public void CompareAllBytesEmitted_Debug()
DebugInformationFormat.Embedded
})
{
var source =
@"class Program
{
public static void Main(string[] args) {}
}";
var result1 = EmitDeterministic(source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: false);
var result2 = EmitDeterministic(source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: false);
var result1 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: false);
var result2 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.AnyCpu32BitPreferred, pdbFormat, optimize: false);
AssertEx.Equal(result1, result2);
var result3 = EmitDeterministic(source, Platform.X64, pdbFormat, optimize: false);
var result4 = EmitDeterministic(source, Platform.X64, pdbFormat, optimize: false);
var result3 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.X64, pdbFormat, optimize: false);
var result4 = EmitDeterministic(CompareAllBytesEmitted_Source, Platform.X64, pdbFormat, optimize: false);
AssertEx.Equal(result3, result4);
}
}
......
......@@ -8,6 +8,7 @@
using System.Reflection.PortableExecutable;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.MetadataUtilities;
using Roslyn.Test.Utilities;
......@@ -2386,5 +2387,91 @@ void M()
</methods>
</symbols>");
}
[Fact]
public void ImportScopeEquality()
{
var sources = new[] { @"
extern alias A;
using System;
using C = System;
namespace N.M
{
using System.Collections;
class C1 { void F() {} }
}
namespace N.M
{
using System.Collections;
class C2 { void F() {} }
}
", @"
extern alias A;
using System;
using C = System;
namespace N.M
{
using System.Collections;
class C3 { void F() {} }
}
namespace N.M
{
using System.Collections.Generic;
class C4 { void F() {} }
}
", @"
extern alias A;
using System;
using D = System;
namespace N.M
{
using System.Collections;
class C5 { void F() {} }
}
", @"
extern alias A;
using System;
class C6 { void F() {} }
" };
var c = CreateCompilationWithMscorlib(sources, new[] { SystemCoreRef.WithAliases(ImmutableArray.Create("A")) });
var pdbStream = new MemoryStream();
c.EmitToArray(EmitOptions.Default.WithDebugInformationFormat(DebugInformationFormat.PortablePdb), pdbStream: pdbStream);
var pdbImage = pdbStream.ToImmutable();
using (var metadata = new PinnedMetadata(pdbImage))
{
var mdReader = metadata.Reader;
var writer = new StringWriter();
var mdVisualizer = new MetadataVisualizer(mdReader, writer);
mdVisualizer.WriteImportScope();
AssertEx.AssertEqualToleratingWhitespaceDifferences(@"
ImportScope (index: 0x35, size: 36):
=============================================================================================
Parent Imports
=============================================================================================
1: nil 'A' (#1) = 0x23000002 (AssemblyRef)
2: 0x35000001 (ImportScope) Extern Alias 'A' (#1), 'System' (#7)
3: 0x35000001 (ImportScope) Extern Alias 'A' (#1), 'System' (#7), 'C' (#1d) = 'System' (#7)
4: 0x35000003 (ImportScope) nil
5: 0x35000004 (ImportScope) 'System.Collections' (#27)
6: 0x35000004 (ImportScope) 'System.Collections.Generic' (#4b)
7: 0x35000001 (ImportScope) Extern Alias 'A' (#1), 'System' (#7), 'D' (#69) = 'System' (#7)
8: 0x35000007 (ImportScope) nil
9: 0x35000008 (ImportScope) 'System.Collections' (#27)
", writer.ToString());
}
}
}
}
......@@ -11,6 +11,7 @@ internal interface IImportScope
{
/// <summary>
/// Zero or more used namespaces. These correspond to using directives in C# or Imports syntax in VB.
/// Multiple invocations return the same array instance.
/// </summary>
ImmutableArray<UsedNamespaceOrType> GetUsedNamespaces();
......
......@@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
namespace Microsoft.Cci
{
......@@ -78,7 +79,28 @@ private struct CustomDebugInformationRow
private readonly List<CustomDebugInformationRow> _customDebugInformationTable = new List<CustomDebugInformationRow>();
private readonly Dictionary<DebugSourceDocument, int> _documentIndex = new Dictionary<DebugSourceDocument, int>();
private readonly Dictionary<IImportScope, int> _scopeIndex = new Dictionary<IImportScope, int>();
private readonly Dictionary<IImportScope, int> _scopeIndex = new Dictionary<IImportScope, int>(ImportScopeEqualityComparer.Instance);
/// <summary>
/// Import scopes are associated with binders (in C#) and thus multiple instances might be created for a single set of imports.
/// We consider scopes with the same parent and the same imports the same.
/// Internal for testing.
/// </summary>
internal sealed class ImportScopeEqualityComparer : IEqualityComparer<IImportScope>
{
public static readonly ImportScopeEqualityComparer Instance = new ImportScopeEqualityComparer();
public bool Equals(IImportScope x, IImportScope y)
{
return (object)x == y ||
x != null && y != null && Equals(x.Parent, y.Parent) && x.GetUsedNamespaces().SequenceEqual(y.GetUsedNamespaces());
}
public int GetHashCode(IImportScope obj)
{
return Hash.Combine(Hash.CombineValues(obj.GetUsedNamespaces()), obj.Parent != null ? GetHashCode(obj.Parent) : 0);
}
}
private void SerializeMethodDebugInfo(IMethodBody bodyOpt, int methodRid, int localSignatureRowId)
{
......
......@@ -142,7 +142,7 @@ public void Visualize(int generation = -1)
WriteLocalScope();
WriteLocalVariable();
WriteLocalConstant();
WriteLocalImport();
WriteImportScope();
WriteCustomDebugInformation();
// heaps:
......@@ -1716,7 +1716,7 @@ private static bool IsPrimitiveType(SignatureTypeCode typeCode)
}
}
private void WriteLocalImport()
public void WriteImportScope()
{
AddHeader(
"Parent",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册