|
// 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.
#nullable disable
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting
{
internal enum RetargetOptions : byte
{
RetargetPrimitiveTypesByName = 0,
RetargetPrimitiveTypesByTypeCode = 1,
}
internal partial class RetargetingModuleSymbol
{
/// <summary>
/// Retargeting map from underlying module to this one.
/// </summary>
private readonly ConcurrentDictionary<Symbol, Symbol> _symbolMap =
new ConcurrentDictionary<Symbol, Symbol>(concurrencyLevel: 2, capacity: 4);
private readonly Func<Symbol, RetargetingMethodSymbol> _createRetargetingMethod;
private readonly Func<Symbol, RetargetingNamespaceSymbol> _createRetargetingNamespace;
private readonly Func<Symbol, RetargetingTypeParameterSymbol> _createRetargetingTypeParameter;
private readonly Func<Symbol, RetargetingNamedTypeSymbol> _createRetargetingNamedType;
private readonly Func<Symbol, FieldSymbol> _createRetargetingField;
private readonly Func<Symbol, RetargetingPropertySymbol> _createRetargetingProperty;
private readonly Func<Symbol, RetargetingEventSymbol> _createRetargetingEvent;
private RetargetingMethodSymbol CreateRetargetingMethod(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingMethodSymbol(this, (MethodSymbol)symbol);
}
private RetargetingNamespaceSymbol CreateRetargetingNamespace(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingNamespaceSymbol(this, (NamespaceSymbol)symbol);
}
private RetargetingNamedTypeSymbol CreateRetargetingNamedType(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingNamedTypeSymbol(this, (NamedTypeSymbol)symbol);
}
private FieldSymbol CreateRetargetingField(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
if (symbol is TupleErrorFieldSymbol tupleErrorField)
{
var correspondingTupleField = tupleErrorField.CorrespondingTupleField;
Debug.Assert(correspondingTupleField is TupleErrorFieldSymbol);
var retargetedCorrespondingDefaultFieldOpt = (correspondingTupleField == (object)tupleErrorField)
? null
: (TupleErrorFieldSymbol)RetargetingTranslator.Retarget(correspondingTupleField);
return new TupleErrorFieldSymbol(
RetargetingTranslator.Retarget(tupleErrorField.ContainingType, RetargetOptions.RetargetPrimitiveTypesByName),
tupleErrorField.Name,
tupleErrorField.TupleElementIndex,
tupleErrorField.TryGetFirstLocation(),
this.RetargetingTranslator.Retarget(tupleErrorField.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
tupleErrorField.GetUseSiteInfo().DiagnosticInfo,
tupleErrorField.IsImplicitlyDeclared,
retargetedCorrespondingDefaultFieldOpt);
}
return new RetargetingFieldSymbol(this, (FieldSymbol)symbol);
}
private RetargetingPropertySymbol CreateRetargetingProperty(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingPropertySymbol(this, (PropertySymbol)symbol);
}
private RetargetingEventSymbol CreateRetargetingEvent(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingEventSymbol(this, (EventSymbol)symbol);
}
private RetargetingTypeParameterSymbol CreateRetargetingTypeParameter(Symbol symbol)
{
Debug.Assert(ReferenceEquals(symbol.ContainingModule, _underlyingModule));
return new RetargetingTypeParameterSymbol(this, (TypeParameterSymbol)symbol);
}
internal class RetargetingSymbolTranslator
: CSharpSymbolVisitor<RetargetOptions, Symbol>
{
private readonly RetargetingModuleSymbol _retargetingModule;
public RetargetingSymbolTranslator(RetargetingModuleSymbol retargetingModule)
{
Debug.Assert((object)retargetingModule != null);
_retargetingModule = retargetingModule;
}
/// <summary>
/// Retargeting map from underlying module to the retargeting module.
/// </summary>
private ConcurrentDictionary<Symbol, Symbol> SymbolMap
{
get
{
return _retargetingModule._symbolMap;
}
}
/// <summary>
/// RetargetingAssemblySymbol owning retargetingModule.
/// </summary>
private RetargetingAssemblySymbol RetargetingAssembly
{
get
{
return _retargetingModule._retargetingAssembly;
}
}
/// <summary>
/// The underlying ModuleSymbol for retargetingModule.
/// </summary>
private SourceModuleSymbol UnderlyingModule
{
get
{
return _retargetingModule._underlyingModule;
}
}
/// <summary>
/// The map that captures information about what assembly should be retargeted
/// to what assembly. Key is the AssemblySymbol referenced by the underlying module,
/// value is the corresponding AssemblySymbol referenced by the retargeting module, and
/// corresponding retargeting map for symbols.
/// </summary>
private Dictionary<AssemblySymbol, DestinationData> RetargetingAssemblyMap
{
get
{
return _retargetingModule._retargetingAssemblyMap;
}
}
public Symbol Retarget(Symbol symbol)
{
Debug.Assert(symbol.Kind != SymbolKind.NamedType || ((NamedTypeSymbol)symbol).PrimitiveTypeCode == Cci.PrimitiveTypeCode.NotPrimitive);
return symbol.Accept(this, RetargetOptions.RetargetPrimitiveTypesByName);
}
public MarshalPseudoCustomAttributeData Retarget(MarshalPseudoCustomAttributeData marshallingInfo)
{
// Retarget by type code - primitive types are encoded in short form in an attribute signature:
return marshallingInfo?.WithTranslatedTypes<TypeSymbol, RetargetingSymbolTranslator>(
(type, translator) => translator.Retarget(type, RetargetOptions.RetargetPrimitiveTypesByTypeCode), this);
}
public TypeSymbol Retarget(TypeSymbol symbol, RetargetOptions options)
{
return (TypeSymbol)symbol.Accept(this, options);
}
public TypeWithAnnotations Retarget(TypeWithAnnotations underlyingType, RetargetOptions options, NamedTypeSymbol asDynamicIfNoPiaContainingType = null)
{
var newTypeSymbol = Retarget(underlyingType.Type, options);
if ((object)asDynamicIfNoPiaContainingType != null)
{
newTypeSymbol = newTypeSymbol.AsDynamicIfNoPia(asDynamicIfNoPiaContainingType);
}
bool modifiersHaveChanged;
var newModifiers = RetargetModifiers(underlyingType.CustomModifiers, out modifiersHaveChanged);
if (modifiersHaveChanged || !TypeSymbol.Equals(underlyingType.Type, newTypeSymbol, TypeCompareKind.ConsiderEverything2))
{
return underlyingType.WithTypeAndModifiers(newTypeSymbol, newModifiers);
}
return underlyingType;
}
public NamespaceSymbol Retarget(NamespaceSymbol ns)
{
return (NamespaceSymbol)this.SymbolMap.GetOrAdd(ns, _retargetingModule._createRetargetingNamespace);
}
private NamedTypeSymbol RetargetNamedTypeDefinition(NamedTypeSymbol type, RetargetOptions options)
{
Debug.Assert(type.IsDefinition);
if (type.IsNativeIntegerWrapperType)
{
var result = RetargetNamedTypeDefinition(type.NativeIntegerUnderlyingType, options);
return result.SpecialType == SpecialType.None ? result : result.AsNativeInteger();
}
// Before we do anything else, check if we need to do special retargeting
// for primitive type references encoded with enum values in metadata signatures.
if (options == RetargetOptions.RetargetPrimitiveTypesByTypeCode)
{
Cci.PrimitiveTypeCode typeCode = type.PrimitiveTypeCode;
if (typeCode != Cci.PrimitiveTypeCode.NotPrimitive)
{
return RetargetingAssembly.GetPrimitiveType(typeCode);
}
}
if (type.Kind == SymbolKind.ErrorType)
{
return Retarget((ErrorTypeSymbol)type);
}
AssemblySymbol retargetFrom = type.ContainingAssembly;
// Deal with "to be local" NoPia types leaking through source module.
// These are the types that are coming from assemblies linked (/l-ed)
// by the compilation that created the source module.
bool isLocalType;
if (ReferenceEquals(retargetFrom, this.RetargetingAssembly.UnderlyingAssembly))
{
Debug.Assert(!retargetFrom.IsLinked);
isLocalType = type.IsExplicitDefinitionOfNoPiaLocalType;
}
else
{
isLocalType = retargetFrom.IsLinked;
}
if (isLocalType)
{
return RetargetNoPiaLocalType(type);
}
// Perform general retargeting.
if (ReferenceEquals(retargetFrom, this.RetargetingAssembly.UnderlyingAssembly))
{
return RetargetNamedTypeDefinitionFromUnderlyingAssembly(type);
}
// Does this type come from one of the retargeted assemblies?
DestinationData destination;
if (!this.RetargetingAssemblyMap.TryGetValue(retargetFrom, out destination))
{
// No need to retarget
return type;
}
// Retarget from one assembly to another
type = PerformTypeRetargeting(ref destination, type);
this.RetargetingAssemblyMap[retargetFrom] = destination;
return type;
}
private NamedTypeSymbol RetargetNamedTypeDefinitionFromUnderlyingAssembly(NamedTypeSymbol type)
{
// The type is defined in the underlying assembly.
var module = type.ContainingModule;
if (ReferenceEquals(module, this.UnderlyingModule))
{
Debug.Assert(module.Ordinal == 0);
Debug.Assert(!type.IsExplicitDefinitionOfNoPiaLocalType);
var container = type.ContainingType;
while ((object)container != null)
{
if (container.IsExplicitDefinitionOfNoPiaLocalType)
{
// Types nested into local types are not supported.
return (NamedTypeSymbol)this.SymbolMap.GetOrAdd(type, new UnsupportedMetadataTypeSymbol());
}
container = container.ContainingType;
}
return (NamedTypeSymbol)this.SymbolMap.GetOrAdd(type, _retargetingModule._createRetargetingNamedType);
}
else
{
// The type is defined in one of the added modules
Debug.Assert(module.Ordinal > 0);
PEModuleSymbol addedModule = (PEModuleSymbol)this.RetargetingAssembly.Modules[module.Ordinal];
Debug.Assert(ReferenceEquals(((PEModuleSymbol)module).Module, addedModule.Module));
return RetargetNamedTypeDefinition((PENamedTypeSymbol)type, addedModule);
}
}
private NamedTypeSymbol RetargetNoPiaLocalType(NamedTypeSymbol type)
{
NamedTypeSymbol cached;
var map = this.RetargetingAssembly.NoPiaUnificationMap;
if (map.TryGetValue(type, out cached))
{
return cached;
}
NamedTypeSymbol result;
if (type.ContainingSymbol.Kind != SymbolKind.NamedType &&
type.Arity == 0)
{
// Get type's identity
bool isInterface = type.IsInterface;
bool hasGuid = false;
string interfaceGuid = null;
string scope = null;
if (isInterface)
{
// Get type's Guid
hasGuid = type.GetGuidString(out interfaceGuid);
}
MetadataTypeName name = MetadataTypeName.FromFullName(type.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat), forcedArity: type.Arity);
string identifier = null;
if ((object)type.ContainingModule == (object)_retargetingModule.UnderlyingModule)
{
// This is a local type explicitly declared in source. Get information from TypeIdentifier attribute.
foreach (var attrData in type.GetAttributes())
{
int signatureIndex = attrData.GetTargetAttributeSignatureIndex(AttributeDescription.TypeIdentifierAttribute);
if (signatureIndex != -1)
{
Debug.Assert(signatureIndex == 0 || signatureIndex == 1);
if (signatureIndex == 1 && attrData.CommonConstructorArguments.Length == 2)
{
scope = attrData.CommonConstructorArguments[0].ValueInternal as string;
identifier = attrData.CommonConstructorArguments[1].ValueInternal as string;
}
break;
}
}
}
else
{
Debug.Assert((object)type.ContainingAssembly != (object)RetargetingAssembly.UnderlyingAssembly);
// Note, this logic should match the one in EmbeddedType.Cci.IReference.GetAttributes.
// Here we are trying to predict what attributes we will emit on embedded type, which corresponds the
// type we are retargeting. That function actually emits the attributes.
if (!(hasGuid && isInterface))
{
type.ContainingAssembly.GetGuidString(out scope);
identifier = name.FullName;
}
}
result = MetadataDecoder.SubstituteNoPiaLocalType(
ref name,
isInterface,
type.BaseTypeNoUseSiteDiagnostics,
interfaceGuid,
scope,
identifier,
RetargetingAssembly);
Debug.Assert((object)result != null);
}
else
{
// TODO: report better error?
result = new UnsupportedMetadataTypeSymbol();
}
cached = map.GetOrAdd(type, result);
return cached;
}
#nullable enable
private static NamedTypeSymbol RetargetNamedTypeDefinition(PENamedTypeSymbol type, PEModuleSymbol addedModule)
{
Debug.Assert(!type.ContainingModule.Equals(addedModule) &&
ReferenceEquals(((PEModuleSymbol)type.ContainingModule).Module, addedModule.Module));
TypeSymbol? cached;
if (addedModule.TypeHandleToTypeMap.TryGetValue(type.Handle, out cached))
{
return (NamedTypeSymbol)cached;
}
NamedTypeSymbol? result;
NamedTypeSymbol containingType = type.ContainingType;
MetadataTypeName mdName;
if ((object)containingType != null)
{
// Nested type. We need to retarget
// the enclosing type and then go back and get the type we are interested in.
NamedTypeSymbol scope = RetargetNamedTypeDefinition((PENamedTypeSymbol)containingType, addedModule);
mdName = MetadataTypeName.FromTypeName(type.MetadataName, forcedArity: type.Arity);
result = scope.LookupMetadataType(ref mdName);
}
else
{
string namespaceName = type.ContainingNamespace.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat);
mdName = MetadataTypeName.FromNamespaceAndTypeName(namespaceName, type.MetadataName, forcedArity: type.Arity);
result = addedModule.LookupTopLevelMetadataType(ref mdName);
}
Debug.Assert(result is PENamedTypeSymbol peResult && peResult.Handle == type.Handle);
return result;
}
private static NamedTypeSymbol PerformTypeRetargeting(
ref DestinationData destination,
NamedTypeSymbol type)
{
NamedTypeSymbol? result;
if (!destination.SymbolMap.TryGetValue(type, out result))
{
// Lookup by name as a TypeRef.
NamedTypeSymbol containingType = type.ContainingType;
NamedTypeSymbol? result1;
MetadataTypeName mdName;
if ((object)containingType != null)
{
// This happens if type is a nested class. We need to retarget
// the enclosing class and then go back and get the type we are interested in.
NamedTypeSymbol scope = PerformTypeRetargeting(ref destination, containingType);
mdName = MetadataTypeName.FromTypeName(type.MetadataName, forcedArity: type.Arity);
result1 = scope.LookupMetadataType(ref mdName);
Debug.Assert(result1?.IsErrorType() != true);
result1 ??= new MissingMetadataTypeSymbol.Nested(scope, ref mdName);
}
else
{
string namespaceName = type.ContainingNamespace.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat);
mdName = MetadataTypeName.FromNamespaceAndTypeName(namespaceName, type.MetadataName, forcedArity: type.Arity);
result1 = destination.To.LookupDeclaredOrForwardedTopLevelMetadataType(ref mdName, visitedAssemblies: null);
}
Debug.Assert(result1.Arity == type.Arity);
result = destination.SymbolMap.GetOrAdd(type, result1);
Debug.Assert(TypeSymbol.Equals(result1, result, TypeCompareKind.ConsiderEverything2));
}
return result;
}
#nullable disable
public NamedTypeSymbol Retarget(NamedTypeSymbol type, RetargetOptions options)
{
NamedTypeSymbol originalDefinition = type.OriginalDefinition;
NamedTypeSymbol newDefinition = RetargetNamedTypeDefinition(originalDefinition, options);
if (ReferenceEquals(type, originalDefinition))
{
return newDefinition;
}
if (newDefinition.Kind == SymbolKind.ErrorType && !newDefinition.IsGenericType)
{
return newDefinition;
}
Debug.Assert(originalDefinition.Arity == 0 || !ReferenceEquals(type.ConstructedFrom, type));
if (type.IsUnboundGenericType)
{
if (ReferenceEquals(newDefinition, originalDefinition))
{
return type;
}
return newDefinition.AsUnboundGenericType();
}
Debug.Assert((object)type.ContainingType == null || !type.ContainingType.IsUnboundGenericType());
// This must be a generic instantiation (i.e. constructed type).
NamedTypeSymbol genericType = type;
var oldArguments = ArrayBuilder<TypeWithAnnotations>.GetInstance();
int startOfNonInterfaceArguments = int.MaxValue;
// Collect generic arguments for the type and its containers.
while ((object)genericType != null)
{
if (startOfNonInterfaceArguments == int.MaxValue &&
!genericType.IsInterface)
{
startOfNonInterfaceArguments = oldArguments.Count;
}
oldArguments.AddRange(genericType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics);
genericType = genericType.ContainingType;
}
bool anythingRetargeted = !originalDefinition.Equals(newDefinition);
// retarget the arguments
var newArguments = ArrayBuilder<TypeWithAnnotations>.GetInstance(oldArguments.Count);
foreach (var arg in oldArguments)
{
var newArg = Retarget(arg, RetargetOptions.RetargetPrimitiveTypesByTypeCode); // generic instantiation is a signature
if (!anythingRetargeted && !newArg.IsSameAs(arg))
{
anythingRetargeted = true;
}
newArguments.Add(newArg);
}
// See if it is or its enclosing type is a non-interface closed over NoPia local types.
bool noPiaIllegalGenericInstantiation = IsNoPiaIllegalGenericInstantiation(oldArguments, newArguments, startOfNonInterfaceArguments);
oldArguments.Free();
NamedTypeSymbol constructedType;
if (!anythingRetargeted)
{
// Nothing was retargeted, return original type symbol.
constructedType = type;
}
else
{
// Create symbol for new constructed type and return it.
// need to collect type parameters in the same order as we have arguments,
// but this should be done for the new definition.
genericType = newDefinition;
ArrayBuilder<TypeParameterSymbol> newParameters = ArrayBuilder<TypeParameterSymbol>.GetInstance(newArguments.Count);
// Collect generic arguments for the type and its containers.
while ((object)genericType != null)
{
if (genericType.Arity > 0)
{
newParameters.AddRange(genericType.TypeParameters);
}
genericType = genericType.ContainingType;
}
Debug.Assert(newParameters.Count == newArguments.Count);
TypeMap substitution = new TypeMap(newParameters.ToImmutableAndFree(), newArguments.ToImmutable());
constructedType = substitution.SubstituteNamedType(newDefinition).WithTupleDataFrom(type);
}
newArguments.Free();
if (noPiaIllegalGenericInstantiation)
{
return new NoPiaIllegalGenericInstantiationSymbol(_retargetingModule, constructedType);
}
return constructedType;
}
private bool IsNoPiaIllegalGenericInstantiation(ArrayBuilder<TypeWithAnnotations> oldArguments, ArrayBuilder<TypeWithAnnotations> newArguments, int startOfNonInterfaceArguments)
{
// TODO: Do we need to check constraints on type parameters as well?
if (this.UnderlyingModule.ContainsExplicitDefinitionOfNoPiaLocalTypes)
{
for (int i = startOfNonInterfaceArguments; i < oldArguments.Count; i++)
{
if (IsOrClosedOverAnExplicitLocalType(oldArguments[i].Type))
{
return true;
}
}
}
ImmutableArray<AssemblySymbol> assembliesToEmbedTypesFrom = this.UnderlyingModule.GetAssembliesToEmbedTypesFrom();
if (assembliesToEmbedTypesFrom.Length > 0)
{
for (int i = startOfNonInterfaceArguments; i < oldArguments.Count; i++)
{
if (MetadataDecoder.IsOrClosedOverATypeFromAssemblies(oldArguments[i].Type, assembliesToEmbedTypesFrom))
{
return true;
}
}
}
ImmutableArray<AssemblySymbol> linkedAssemblies = RetargetingAssembly.GetLinkedReferencedAssemblies();
if (!linkedAssemblies.IsDefaultOrEmpty)
{
for (int i = startOfNonInterfaceArguments; i < newArguments.Count; i++)
{
if (MetadataDecoder.IsOrClosedOverATypeFromAssemblies(newArguments[i].Type, linkedAssemblies))
{
return true;
}
}
}
return false;
}
/// <summary>
/// Perform a check whether the type or at least one of its generic arguments
/// is an explicitly defined local type. The check is performed recursively.
/// </summary>
private bool IsOrClosedOverAnExplicitLocalType(TypeSymbol symbol)
{
switch (symbol.Kind)
{
case SymbolKind.TypeParameter:
return false;
case SymbolKind.ArrayType:
return IsOrClosedOverAnExplicitLocalType(((ArrayTypeSymbol)symbol).ElementType);
case SymbolKind.PointerType:
return IsOrClosedOverAnExplicitLocalType(((PointerTypeSymbol)symbol).PointedAtType);
case SymbolKind.DynamicType:
return false;
case SymbolKind.ErrorType:
case SymbolKind.NamedType:
var namedType = (NamedTypeSymbol)symbol;
if ((object)symbol.OriginalDefinition.ContainingModule == (object)_retargetingModule.UnderlyingModule &&
namedType.IsExplicitDefinitionOfNoPiaLocalType)
{
return true;
}
do
{
foreach (var argument in namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics)
{
if (IsOrClosedOverAnExplicitLocalType(argument.Type))
{
return true;
}
}
namedType = namedType.ContainingType;
}
while ((object)namedType != null);
return false;
default:
throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
}
}
public virtual TypeParameterSymbol Retarget(TypeParameterSymbol typeParameter)
{
return (TypeParameterSymbol)this.SymbolMap.GetOrAdd(typeParameter, _retargetingModule._createRetargetingTypeParameter);
}
public ArrayTypeSymbol Retarget(ArrayTypeSymbol type)
{
TypeWithAnnotations oldElement = type.ElementTypeWithAnnotations;
TypeWithAnnotations newElement = Retarget(oldElement, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
if (oldElement.IsSameAs(newElement))
{
return type;
}
if (type.IsSZArray)
{
return ArrayTypeSymbol.CreateSZArray(this.RetargetingAssembly, newElement);
}
return ArrayTypeSymbol.CreateMDArray(this.RetargetingAssembly, newElement, type.Rank, type.Sizes, type.LowerBounds);
}
internal ImmutableArray<CustomModifier> RetargetModifiers(ImmutableArray<CustomModifier> oldModifiers, out bool modifiersHaveChanged)
{
ArrayBuilder<CustomModifier> newModifiers = null;
for (int i = 0; i < oldModifiers.Length; i++)
{
var oldModifier = oldModifiers[i];
NamedTypeSymbol oldModifierSymbol = ((CSharpCustomModifier)oldModifier).ModifierSymbol;
NamedTypeSymbol newModifierSymbol = Retarget(oldModifierSymbol, RetargetOptions.RetargetPrimitiveTypesByName); // should be retargeted by name
if (!newModifierSymbol.Equals(oldModifierSymbol))
{
if (newModifiers == null)
{
newModifiers = ArrayBuilder<CustomModifier>.GetInstance(oldModifiers.Length);
newModifiers.AddRange(oldModifiers, i);
}
newModifiers.Add(oldModifier.IsOptional ?
CSharpCustomModifier.CreateOptional(newModifierSymbol) :
CSharpCustomModifier.CreateRequired(newModifierSymbol));
}
else if (newModifiers != null)
{
newModifiers.Add(oldModifier);
}
}
Debug.Assert(newModifiers == null || newModifiers.Count == oldModifiers.Length);
modifiersHaveChanged = (newModifiers != null);
return modifiersHaveChanged ? newModifiers.ToImmutableAndFree() : oldModifiers;
}
public PointerTypeSymbol Retarget(PointerTypeSymbol type)
{
TypeWithAnnotations oldPointed = type.PointedAtTypeWithAnnotations;
TypeWithAnnotations newPointed = Retarget(oldPointed, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
if (oldPointed.IsSameAs(newPointed))
{
return type;
}
return new PointerTypeSymbol(newPointed);
}
public FunctionPointerTypeSymbol Retarget(FunctionPointerTypeSymbol type)
{
var signature = type.Signature;
var newReturn = Retarget(signature.ReturnTypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
var newRefModifiers = RetargetModifiers(signature.RefCustomModifiers, out bool symbolModified);
symbolModified = symbolModified || !signature.ReturnTypeWithAnnotations.IsSameAs(newReturn);
var newParameterTypes = ImmutableArray<TypeWithAnnotations>.Empty;
ImmutableArray<ImmutableArray<CustomModifier>> newParamModifiers = default;
var paramCount = signature.ParameterCount;
if (paramCount > 0)
{
var newParameterTypesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance(paramCount);
var newParameterCustomModifiersBuilder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(paramCount);
bool parametersModified = false;
foreach (var parameter in signature.Parameters)
{
var newParameterType = Retarget(parameter.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
var newModifiers = RetargetModifiers(parameter.RefCustomModifiers, out bool customModifiersChanged);
newParameterTypesBuilder.Add(newParameterType);
newParameterCustomModifiersBuilder.Add(newModifiers);
parametersModified = parametersModified || !parameter.TypeWithAnnotations.IsSameAs(newParameterType) || customModifiersChanged;
}
if (parametersModified)
{
newParameterTypes = newParameterTypesBuilder.ToImmutableAndFree();
newParamModifiers = newParameterCustomModifiersBuilder.ToImmutableAndFree();
symbolModified = true;
}
else
{
newParameterTypesBuilder.Free();
newParameterCustomModifiersBuilder.Free();
newParameterTypes = signature.ParameterTypesWithAnnotations;
}
}
if (symbolModified)
{
return type.SubstituteTypeSymbol(newReturn, newParameterTypes, newRefModifiers, newParamModifiers);
}
else
{
return type;
}
}
public static ErrorTypeSymbol Retarget(ErrorTypeSymbol type)
{
// TODO: if it is a missing symbol error but no longer missing in the target assembly, then we can resolve it here.
var useSiteDiagnostic = type.GetUseSiteInfo().DiagnosticInfo;
if (useSiteDiagnostic?.Severity == DiagnosticSeverity.Error)
{
return type;
}
// A retargeted error symbol must trigger an error on use so that a dependent compilation won't
// improperly succeed. We therefore ensure we have a use-site diagnostic.
return
(type as ExtendedErrorTypeSymbol)?.AsUnreported() ?? // preserve diagnostic information if possible
new ExtendedErrorTypeSymbol(type, type.ResultKind,
type.ErrorInfo ?? new CSDiagnosticInfo(ErrorCode.ERR_ErrorInReferencedAssembly, type.ContainingAssembly?.Identity.GetDisplayName() ?? string.Empty), true);
}
public ImmutableArray<Symbol> Retarget(ImmutableArray<Symbol> arr)
{
return arr.SelectAsArray(
static (s, self) => self.Retarget(s),
this);
}
public ImmutableArray<NamedTypeSymbol> Retarget(ImmutableArray<NamedTypeSymbol> sequence)
{
return sequence.SelectAsArray(
static (nts, self) =>
{
// If there is an error type in the base type list, it will end up in the interface list (rather
// than as the base class), so it might end up passing through here. If it is specified using
// a primitive type keyword, then it will have a primitive type code, even if corlib is missing.
Debug.Assert(nts.TypeKind == TypeKind.Error || nts.PrimitiveTypeCode == Cci.PrimitiveTypeCode.NotPrimitive);
return self.Retarget(nts, RetargetOptions.RetargetPrimitiveTypesByName);
},
this);
}
public ImmutableArray<TypeSymbol> Retarget(ImmutableArray<TypeSymbol> sequence)
{
return sequence.SelectAsArray(
static (ts, self) =>
{
// In incorrect code, a type parameter constraint list can contain primitive types.
Debug.Assert(ts.TypeKind == TypeKind.Error || ts.PrimitiveTypeCode == Cci.PrimitiveTypeCode.NotPrimitive);
return self.Retarget(ts, RetargetOptions.RetargetPrimitiveTypesByName);
},
this);
}
public ImmutableArray<TypeWithAnnotations> Retarget(ImmutableArray<TypeWithAnnotations> sequence)
{
return sequence.SelectAsArray(
static (ts, self) => self.Retarget(ts, RetargetOptions.RetargetPrimitiveTypesByName),
this);
}
public ImmutableArray<TypeParameterSymbol> Retarget(ImmutableArray<TypeParameterSymbol> list)
{
return list.SelectAsArray(
static (tps, self) => self.Retarget(tps),
this);
}
public MethodSymbol Retarget(MethodSymbol method)
{
Debug.Assert(ReferenceEquals(method.ContainingModule, this.UnderlyingModule));
Debug.Assert(ReferenceEquals(method, method.OriginalDefinition));
return (MethodSymbol)this.SymbolMap.GetOrAdd(method, _retargetingModule._createRetargetingMethod);
}
public MethodSymbol Retarget(MethodSymbol method, IEqualityComparer<MethodSymbol> retargetedMethodComparer)
{
Debug.Assert((object)method == method.ConstructedFrom);
if (ReferenceEquals(method.ContainingModule, this.UnderlyingModule) && ReferenceEquals(method, method.OriginalDefinition))
{
return Retarget(method);
}
var containingType = method.ContainingType;
var retargetedType = Retarget(containingType, RetargetOptions.RetargetPrimitiveTypesByName);
if (ReferenceEquals(retargetedType, containingType))
{
return method;
}
if (!containingType.IsDefinition)
{
Debug.Assert(!retargetedType.IsDefinition);
var retargetedDefinition = Retarget(method.OriginalDefinition, retargetedMethodComparer);
if (retargetedDefinition is null)
{
return null;
}
return retargetedDefinition.AsMember(retargetedType);
}
Debug.Assert(retargetedType.IsDefinition);
// NB: may return null if the method cannot be found in the retargeted type (e.g. removed in a subsequent version)
return FindMethodInRetargetedType(method, retargetedType, retargetedMethodComparer);
}
public FieldSymbol Retarget(FieldSymbol field)
{
return (FieldSymbol)this.SymbolMap.GetOrAdd(field, _retargetingModule._createRetargetingField);
}
public PropertySymbol Retarget(PropertySymbol property)
{
Debug.Assert(ReferenceEquals(property.ContainingModule, this.UnderlyingModule));
Debug.Assert(ReferenceEquals(property, property.OriginalDefinition));
return (PropertySymbol)this.SymbolMap.GetOrAdd(property, _retargetingModule._createRetargetingProperty);
}
public PropertySymbol Retarget(PropertySymbol property, IEqualityComparer<PropertySymbol> retargetedPropertyComparer)
{
if (ReferenceEquals(property.ContainingModule, this.UnderlyingModule) && ReferenceEquals(property, property.OriginalDefinition))
{
return Retarget(property);
}
var containingType = property.ContainingType;
var retargetedType = Retarget(containingType, RetargetOptions.RetargetPrimitiveTypesByName);
// NB: may return null if the property cannot be found in the retargeted type (e.g. removed in a subsequent version)
return ReferenceEquals(retargetedType, containingType) ?
property :
FindPropertyInRetargetedType(property, retargetedType, retargetedPropertyComparer);
}
public EventSymbol Retarget(EventSymbol @event)
{
if (ReferenceEquals(@event.ContainingModule, this.UnderlyingModule) && ReferenceEquals(@event, @event.OriginalDefinition))
{
return (EventSymbol)this.SymbolMap.GetOrAdd(@event, _retargetingModule._createRetargetingEvent);
}
var containingType = @event.ContainingType;
var retargetedType = Retarget(containingType, RetargetOptions.RetargetPrimitiveTypesByName);
// NB: may return null if the event cannot be found in the retargeted type (e.g. removed in a subsequent version)
return ReferenceEquals(retargetedType, containingType) ?
@event :
FindEventInRetargetedType(@event, retargetedType);
}
private MethodSymbol FindMethodInRetargetedType(MethodSymbol method, NamedTypeSymbol retargetedType, IEqualityComparer<MethodSymbol> retargetedMethodComparer)
{
return RetargetedTypeMethodFinder.Find(this, method, retargetedType, retargetedMethodComparer);
}
private class RetargetedTypeMethodFinder : RetargetingSymbolTranslator
{
private readonly NamedTypeSymbol _retargetedType;
private readonly MethodSymbol _toFind;
private RetargetedTypeMethodFinder(RetargetingModuleSymbol retargetingModule, NamedTypeSymbol retargetedType, MethodSymbol toFind) :
base(retargetingModule)
{
_retargetedType = retargetedType;
_toFind = toFind;
}
public static MethodSymbol Find(RetargetingSymbolTranslator translator, MethodSymbol method, NamedTypeSymbol retargetedType, IEqualityComparer<MethodSymbol> retargetedMethodComparer)
{
if (!method.IsGenericMethod && !retargetedType.IsGenericType)
{
return FindWorker(translator, method, retargetedType, retargetedMethodComparer);
}
// A generic method or a method in generic type needs special handling because its signature is very likely
// to refer to method's or type's type parameters.
var finder = new RetargetedTypeMethodFinder(translator._retargetingModule, retargetedType, method);
return FindWorker(finder, method, retargetedType, retargetedMethodComparer);
}
private static MethodSymbol FindWorker
(
RetargetingSymbolTranslator translator,
MethodSymbol method,
NamedTypeSymbol retargetedType,
IEqualityComparer<MethodSymbol> retargetedMethodComparer
)
{
var targetParams = method.Parameters.SelectAsArray(
static ParameterSymbol (param, translator) => new SignatureOnlyParameterSymbol(
translator.Retarget(param.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
translator.RetargetModifiers(param.RefCustomModifiers, modifiersHaveChanged: out _),
isParamsArray: param.IsParamsArray,
isParamsCollection: param.IsParamsCollection,
param.RefKind),
translator);
// We will be using this symbol only for the purpose of method signature comparison,
// IndexedTypeParameterSymbols should work just fine as the type parameters for the method.
// We can't produce "real" TypeParameterSymbols without finding the method first and this
// is what we are trying to do right now.
var targetMethod = new SignatureOnlyMethodSymbol(
method.Name,
retargetedType,
method.MethodKind,
method.CallingConvention,
IndexedTypeParameterSymbol.TakeSymbols(method.Arity),
targetParams,
method.RefKind,
method.IsInitOnly,
method.IsStatic,
translator.Retarget(method.ReturnTypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
translator.RetargetModifiers(method.RefCustomModifiers, modifiersHaveChanged: out _),
ImmutableArray<MethodSymbol>.Empty);
foreach (var retargetedMember in retargetedType.GetMembers(method.Name))
{
if (retargetedMember.Kind == SymbolKind.Method)
{
var retargetedMethod = (MethodSymbol)retargetedMember;
if (retargetedMethodComparer.Equals(retargetedMethod, targetMethod))
{
return retargetedMethod;
}
}
}
return null;
}
public override TypeParameterSymbol Retarget(TypeParameterSymbol typeParameter)
{
if (typeParameter.TypeParameterKind == TypeParameterKind.Method)
{
Debug.Assert((object)typeParameter.ContainingSymbol == _toFind);
// The method symbol we are building will be using IndexedTypeParameterSymbols as
// its type parameters, therefore, we should return them here as well.
return IndexedTypeParameterSymbol.GetTypeParameter(typeParameter.Ordinal);
}
NamedTypeSymbol containingType = _toFind.ContainingType;
NamedTypeSymbol retargetedContainingType = _retargetedType;
do
{
if ((object)containingType == typeParameter.ContainingSymbol)
{
return retargetedContainingType.TypeParameters[typeParameter.Ordinal];
}
containingType = containingType.ContainingType;
retargetedContainingType = retargetedContainingType.ContainingType;
}
while (containingType is object);
throw ExceptionUtilities.Unreachable();
}
}
private PropertySymbol FindPropertyInRetargetedType(PropertySymbol property, NamedTypeSymbol retargetedType, IEqualityComparer<PropertySymbol> retargetedPropertyComparer)
{
var targetParams = property.Parameters.SelectAsArray(
static ParameterSymbol (param, self) => new SignatureOnlyParameterSymbol(
self.Retarget(param.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
self.RetargetModifiers(param.RefCustomModifiers, modifiersHaveChanged: out _),
isParamsArray: param.IsParamsArray,
isParamsCollection: param.IsParamsCollection,
param.RefKind),
this);
var targetProperty = new SignatureOnlyPropertySymbol(
property.Name,
retargetedType,
targetParams,
property.RefKind,
Retarget(property.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode),
RetargetModifiers(property.RefCustomModifiers, modifiersHaveChanged: out _),
property.IsStatic,
ImmutableArray<PropertySymbol>.Empty);
foreach (var retargetedMember in retargetedType.GetMembers(property.Name))
{
if (retargetedMember.Kind == SymbolKind.Property)
{
var retargetedProperty = (PropertySymbol)retargetedMember;
if (retargetedPropertyComparer.Equals(retargetedProperty, targetProperty))
{
return retargetedProperty;
}
}
}
return null;
}
private EventSymbol FindEventInRetargetedType(EventSymbol @event, NamedTypeSymbol retargetedType)
{
var targetType = Retarget(@event.TypeWithAnnotations, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
foreach (var retargetedMember in retargetedType.GetMembers(@event.Name))
{
if (retargetedMember.Kind == SymbolKind.Event)
{
var retargetedEvent = (EventSymbol)retargetedMember;
if (TypeSymbol.Equals(retargetedEvent.Type, targetType.Type, TypeCompareKind.ConsiderEverything2))
{
return retargetedEvent;
}
}
}
return null;
}
internal ImmutableArray<CustomModifier> RetargetModifiers(
ImmutableArray<CustomModifier> oldModifiers,
ref ImmutableArray<CustomModifier> lazyCustomModifiers)
{
if (lazyCustomModifiers.IsDefault)
{
bool modifiersHaveChanged;
ImmutableArray<CustomModifier> newModifiers = this.RetargetModifiers(oldModifiers, out modifiersHaveChanged);
ImmutableInterlocked.InterlockedCompareExchange(ref lazyCustomModifiers, newModifiers, default(ImmutableArray<CustomModifier>));
}
return lazyCustomModifiers;
}
private ImmutableArray<CSharpAttributeData> RetargetAttributes(ImmutableArray<CSharpAttributeData> oldAttributes)
{
return oldAttributes.SelectAsArray((a, t) => t.RetargetAttributeData(a), this);
}
internal IEnumerable<CSharpAttributeData> RetargetAttributes(IEnumerable<CSharpAttributeData> attributes)
{
foreach (var attributeData in attributes)
{
yield return this.RetargetAttributeData(attributeData);
}
}
private CSharpAttributeData RetargetAttributeData(CSharpAttributeData oldAttributeData)
{
MethodSymbol oldAttributeCtor = oldAttributeData.AttributeConstructor;
MethodSymbol newAttributeCtor = (object)oldAttributeCtor == null ?
null :
Retarget(oldAttributeCtor, MemberSignatureComparer.RetargetedExplicitImplementationComparer);
NamedTypeSymbol oldAttributeType = oldAttributeData.AttributeClass;
NamedTypeSymbol newAttributeType;
if ((object)newAttributeCtor != null)
{
newAttributeType = newAttributeCtor.ContainingType;
}
else if ((object)oldAttributeType != null)
{
newAttributeType = Retarget(oldAttributeType, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
}
else
{
newAttributeType = null;
}
ImmutableArray<TypedConstant> oldAttributeCtorArguments = oldAttributeData.CommonConstructorArguments;
ImmutableArray<TypedConstant> newAttributeCtorArguments = RetargetAttributeConstructorArguments(oldAttributeCtorArguments);
ImmutableArray<KeyValuePair<string, TypedConstant>> oldAttributeNamedArguments = oldAttributeData.CommonNamedArguments;
ImmutableArray<KeyValuePair<string, TypedConstant>> newAttributeNamedArguments = RetargetAttributeNamedArguments(oldAttributeNamedArguments);
// Must create a RetargetingAttributeData even if the types and
// arguments are unchanged since the AttributeData instance is
// used to resolve System.Type which may require retargeting.
return new RetargetingAttributeData(
oldAttributeData,
newAttributeType,
newAttributeCtor,
newAttributeCtorArguments,
newAttributeNamedArguments);
}
private ImmutableArray<TypedConstant> RetargetAttributeConstructorArguments(ImmutableArray<TypedConstant> constructorArguments)
{
ImmutableArray<TypedConstant> retargetedArguments = constructorArguments;
bool argumentsHaveChanged = false;
if (!constructorArguments.IsDefault && constructorArguments.Any())
{
var newArguments = ArrayBuilder<TypedConstant>.GetInstance(constructorArguments.Length);
foreach (TypedConstant oldArgument in constructorArguments)
{
TypedConstant retargetedArgument = RetargetTypedConstant(oldArgument, ref argumentsHaveChanged);
newArguments.Add(retargetedArgument);
}
if (argumentsHaveChanged)
{
retargetedArguments = newArguments.ToImmutable();
}
newArguments.Free();
}
return retargetedArguments;
}
private TypedConstant RetargetTypedConstant(TypedConstant oldConstant, ref bool typedConstantChanged)
{
TypeSymbol oldConstantType = (TypeSymbol)oldConstant.TypeInternal;
TypeSymbol newConstantType = (object)oldConstantType == null ?
null :
Retarget(oldConstantType, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
if (oldConstant.Kind == TypedConstantKind.Array)
{
var newArray = RetargetAttributeConstructorArguments(oldConstant.Values);
if (!TypeSymbol.Equals(newConstantType, oldConstantType, TypeCompareKind.ConsiderEverything2) || newArray != oldConstant.Values)
{
typedConstantChanged = true;
return new TypedConstant(newConstantType, newArray);
}
else
{
return oldConstant;
}
}
object newConstantValue;
object oldConstantValue = oldConstant.ValueInternal;
if ((oldConstant.Kind == TypedConstantKind.Type) && (oldConstantValue != null))
{
newConstantValue = Retarget((TypeSymbol)oldConstantValue, RetargetOptions.RetargetPrimitiveTypesByTypeCode);
}
else
{
newConstantValue = oldConstantValue;
}
if (!TypeSymbol.Equals(newConstantType, oldConstantType, TypeCompareKind.ConsiderEverything2) || newConstantValue != oldConstantValue)
{
typedConstantChanged = true;
return new TypedConstant(newConstantType, oldConstant.Kind, newConstantValue);
}
else
{
return oldConstant;
}
}
private ImmutableArray<KeyValuePair<string, TypedConstant>> RetargetAttributeNamedArguments(ImmutableArray<KeyValuePair<string, TypedConstant>> namedArguments)
{
var retargetedArguments = namedArguments;
bool argumentsHaveChanged = false;
if (namedArguments.Any())
{
var newArguments = ArrayBuilder<KeyValuePair<string, TypedConstant>>.GetInstance(namedArguments.Length);
foreach (KeyValuePair<string, TypedConstant> oldArgument in namedArguments)
{
TypedConstant oldConstant = oldArgument.Value;
bool typedConstantChanged = false;
TypedConstant newConstant = RetargetTypedConstant(oldConstant, ref typedConstantChanged);
if (typedConstantChanged)
{
newArguments.Add(new KeyValuePair<string, TypedConstant>(oldArgument.Key, newConstant));
argumentsHaveChanged = true;
}
else
{
newArguments.Add(oldArgument);
}
}
if (argumentsHaveChanged)
{
retargetedArguments = newArguments.ToImmutable();
}
newArguments.Free();
}
return retargetedArguments;
}
// Get the retargeted attributes
internal ImmutableArray<CSharpAttributeData> GetRetargetedAttributes(
ImmutableArray<CSharpAttributeData> underlyingAttributes,
ref ImmutableArray<CSharpAttributeData> lazyCustomAttributes)
{
if (lazyCustomAttributes.IsDefault)
{
// Retarget the attributes
ImmutableArray<CSharpAttributeData> retargetedAttributes = this.RetargetAttributes(underlyingAttributes);
ImmutableInterlocked.InterlockedCompareExchange(ref lazyCustomAttributes, retargetedAttributes, default(ImmutableArray<CSharpAttributeData>));
}
return lazyCustomAttributes;
}
public override Symbol VisitModule(ModuleSymbol symbol, RetargetOptions options)
{
// We shouldn't run into any other module, but the underlying module
Debug.Assert(ReferenceEquals(symbol, _retargetingModule.UnderlyingModule));
return _retargetingModule;
}
public override Symbol VisitNamespace(NamespaceSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitNamedType(NamedTypeSymbol symbol, RetargetOptions options)
{
return Retarget(symbol, options);
}
public override Symbol VisitArrayType(ArrayTypeSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitPointerType(PointerTypeSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitFunctionPointerType(FunctionPointerTypeSymbol symbol, RetargetOptions argument)
{
return Retarget(symbol);
}
public override Symbol VisitMethod(MethodSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitParameter(ParameterSymbol symbol, RetargetOptions options)
{
throw ExceptionUtilities.Unreachable();
}
public override Symbol VisitField(FieldSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitProperty(PropertySymbol symbol, RetargetOptions argument)
{
return Retarget(symbol);
}
public override Symbol VisitTypeParameter(TypeParameterSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitErrorType(ErrorTypeSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitEvent(EventSymbol symbol, RetargetOptions options)
{
return Retarget(symbol);
}
public override Symbol VisitDynamicType(DynamicTypeSymbol symbol, RetargetOptions argument)
{
// TODO(cyrusn): What's the right thing to do here?
return symbol;
}
}
}
}
|