|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Emit.EditAndContinue;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
namespace Microsoft.CodeAnalysis.CSharp.Emit
{
internal sealed class CSharpSymbolMatcher : SymbolMatcher
{
private readonly Visitor _visitor;
public CSharpSymbolMatcher(
SourceAssemblySymbol sourceAssembly,
SourceAssemblySymbol otherAssembly,
SynthesizedTypeMaps synthesizedTypes,
IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? otherSynthesizedMembers,
IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? otherDeletedMembers)
{
_visitor = new Visitor(sourceAssembly, otherAssembly, synthesizedTypes, otherSynthesizedMembers, otherDeletedMembers, new DeepTranslator(otherAssembly.GetSpecialType(SpecialType.System_Object)));
}
public CSharpSymbolMatcher(
SynthesizedTypeMaps synthesizedTypes,
SourceAssemblySymbol sourceAssembly,
PEAssemblySymbol otherAssembly)
{
_visitor = new Visitor(
sourceAssembly,
otherAssembly,
synthesizedTypes,
otherSynthesizedMembers: null,
deepTranslator: null,
otherDeletedMembers: null);
}
public override Cci.IDefinition? MapDefinition(Cci.IDefinition definition)
{
if (definition.GetInternalSymbol() is Symbol symbol)
{
return (Cci.IDefinition?)_visitor.Visit(symbol)?.GetCciAdapter();
}
// For simplicity, PID helpers and no-PIA embedded definitions are not reused across generations, so we don't map them here.
// Instead, new ones are regenerated as needed.
Debug.Assert(definition is PrivateImplementationDetails or Cci.IEmbeddedDefinition);
return null;
}
public override Cci.INamespace? MapNamespace(Cci.INamespace @namespace)
{
if (@namespace.GetInternalSymbol() is NamespaceSymbol symbol)
{
return (Cci.INamespace?)_visitor.Visit(symbol)?.GetCciAdapter();
}
return null;
}
public override Cci.ITypeReference? MapReference(Cci.ITypeReference reference)
{
if (reference.GetInternalSymbol() is Symbol symbol)
{
return (Cci.ITypeReference?)_visitor.Visit(symbol)?.GetCciAdapter();
}
return null;
}
internal bool TryGetAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue typeValue)
=> _visitor.TryGetAnonymousTypeValue(template, out typeValue);
private sealed class Visitor : CSharpSymbolVisitor<Symbol?>
{
private readonly SynthesizedTypeMaps _synthesizedTypes;
private readonly SourceAssemblySymbol _sourceAssembly;
// metadata or source assembly:
private readonly AssemblySymbol _otherAssembly;
/// <summary>
/// Members that are not listed directly on their containing type or namespace symbol as they were synthesized in a lowering phase,
/// after the symbol has been created.
/// </summary>
private readonly IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? _otherSynthesizedMembers;
private readonly IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? _otherDeletedMembers;
private readonly SymbolComparer _comparer;
private readonly ConcurrentDictionary<Symbol, Symbol?> _matches = new(ReferenceEqualityComparer.Instance);
/// <summary>
/// A cache of members per type, populated when the first member for a given
/// type is needed. Within each type, members are indexed by name. The reason
/// for caching, and indexing by name, is to avoid searching sequentially
/// through all members of a given kind each time a member is matched.
/// </summary>
private readonly ConcurrentDictionary<ISymbolInternal, IReadOnlyDictionary<string, ImmutableArray<ISymbolInternal>>> _otherMembers = new(ReferenceEqualityComparer.Instance);
public Visitor(
SourceAssemblySymbol sourceAssembly,
AssemblySymbol otherAssembly,
SynthesizedTypeMaps synthesizedTypes,
IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? otherSynthesizedMembers,
IReadOnlyDictionary<ISymbolInternal, ImmutableArray<ISymbolInternal>>? otherDeletedMembers,
DeepTranslator? deepTranslator)
{
_synthesizedTypes = synthesizedTypes;
_sourceAssembly = sourceAssembly;
_otherAssembly = otherAssembly;
_otherSynthesizedMembers = otherSynthesizedMembers;
_otherDeletedMembers = otherDeletedMembers;
_comparer = new SymbolComparer(this, deepTranslator);
}
public override Symbol DefaultVisit(Symbol symbol)
{
// Symbol should have been handled elsewhere.
throw ExceptionUtilities.Unreachable();
}
public override Symbol? Visit(Symbol symbol)
{
Debug.Assert((object)symbol.ContainingAssembly != (object)_otherAssembly);
// Add an entry for the match, even if there is no match, to avoid
// matching the same symbol unsuccessfully multiple times.
return _matches.GetOrAdd(symbol, base.Visit);
}
public override Symbol? VisitArrayType(ArrayTypeSymbol symbol)
{
var otherElementType = (TypeSymbol?)Visit(symbol.ElementType);
if (otherElementType is null)
{
// For a newly added type, there is no match in the previous generation, so it could be null.
return null;
}
var otherModifiers = VisitCustomModifiers(symbol.ElementTypeWithAnnotations.CustomModifiers);
if (symbol.IsSZArray)
{
return ArrayTypeSymbol.CreateSZArray(_otherAssembly, symbol.ElementTypeWithAnnotations.WithTypeAndModifiers(otherElementType, otherModifiers));
}
return ArrayTypeSymbol.CreateMDArray(_otherAssembly, symbol.ElementTypeWithAnnotations.WithTypeAndModifiers(otherElementType, otherModifiers), symbol.Rank, symbol.Sizes, symbol.LowerBounds);
}
public override Symbol? VisitEvent(EventSymbol symbol)
=> VisitNamedTypeMember(symbol, AreEventsEqual);
public override Symbol? VisitField(FieldSymbol symbol)
=> VisitNamedTypeMember(symbol, AreFieldsEqual);
public override Symbol? VisitMethod(MethodSymbol symbol)
{
// Not expecting constructed method.
Debug.Assert(symbol.IsDefinition);
return VisitNamedTypeMember(symbol, AreMethodsEqual);
}
public override Symbol? VisitModule(ModuleSymbol module)
{
var otherAssembly = (AssemblySymbol?)Visit(module.ContainingAssembly);
if (otherAssembly is null)
{
return null;
}
// manifest module:
if (module.Ordinal == 0)
{
return otherAssembly.Modules[0];
}
// match non-manifest module by name:
for (int i = 1; i < otherAssembly.Modules.Length; i++)
{
var otherModule = otherAssembly.Modules[i];
// use case sensitive comparison -- modules whose names differ in casing are considered distinct:
if (StringComparer.Ordinal.Equals(otherModule.Name, module.Name))
{
return otherModule;
}
}
return null;
}
public override Symbol? VisitAssembly(AssemblySymbol assembly)
{
if (assembly.IsLinked)
{
return assembly;
}
// When we map synthesized symbols from previous generations to the latest compilation
// we might encounter a symbol that is defined in arbitrary preceding generation,
// not just the immediately preceding generation. If the source assembly uses time-based
// versioning assemblies of preceding generations might differ in their version number.
if (IdentityEqualIgnoringVersionWildcard(assembly, _sourceAssembly))
{
return _otherAssembly;
}
// find a referenced assembly with the same source identity (modulo assembly version patterns):
foreach (var otherReferencedAssembly in _otherAssembly.Modules[0].ReferencedAssemblySymbols)
{
if (IdentityEqualIgnoringVersionWildcard(assembly, otherReferencedAssembly))
{
return otherReferencedAssembly;
}
}
return null;
}
private static bool IdentityEqualIgnoringVersionWildcard(AssemblySymbol left, AssemblySymbol right)
{
var leftIdentity = left.Identity;
var rightIdentity = right.Identity;
return AssemblyIdentityComparer.SimpleNameComparer.Equals(leftIdentity.Name, rightIdentity.Name) &&
(left.AssemblyVersionPattern ?? leftIdentity.Version).Equals(right.AssemblyVersionPattern ?? rightIdentity.Version) &&
AssemblyIdentity.EqualIgnoringNameAndVersion(leftIdentity, rightIdentity);
}
public override Symbol? VisitNamespace(NamespaceSymbol @namespace)
{
var otherContainer = Visit(@namespace.ContainingSymbol);
// Containing namespace will be missing from other assembly
// if its was added in the (newer) source assembly.
if (otherContainer is null)
{
return null;
}
switch (otherContainer.Kind)
{
case SymbolKind.NetModule:
Debug.Assert(@namespace.IsGlobalNamespace);
return ((ModuleSymbol)otherContainer).GlobalNamespace;
case SymbolKind.Namespace:
return FindMatchingMember(otherContainer, @namespace, AreNamespacesEqual);
default:
throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind);
}
}
public override Symbol VisitDynamicType(DynamicTypeSymbol symbol)
{
return _otherAssembly.GetSpecialType(SpecialType.System_Object);
}
public override Symbol? VisitNamedType(NamedTypeSymbol sourceType)
{
var originalDef = sourceType.OriginalDefinition;
if ((object)originalDef != (object)sourceType)
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
var typeArguments = sourceType.GetAllTypeArguments(ref discardedUseSiteInfo);
var otherDef = (NamedTypeSymbol?)Visit(originalDef);
if (otherDef is null)
{
return null;
}
var otherTypeParameters = otherDef.GetAllTypeParameters();
bool translationFailed = false;
var otherTypeArguments = typeArguments.SelectAsArray((t, v) =>
{
var newType = (TypeSymbol?)v.Visit(t.Type);
if (newType is null)
{
// For a newly added type, there is no match in the previous generation, so it could be null.
translationFailed = true;
newType = t.Type;
}
return t.WithTypeAndModifiers(newType, v.VisitCustomModifiers(t.CustomModifiers));
}, this);
if (translationFailed)
{
// For a newly added type, there is no match in the previous generation, so it could be null.
return null;
}
// TODO: LambdaFrame has alpha renamed type parameters, should we rather fix that?
var typeMap = new TypeMap(otherTypeParameters, otherTypeArguments, allowAlpha: true);
return typeMap.SubstituteNamedType(otherDef);
}
Debug.Assert(sourceType.IsDefinition);
var otherContainer = this.Visit(sourceType.ContainingSymbol);
// Containing type will be missing from other assembly
// if the type was added in the (newer) source assembly.
if (otherContainer is null)
{
return null;
}
switch (otherContainer.Kind)
{
case SymbolKind.Namespace:
if (sourceType is AnonymousTypeManager.AnonymousTypeTemplateSymbol typeTemplate)
{
Debug.Assert((object)otherContainer == (object)_otherAssembly.GlobalNamespace);
TryGetAnonymousTypeValue(typeTemplate, out var value);
return (NamedTypeSymbol?)value.Type?.GetInternalSymbol();
}
else if (sourceType is AnonymousTypeManager.AnonymousDelegateTemplateSymbol delegateTemplate)
{
Debug.Assert((object)otherContainer == (object)_otherAssembly.GlobalNamespace);
if (delegateTemplate.HasIndexedName)
{
TryGetAnonymousTypeValue(delegateTemplate, out var value);
return (NamedTypeSymbol?)value.Type?.GetInternalSymbol();
}
else
{
TryGetAnonymousDelegateValue(delegateTemplate, out var value);
return (NamedTypeSymbol?)value.Delegate?.GetInternalSymbol();
}
}
if (sourceType.IsAnonymousType)
{
return Visit(AnonymousTypeManager.TranslateAnonymousTypeSymbol(sourceType));
}
return FindMatchingMember(otherContainer, sourceType, AreNamedTypesEqual);
case SymbolKind.NamedType:
return FindMatchingMember(otherContainer, sourceType, AreNamedTypesEqual);
default:
throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind);
}
}
public override Symbol VisitParameter(ParameterSymbol parameter)
{
// Should never reach here. Should be matched as a result of matching the container.
throw ExceptionUtilities.Unreachable();
}
public override Symbol? VisitPointerType(PointerTypeSymbol symbol)
{
var otherPointedAtType = (TypeSymbol?)Visit(symbol.PointedAtType);
if (otherPointedAtType is null)
{
// For a newly added type, there is no match in the previous generation, so it could be null.
return null;
}
var otherModifiers = VisitCustomModifiers(symbol.PointedAtTypeWithAnnotations.CustomModifiers);
return new PointerTypeSymbol(symbol.PointedAtTypeWithAnnotations.WithTypeAndModifiers(otherPointedAtType, otherModifiers));
}
public override Symbol? VisitFunctionPointerType(FunctionPointerTypeSymbol symbol)
{
var sig = symbol.Signature;
var otherReturnType = (TypeSymbol?)Visit(sig.ReturnType);
if (otherReturnType is null)
{
return null;
}
var otherRefCustomModifiers = VisitCustomModifiers(sig.RefCustomModifiers);
var otherReturnTypeWithAnnotations = sig.ReturnTypeWithAnnotations.WithTypeAndModifiers(otherReturnType, VisitCustomModifiers(sig.ReturnTypeWithAnnotations.CustomModifiers));
var otherParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> otherParamRefCustomModifiers = default;
if (sig.ParameterCount > 0)
{
var otherParamsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(sig.ParameterCount);
var otherParamRefCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(sig.ParameterCount);
foreach (var param in sig.Parameters)
{
var otherType = (TypeSymbol?)Visit(param.Type);
if (otherType is null)
{
otherParamsBuilder.Free();
otherParamRefCustomModifiersBuilder.Free();
return null;
}
otherParamRefCustomModifiersBuilder.Add(VisitCustomModifiers(param.RefCustomModifiers));
otherParamsBuilder.Add(param.TypeWithAnnotations.WithTypeAndModifiers(otherType, VisitCustomModifiers(param.TypeWithAnnotations.CustomModifiers)));
}
otherParameterTypes = otherParamsBuilder.ToImmutableAndFree();
otherParamRefCustomModifiers = otherParamRefCustomModifiersBuilder.ToImmutableAndFree();
}
return symbol.SubstituteTypeSymbol(otherReturnTypeWithAnnotations, otherParameterTypes, otherRefCustomModifiers, otherParamRefCustomModifiers);
}
public override Symbol? VisitProperty(PropertySymbol symbol)
=> VisitNamedTypeMember(symbol, ArePropertiesEqual);
public override Symbol VisitTypeParameter(TypeParameterSymbol symbol)
{
if (symbol is IndexedTypeParameterSymbol indexed)
{
return indexed;
}
var otherContainer = Visit(symbol.ContainingSymbol);
RoslynDebug.AssertNotNull(otherContainer);
var otherTypeParameters = otherContainer.Kind switch
{
SymbolKind.NamedType or SymbolKind.ErrorType => ((NamedTypeSymbol)otherContainer).TypeParameters,
SymbolKind.Method => ((MethodSymbol)otherContainer).TypeParameters,
_ => throw ExceptionUtilities.UnexpectedValue(otherContainer.Kind),
};
return otherTypeParameters[symbol.Ordinal];
}
private ImmutableArray<CustomModifier> VisitCustomModifiers(ImmutableArray<CustomModifier> modifiers)
{
return modifiers.SelectAsArray(VisitCustomModifier);
}
private CustomModifier VisitCustomModifier(CustomModifier modifier)
{
var type = (NamedTypeSymbol?)Visit(((CSharpCustomModifier)modifier).ModifierSymbol);
RoslynDebug.AssertNotNull(type);
return modifier.IsOptional ?
CSharpCustomModifier.CreateOptional(type) :
CSharpCustomModifier.CreateRequired(type);
}
internal bool TryGetAnonymousDelegateValue(AnonymousTypeManager.AnonymousDelegateTemplateSymbol delegateSymbol, out SynthesizedDelegateValue otherDelegateSymbol)
{
Debug.Assert((object)delegateSymbol.ContainingSymbol == (object)_sourceAssembly.GlobalNamespace);
var key = new SynthesizedDelegateKey(delegateSymbol.MetadataName);
return _synthesizedTypes.AnonymousDelegates.TryGetValue(key, out otherDelegateSymbol);
}
internal bool TryGetAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue otherType)
{
Debug.Assert((object)template.ContainingSymbol == (object)_sourceAssembly.GlobalNamespace);
if (template is AnonymousTypeManager.AnonymousTypeTemplateSymbol typeTemplate)
{
return _synthesizedTypes.AnonymousTypes.TryGetValue(typeTemplate.GetAnonymousTypeKey(), out otherType);
}
var delegateTemplate = (AnonymousTypeManager.AnonymousDelegateTemplateSymbol)template;
Debug.Assert(delegateTemplate.DelegateInvokeMethod != null);
var key = new AnonymousDelegateWithIndexedNamePartialKey(delegateTemplate.Arity, delegateTemplate.DelegateInvokeMethod.ParameterCount);
if (_synthesizedTypes.AnonymousDelegatesWithIndexedNames.TryGetValue(key, out var otherTypeCandidates))
{
// The key is partial (not unique). Find a matching Invoke method signature.
foreach (var otherTypeCandidate in otherTypeCandidates)
{
var otherDelegateType = (NamedTypeSymbol?)otherTypeCandidate.Type.GetInternalSymbol();
Debug.Assert(otherDelegateType is not null);
if (isCorrespondingAnonymousDelegate(delegateTemplate, otherDelegateType))
{
otherType = otherTypeCandidate;
return true;
}
}
}
otherType = default;
return false;
bool isCorrespondingAnonymousDelegate(NamedTypeSymbol type, NamedTypeSymbol otherType)
{
Debug.Assert(type.Arity == otherType.Arity);
type = SubstituteTypeParameters(type);
otherType = SubstituteTypeParameters(otherType);
return type.DelegateInvokeMethod is { } invokeMethod &&
otherType.DelegateInvokeMethod is { } otherInvokeMethod &&
invokeMethod.Parameters.SequenceEqual(otherInvokeMethod.Parameters,
(x, y) => isCorrespondingType(x.TypeWithAnnotations, y.TypeWithAnnotations) &&
x.ExplicitDefaultConstantValue == y.ExplicitDefaultConstantValue &&
x.IsParamsArray == y.IsParamsArray &&
x.IsParamsCollection == y.IsParamsCollection) &&
isCorrespondingType(invokeMethod.ReturnTypeWithAnnotations, otherInvokeMethod.ReturnTypeWithAnnotations);
}
bool isCorrespondingType(TypeWithAnnotations type, TypeWithAnnotations expectedType)
{
var otherType = type.WithTypeAndModifiers((TypeSymbol?)this.Visit(type.Type), this.VisitCustomModifiers(type.CustomModifiers));
return otherType.Equals(expectedType, TypeCompareKind.CLRSignatureCompareOptions);
}
}
private Symbol? VisitNamedTypeMember<T>(T member, Func<T, T, bool> predicate)
where T : Symbol
{
var otherType = (NamedTypeSymbol?)Visit(member.ContainingType);
// Containing type may be null for synthesized
// types such as iterators.
if (otherType is null)
{
return null;
}
return FindMatchingMember(otherType, member, predicate);
}
private T? FindMatchingMember<T>(ISymbolInternal otherTypeOrNamespace, T sourceMember, Func<T, T, bool> predicate)
where T : Symbol
{
Debug.Assert(!string.IsNullOrEmpty(sourceMember.MetadataName));
var otherMembersByName = _otherMembers.GetOrAdd(otherTypeOrNamespace, GetAllEmittedMembers);
if (otherMembersByName.TryGetValue(sourceMember.MetadataName, out var otherMembers))
{
foreach (var otherMember in otherMembers)
{
if (otherMember is T other && predicate(sourceMember, other))
{
return other;
}
}
}
return null;
}
private bool AreArrayTypesEqual(ArrayTypeSymbol type, ArrayTypeSymbol other)
{
// TODO: Test with overloads (from PE base class?) that have modifiers.
Debug.Assert(type.ElementTypeWithAnnotations.CustomModifiers.IsEmpty);
Debug.Assert(other.ElementTypeWithAnnotations.CustomModifiers.IsEmpty);
return type.HasSameShapeAs(other) &&
AreTypesEqual(type.ElementType, other.ElementType);
}
private bool AreEventsEqual(EventSymbol @event, EventSymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(@event.Name, other.Name));
// Events can't be overloaded on type.
// ECMA: Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name [ERROR]
return true;
}
private bool AreFieldsEqual(FieldSymbol field, FieldSymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(field.Name, other.Name));
return _comparer.Equals(field.Type, other.Type);
}
private bool AreMethodsEqual(MethodSymbol method, MethodSymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(method.Name, other.Name));
Debug.Assert(method.IsDefinition);
Debug.Assert(other.IsDefinition);
method = SubstituteTypeParameters(method);
other = SubstituteTypeParameters(other);
return _comparer.Equals(method.ReturnType, other.ReturnType) &&
method.RefKind.Equals(other.RefKind) &&
method.Parameters.SequenceEqual(other.Parameters, AreParametersEqual) &&
method.TypeParameters.SequenceEqual(other.TypeParameters, AreTypesEqual);
}
private static MethodSymbol SubstituteTypeParameters(MethodSymbol method)
{
Debug.Assert(method.IsDefinition);
var typeParameters = method.TypeParameters;
int n = typeParameters.Length;
if (n == 0)
{
return method;
}
return method.Construct(IndexedTypeParameterSymbol.Take(n));
}
private bool AreNamedTypesEqual(NamedTypeSymbol type, NamedTypeSymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(type.MetadataName, other.MetadataName));
// TODO: Test with overloads (from PE base class?) that have modifiers.
Debug.Assert(type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.All(t => t.CustomModifiers.IsEmpty));
Debug.Assert(other.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.All(t => t.CustomModifiers.IsEmpty));
return type.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics.SequenceEqual(other.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics, AreTypesEqual);
}
private static NamedTypeSymbol SubstituteTypeParameters(NamedTypeSymbol type)
{
Debug.Assert(type.IsDefinition);
var typeParameters = type.TypeParameters;
int n = typeParameters.Length;
if (n == 0)
{
return type;
}
return type.Construct(IndexedTypeParameterSymbol.Take(n));
}
private bool AreNamespacesEqual(NamespaceSymbol @namespace, NamespaceSymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(@namespace.MetadataName, other.MetadataName));
return true;
}
private bool AreParametersEqual(ParameterSymbol parameter, ParameterSymbol other)
{
Debug.Assert(parameter.Ordinal == other.Ordinal);
// allow a different ref-kind as long as the runtime type is the same:
return parameter.RefKind is RefKind.None == other.RefKind is RefKind.None &&
_comparer.Equals(parameter.Type, other.Type);
}
private bool ArePointerTypesEqual(PointerTypeSymbol type, PointerTypeSymbol other)
{
// TODO: Test with overloads (from PE base class?) that have modifiers.
Debug.Assert(type.PointedAtTypeWithAnnotations.CustomModifiers.IsEmpty);
Debug.Assert(other.PointedAtTypeWithAnnotations.CustomModifiers.IsEmpty);
return AreTypesEqual(type.PointedAtType, other.PointedAtType);
}
private bool AreFunctionPointerTypesEqual(FunctionPointerTypeSymbol type, FunctionPointerTypeSymbol other)
{
var sig = type.Signature;
var otherSig = other.Signature;
ValidateFunctionPointerParamOrReturn(sig.ReturnTypeWithAnnotations, sig.RefKind, sig.RefCustomModifiers, allowOut: false);
ValidateFunctionPointerParamOrReturn(otherSig.ReturnTypeWithAnnotations, otherSig.RefKind, otherSig.RefCustomModifiers, allowOut: false);
if (sig.RefKind != otherSig.RefKind || !AreTypesEqual(sig.ReturnTypeWithAnnotations, otherSig.ReturnTypeWithAnnotations))
{
return false;
}
return sig.Parameters.SequenceEqual(otherSig.Parameters, AreFunctionPointerParametersEqual);
}
private bool AreFunctionPointerParametersEqual(ParameterSymbol param, ParameterSymbol otherParam)
{
ValidateFunctionPointerParamOrReturn(param.TypeWithAnnotations, param.RefKind, param.RefCustomModifiers, allowOut: true);
ValidateFunctionPointerParamOrReturn(otherParam.TypeWithAnnotations, otherParam.RefKind, otherParam.RefCustomModifiers, allowOut: true);
return param.RefKind == otherParam.RefKind && AreTypesEqual(param.TypeWithAnnotations, otherParam.TypeWithAnnotations);
}
[Conditional("DEBUG")]
private static void ValidateFunctionPointerParamOrReturn(TypeWithAnnotations type, RefKind refKind, ImmutableArray<CustomModifier> refCustomModifiers, bool allowOut)
{
Debug.Assert(type.CustomModifiers.IsEmpty);
Debug.Assert(verifyRefModifiers(refCustomModifiers, refKind, allowOut));
static bool verifyRefModifiers(ImmutableArray<CustomModifier> modifiers, RefKind refKind, bool allowOut)
{
Debug.Assert(RefKind.RefReadOnly == RefKind.In);
switch (refKind)
{
case RefKind.RefReadOnly:
case RefKind.Out when allowOut:
return modifiers.Length == 1;
default:
return modifiers.IsEmpty;
}
}
}
private bool ArePropertiesEqual(PropertySymbol property, PropertySymbol other)
{
Debug.Assert(StringOrdinalComparer.Equals(property.MetadataName, other.MetadataName));
// Properties may be overloaded on their signature.
// ECMA: Within the rows owned by a given row in the TypeDef table, there shall be no duplicates based upon Name+Type [ERROR]
return _comparer.Equals(property.Type, other.Type) &&
property.RefKind.Equals(other.RefKind) &&
property.Parameters.SequenceEqual(other.Parameters, AreParametersEqual);
}
private static bool AreTypeParametersEqual(TypeParameterSymbol type, TypeParameterSymbol other)
{
Debug.Assert(type.Ordinal == other.Ordinal);
Debug.Assert(StringOrdinalComparer.Equals(type.Name, other.Name));
// Comparing constraints is unnecessary: two methods cannot differ by
// constraints alone and changing the signature of a method is a rude
// edit. Furthermore, comparing constraint types might lead to a cycle.
Debug.Assert(type.HasConstructorConstraint == other.HasConstructorConstraint);
Debug.Assert(type.HasValueTypeConstraint == other.HasValueTypeConstraint);
Debug.Assert(type.AllowsRefLikeType == other.AllowsRefLikeType);
Debug.Assert(type.HasUnmanagedTypeConstraint == other.HasUnmanagedTypeConstraint);
Debug.Assert(type.HasReferenceTypeConstraint == other.HasReferenceTypeConstraint);
Debug.Assert(type.ConstraintTypesNoUseSiteDiagnostics.Length == other.ConstraintTypesNoUseSiteDiagnostics.Length);
return true;
}
private bool AreTypesEqual(TypeWithAnnotations type, TypeWithAnnotations other)
{
Debug.Assert(type.CustomModifiers.IsDefaultOrEmpty);
Debug.Assert(other.CustomModifiers.IsDefaultOrEmpty);
return AreTypesEqual(type.Type, other.Type);
}
private bool AreTypesEqual(TypeSymbol type, TypeSymbol other)
{
if (type.Kind != other.Kind)
{
return false;
}
switch (type.Kind)
{
case SymbolKind.ArrayType:
return AreArrayTypesEqual((ArrayTypeSymbol)type, (ArrayTypeSymbol)other);
case SymbolKind.PointerType:
return ArePointerTypesEqual((PointerTypeSymbol)type, (PointerTypeSymbol)other);
case SymbolKind.FunctionPointerType:
return AreFunctionPointerTypesEqual((FunctionPointerTypeSymbol)type, (FunctionPointerTypeSymbol)other);
case SymbolKind.NamedType:
case SymbolKind.ErrorType:
return AreNamedTypesEqual((NamedTypeSymbol)type, (NamedTypeSymbol)other);
case SymbolKind.TypeParameter:
return AreTypeParametersEqual((TypeParameterSymbol)type, (TypeParameterSymbol)other);
default:
throw ExceptionUtilities.UnexpectedValue(type.Kind);
}
}
private IReadOnlyDictionary<string, ImmutableArray<ISymbolInternal>> GetAllEmittedMembers(ISymbolInternal symbol)
{
var members = ArrayBuilder<ISymbolInternal>.GetInstance();
if (symbol.Kind == SymbolKind.NamedType)
{
var type = (NamedTypeSymbol)symbol;
members.AddRange(type.GetEventsToEmit());
members.AddRange(type.GetFieldsToEmit());
members.AddRange(type.GetMethodsToEmit());
members.AddRange(type.GetTypeMembers());
members.AddRange(type.GetPropertiesToEmit());
}
else
{
members.AddRange(((NamespaceSymbol)symbol).GetMembers());
}
if (_otherSynthesizedMembers != null && _otherSynthesizedMembers.TryGetValue(symbol, out var synthesizedMembers))
{
members.AddRange(synthesizedMembers);
}
if (_otherDeletedMembers?.TryGetValue(symbol, out var deletedMembers) == true)
{
members.AddRange(deletedMembers);
}
var result = members.ToDictionary(s => s.MetadataName, StringOrdinalComparer.Instance);
members.Free();
return result;
}
private sealed class SymbolComparer
{
private readonly Visitor _matcher;
private readonly DeepTranslator? _deepTranslator;
public SymbolComparer(Visitor matcher, DeepTranslator? deepTranslator)
{
Debug.Assert(matcher != null);
_matcher = matcher;
_deepTranslator = deepTranslator;
}
public bool Equals(TypeSymbol source, TypeSymbol other)
{
if (ReferenceEquals(source, other))
{
return true;
}
var visitedSource = (TypeSymbol?)_matcher.Visit(source);
var visitedOther = (_deepTranslator != null) ? (TypeSymbol)_deepTranslator.Visit(other) : other;
return visitedSource?.Equals(visitedOther, TypeCompareKind.IgnoreDynamicAndTupleNames | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes) == true;
}
}
}
internal sealed class DeepTranslator : CSharpSymbolVisitor<Symbol>
{
private readonly ConcurrentDictionary<Symbol, Symbol> _matches;
private readonly NamedTypeSymbol _systemObject;
public DeepTranslator(NamedTypeSymbol systemObject)
{
_matches = new ConcurrentDictionary<Symbol, Symbol>(ReferenceEqualityComparer.Instance);
_systemObject = systemObject;
}
public override Symbol DefaultVisit(Symbol symbol)
{
// Symbol should have been handled elsewhere.
throw ExceptionUtilities.Unreachable();
}
public override Symbol Visit(Symbol symbol)
{
return _matches.GetOrAdd(symbol, base.Visit(symbol));
}
public override Symbol VisitArrayType(ArrayTypeSymbol symbol)
{
var translatedElementType = (TypeSymbol)this.Visit(symbol.ElementType);
var translatedModifiers = VisitCustomModifiers(symbol.ElementTypeWithAnnotations.CustomModifiers);
if (symbol.IsSZArray)
{
return ArrayTypeSymbol.CreateSZArray(symbol.BaseTypeNoUseSiteDiagnostics.ContainingAssembly, symbol.ElementTypeWithAnnotations.WithTypeAndModifiers(translatedElementType, translatedModifiers));
}
return ArrayTypeSymbol.CreateMDArray(symbol.BaseTypeNoUseSiteDiagnostics.ContainingAssembly, symbol.ElementTypeWithAnnotations.WithTypeAndModifiers(translatedElementType, translatedModifiers), symbol.Rank, symbol.Sizes, symbol.LowerBounds);
}
public override Symbol VisitDynamicType(DynamicTypeSymbol symbol)
{
return _systemObject;
}
public override Symbol VisitNamedType(NamedTypeSymbol type)
{
var originalDef = type.OriginalDefinition;
if ((object)originalDef != type)
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
var translatedTypeArguments = type.GetAllTypeArguments(ref discardedUseSiteInfo).SelectAsArray((t, v) => t.WithTypeAndModifiers((TypeSymbol)v.Visit(t.Type),
v.VisitCustomModifiers(t.CustomModifiers)),
this);
var translatedOriginalDef = (NamedTypeSymbol)this.Visit(originalDef);
var typeMap = new TypeMap(translatedOriginalDef.GetAllTypeParameters(), translatedTypeArguments, allowAlpha: true);
return typeMap.SubstituteNamedType(translatedOriginalDef);
}
Debug.Assert(type.IsDefinition);
if (type.IsAnonymousType)
{
return this.Visit(AnonymousTypeManager.TranslateAnonymousTypeSymbol(type));
}
return type;
}
public override Symbol VisitPointerType(PointerTypeSymbol symbol)
{
var translatedPointedAtType = (TypeSymbol)this.Visit(symbol.PointedAtType);
var translatedModifiers = VisitCustomModifiers(symbol.PointedAtTypeWithAnnotations.CustomModifiers);
return new PointerTypeSymbol(symbol.PointedAtTypeWithAnnotations.WithTypeAndModifiers(translatedPointedAtType, translatedModifiers));
}
public override Symbol VisitFunctionPointerType(FunctionPointerTypeSymbol symbol)
{
var sig = symbol.Signature;
var translatedReturnType = (TypeSymbol)Visit(sig.ReturnType);
var translatedReturnTypeWithAnnotations = sig.ReturnTypeWithAnnotations.WithTypeAndModifiers(translatedReturnType, VisitCustomModifiers(sig.ReturnTypeWithAnnotations.CustomModifiers));
var translatedRefCustomModifiers = VisitCustomModifiers(sig.RefCustomModifiers);
var translatedParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> translatedParamRefCustomModifiers = default;
if (sig.ParameterCount > 0)
{
var translatedParamsBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(sig.ParameterCount);
var translatedParamRefCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(sig.ParameterCount);
foreach (var param in sig.Parameters)
{
var translatedParamType = (TypeSymbol)Visit(param.Type);
translatedParamsBuilder.Add(param.TypeWithAnnotations.WithTypeAndModifiers(translatedParamType, VisitCustomModifiers(param.TypeWithAnnotations.CustomModifiers)));
translatedParamRefCustomModifiersBuilder.Add(VisitCustomModifiers(param.RefCustomModifiers));
}
translatedParameterTypes = translatedParamsBuilder.ToImmutableAndFree();
translatedParamRefCustomModifiers = translatedParamRefCustomModifiersBuilder.ToImmutableAndFree();
}
return symbol.SubstituteTypeSymbol(translatedReturnTypeWithAnnotations, translatedParameterTypes, translatedRefCustomModifiers, translatedParamRefCustomModifiers);
}
public override Symbol VisitTypeParameter(TypeParameterSymbol symbol)
{
return symbol;
}
private ImmutableArray<CustomModifier> VisitCustomModifiers(ImmutableArray<CustomModifier> modifiers)
{
return modifiers.SelectAsArray(VisitCustomModifier);
}
private CustomModifier VisitCustomModifier(CustomModifier modifier)
{
var translatedType = (NamedTypeSymbol)this.Visit(((CSharpCustomModifier)modifier).ModifierSymbol);
Debug.Assert((object)translatedType != null);
return modifier.IsOptional ?
CSharpCustomModifier.CreateOptional(translatedType) :
CSharpCustomModifier.CreateRequired(translatedType);
}
}
}
}
|