|
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Metadata;
using Microsoft.CodeAnalysis.Symbols;
namespace Microsoft.CodeAnalysis.Emit.NoPia
{
internal abstract partial class EmbeddedTypesManager<
TPEModuleBuilder,
TModuleCompilationState,
TEmbeddedTypesManager,
TSyntaxNode,
TAttributeData,
TSymbol,
TAssemblySymbol,
TNamedTypeSymbol,
TFieldSymbol,
TMethodSymbol,
TEventSymbol,
TPropertySymbol,
TParameterSymbol,
TTypeParameterSymbol,
TEmbeddedType,
TEmbeddedField,
TEmbeddedMethod,
TEmbeddedEvent,
TEmbeddedProperty,
TEmbeddedParameter,
TEmbeddedTypeParameter>
{
internal abstract class CommonEmbeddedType : Cci.IEmbeddedDefinition, Cci.INamespaceTypeDefinition
{
public readonly TEmbeddedTypesManager TypeManager;
public readonly TNamedTypeSymbol UnderlyingNamedType;
private ImmutableArray<Cci.IFieldDefinition> _lazyFields;
private ImmutableArray<Cci.IMethodDefinition> _lazyMethods;
private ImmutableArray<Cci.IPropertyDefinition> _lazyProperties;
private ImmutableArray<Cci.IEventDefinition> _lazyEvents;
private ImmutableArray<TAttributeData> _lazyAttributes;
private int _lazyAssemblyRefIndex = -1;
protected CommonEmbeddedType(TEmbeddedTypesManager typeManager, TNamedTypeSymbol underlyingNamedType)
{
this.TypeManager = typeManager;
this.UnderlyingNamedType = underlyingNamedType;
}
public bool IsEncDeleted
=> false;
protected abstract int GetAssemblyRefIndex();
protected abstract IEnumerable<TFieldSymbol> GetFieldsToEmit();
protected abstract IEnumerable<TMethodSymbol> GetMethodsToEmit();
protected abstract IEnumerable<TEventSymbol> GetEventsToEmit();
protected abstract IEnumerable<TPropertySymbol> GetPropertiesToEmit();
protected abstract bool IsPublic { get; }
protected abstract bool IsAbstract { get; }
protected abstract Cci.ITypeReference GetBaseClass(TPEModuleBuilder moduleBuilder, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
protected abstract IEnumerable<Cci.TypeReferenceWithAttributes> GetInterfaces(EmitContext context);
protected abstract bool IsBeforeFieldInit { get; }
protected abstract bool IsComImport { get; }
protected abstract bool IsInterface { get; }
protected abstract bool IsDelegate { get; }
protected abstract bool IsSerializable { get; }
protected abstract bool IsSpecialName { get; }
protected abstract bool IsWindowsRuntimeImport { get; }
protected abstract bool IsSealed { get; }
protected abstract TypeLayout? GetTypeLayoutIfStruct();
protected abstract System.Runtime.InteropServices.CharSet StringFormat { get; }
protected abstract TAttributeData CreateTypeIdentifierAttribute(bool hasGuid, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
protected abstract void EmbedDefaultMembers(string defaultMember, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
protected abstract IEnumerable<TAttributeData> GetCustomAttributesToEmit(TPEModuleBuilder moduleBuilder);
protected abstract void ReportMissingAttribute(AttributeDescription description, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics);
private bool IsTargetAttribute(TAttributeData attrData, AttributeDescription description, out int signatureIndex)
{
return TypeManager.IsTargetAttribute(attrData, description, out signatureIndex);
}
private ImmutableArray<TAttributeData> GetAttributes(TPEModuleBuilder moduleBuilder, TSyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
{
var builder = ArrayBuilder<TAttributeData>.GetInstance();
// Put the CompilerGenerated attribute on the NoPIA types we define so that
// static analysis tools (e.g. fxcop) know that they can be skipped
builder.AddOptional(TypeManager.CreateCompilerGeneratedAttribute());
// Copy some of the attributes.
bool hasGuid = false;
bool hasComEventInterfaceAttribute = false;
// Note, when porting attributes, we are not using constructors from original symbol.
// The constructors might be missing (for example, in metadata case) and doing lookup
// will ensure that we report appropriate errors.
foreach (var attrData in GetCustomAttributesToEmit(moduleBuilder))
{
int signatureIndex;
ImmutableArray<TypedConstant> constructorArguments;
ImmutableArray<KeyValuePair<string, TypedConstant>> namedArguments;
if (IsTargetAttribute(attrData, AttributeDescription.GuidAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
// If this type has a GuidAttribute, we should emit it.
hasGuid = true;
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Runtime_InteropServices_GuidAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.ComEventInterfaceAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
hasComEventInterfaceAttribute = true;
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Runtime_InteropServices_ComEventInterfaceAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.InterfaceTypeAttribute, out signatureIndex))
{
if ((signatureIndex == 0 || signatureIndex == 1) && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(signatureIndex == 0 ? WellKnownMember.System_Runtime_InteropServices_InterfaceTypeAttribute__ctorInt16 :
WellKnownMember.System_Runtime_InteropServices_InterfaceTypeAttribute__ctorComInterfaceType,
constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.BestFitMappingAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Runtime_InteropServices_BestFitMappingAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.CoClassAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Runtime_InteropServices_CoClassAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.FlagsAttribute, out signatureIndex))
{
if (UnderlyingNamedType.IsEnum && signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_FlagsAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.DefaultMemberAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Reflection_DefaultMemberAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
// Embed members matching default member name.
string defaultMember = constructorArguments[0].ValueInternal as string;
if (defaultMember != null)
{
EmbedDefaultMembers(defaultMember, syntaxNodeOpt, diagnostics);
}
}
}
else if (IsTargetAttribute(attrData, AttributeDescription.UnmanagedFunctionPointerAttribute, out signatureIndex))
{
if (signatureIndex == 0 && TypeManager.TryGetAttributeArguments(attrData, out constructorArguments, out namedArguments, syntaxNodeOpt, diagnostics))
{
builder.AddOptional(TypeManager.CreateSynthesizedAttribute(WellKnownMember.System_Runtime_InteropServices_UnmanagedFunctionPointerAttribute__ctor, constructorArguments, namedArguments, syntaxNodeOpt, diagnostics));
}
}
}
// We must emit a TypeIdentifier attribute which connects this local type with the canonical type.
// Interfaces usually have a guid attribute, in which case the TypeIdentifier attribute we emit will
// not need any additional parameters. For interfaces which lack a guid and all other types, we must
// emit a TypeIdentifier that has parameters identifying the scope and name of the original type. We
// will use the Assembly GUID as the scope identifier.
if (IsInterface && !hasComEventInterfaceAttribute)
{
if (!IsComImport)
{
// If we have an interface not marked ComImport, but the assembly is linked, then
// we need to give an error. We allow event interfaces to not have ComImport marked on them.
// ERRID_NoPIAAttributeMissing2/ERR_InteropTypeMissingAttribute
ReportMissingAttribute(AttributeDescription.ComImportAttribute, syntaxNodeOpt, diagnostics);
}
else if (!hasGuid)
{
// Interfaces used with No-PIA ought to have a guid attribute, or the CLR cannot do type unification.
// This interface lacks a guid, so unification probably won't work. We allow event interfaces to not have a Guid.
// ERRID_NoPIAAttributeMissing2/ERR_InteropTypeMissingAttribute
ReportMissingAttribute(AttributeDescription.GuidAttribute, syntaxNodeOpt, diagnostics);
}
}
// Note, this logic should match the one in RetargetingSymbolTranslator.RetargetNoPiaLocalType
// when we try to predict what attributes we will emit on embedded type, which corresponds the
// type we are retargeting.
builder.AddOptional(CreateTypeIdentifierAttribute(hasGuid && IsInterface, syntaxNodeOpt, diagnostics));
return builder.ToImmutableAndFree();
}
public int AssemblyRefIndex
{
get
{
if (_lazyAssemblyRefIndex == -1)
{
_lazyAssemblyRefIndex = GetAssemblyRefIndex();
Debug.Assert(_lazyAssemblyRefIndex >= 0);
}
return _lazyAssemblyRefIndex;
}
}
bool Cci.INamespaceTypeDefinition.IsPublic
{
get
{
return IsPublic;
}
}
Cci.ITypeReference Cci.ITypeDefinition.GetBaseClass(EmitContext context)
{
return GetBaseClass((TPEModuleBuilder)context.Module, (TSyntaxNode)context.SyntaxNode, context.Diagnostics);
}
IEnumerable<Cci.IEventDefinition> Cci.ITypeDefinition.GetEvents(EmitContext context)
{
if (_lazyEvents.IsDefault)
{
Debug.Assert(TypeManager.IsFrozen);
var builder = ArrayBuilder<Cci.IEventDefinition>.GetInstance();
foreach (var e in GetEventsToEmit())
{
TEmbeddedEvent embedded;
if (TypeManager.EmbeddedEventsMap.TryGetValue(e, out embedded))
{
builder.Add(embedded);
}
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyEvents, builder.ToImmutableAndFree());
}
return _lazyEvents;
}
IEnumerable<Cci.MethodImplementation> Cci.ITypeDefinition.GetExplicitImplementationOverrides(EmitContext context)
{
return SpecializedCollections.EmptyEnumerable<Cci.MethodImplementation>();
}
IEnumerable<Cci.IFieldDefinition> Cci.ITypeDefinition.GetFields(EmitContext context)
{
if (_lazyFields.IsDefault)
{
Debug.Assert(TypeManager.IsFrozen);
var builder = ArrayBuilder<Cci.IFieldDefinition>.GetInstance();
foreach (var f in GetFieldsToEmit())
{
TEmbeddedField embedded;
if (TypeManager.EmbeddedFieldsMap.TryGetValue(f, out embedded))
{
builder.Add(embedded);
}
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyFields, builder.ToImmutableAndFree());
}
return _lazyFields;
}
IEnumerable<Cci.IGenericTypeParameter> Cci.ITypeDefinition.GenericParameters
{
get
{
return SpecializedCollections.EmptyEnumerable<Cci.IGenericTypeParameter>();
}
}
ushort Cci.ITypeDefinition.GenericParameterCount
{
get
{
return 0;
}
}
bool Cci.ITypeDefinition.HasDeclarativeSecurity
{
get
{
// None of the transferrable attributes are security attributes.
return false;
}
}
IEnumerable<Cci.TypeReferenceWithAttributes> Cci.ITypeDefinition.Interfaces(EmitContext context)
{
return GetInterfaces(context);
}
bool Cci.ITypeDefinition.IsAbstract
{
get
{
return IsAbstract;
}
}
bool Cci.ITypeDefinition.IsBeforeFieldInit
{
get
{
return IsBeforeFieldInit;
}
}
bool Cci.ITypeDefinition.IsComObject
{
get
{
return IsInterface || IsComImport;
}
}
bool Cci.ITypeDefinition.IsGeneric
{
get
{
return false;
}
}
bool Cci.ITypeDefinition.IsInterface
{
get
{
return IsInterface;
}
}
bool Cci.ITypeDefinition.IsDelegate
{
get
{
return IsDelegate;
}
}
bool Cci.ITypeDefinition.IsRuntimeSpecial
{
get
{
return false;
}
}
bool Cci.ITypeDefinition.IsSerializable
{
get
{
return IsSerializable;
}
}
bool Cci.ITypeDefinition.IsSpecialName
{
get
{
return IsSpecialName;
}
}
bool Cci.ITypeDefinition.IsWindowsRuntimeImport
{
get
{
return IsWindowsRuntimeImport;
}
}
bool Cci.ITypeDefinition.IsSealed
{
get
{
return IsSealed;
}
}
System.Runtime.InteropServices.LayoutKind Cci.ITypeDefinition.Layout
{
get
{
var layout = GetTypeLayoutIfStruct();
return layout?.Kind ?? System.Runtime.InteropServices.LayoutKind.Auto;
}
}
ushort Cci.ITypeDefinition.Alignment
{
get
{
var layout = GetTypeLayoutIfStruct();
return (ushort)(layout?.Alignment ?? 0);
}
}
uint Cci.ITypeDefinition.SizeOf
{
get
{
var layout = GetTypeLayoutIfStruct();
return (uint)(layout?.Size ?? 0);
}
}
IEnumerable<Cci.IMethodDefinition> Cci.ITypeDefinition.GetMethods(EmitContext context)
{
if (_lazyMethods.IsDefault)
{
Debug.Assert(TypeManager.IsFrozen);
var builder = ArrayBuilder<Cci.IMethodDefinition>.GetInstance();
int gapIndex = 1;
int gapSize = 0;
foreach (var method in GetMethodsToEmit())
{
if ((object)method != null)
{
TEmbeddedMethod embedded;
if (TypeManager.EmbeddedMethodsMap.TryGetValue(method, out embedded))
{
if (gapSize > 0)
{
builder.Add(new VtblGap(this, ModuleExtensions.GetVTableGapName(gapIndex, gapSize)));
gapIndex++;
gapSize = 0;
}
builder.Add(embedded);
}
else
{
gapSize++;
}
}
else
{
gapSize++;
}
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyMethods, builder.ToImmutableAndFree());
}
return _lazyMethods;
}
IEnumerable<Cci.INestedTypeDefinition> Cci.ITypeDefinition.GetNestedTypes(EmitContext context)
{
return SpecializedCollections.EmptyEnumerable<Cci.INestedTypeDefinition>();
}
IEnumerable<Cci.IPropertyDefinition> Cci.ITypeDefinition.GetProperties(EmitContext context)
{
if (_lazyProperties.IsDefault)
{
Debug.Assert(TypeManager.IsFrozen);
var builder = ArrayBuilder<Cci.IPropertyDefinition>.GetInstance();
foreach (var p in GetPropertiesToEmit())
{
TEmbeddedProperty embedded;
if (TypeManager.EmbeddedPropertiesMap.TryGetValue(p, out embedded))
{
builder.Add(embedded);
}
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyProperties, builder.ToImmutableAndFree());
}
return _lazyProperties;
}
IEnumerable<Cci.SecurityAttribute> Cci.ITypeDefinition.SecurityAttributes
{
get
{
// None of the transferrable attributes are security attributes.
return SpecializedCollections.EmptyEnumerable<Cci.SecurityAttribute>();
}
}
System.Runtime.InteropServices.CharSet Cci.ITypeDefinition.StringFormat
{
get
{
return StringFormat;
}
}
IEnumerable<Cci.ICustomAttribute> Cci.IReference.GetAttributes(EmitContext context)
{
if (_lazyAttributes.IsDefault)
{
var diagnostics = DiagnosticBag.GetInstance();
var attributes = GetAttributes((TPEModuleBuilder)context.Module, (TSyntaxNode)context.SyntaxNode, diagnostics);
if (ImmutableInterlocked.InterlockedInitialize(ref _lazyAttributes, attributes))
{
// Save any diagnostics that we encountered.
context.Diagnostics.AddRange(diagnostics);
}
diagnostics.Free();
}
return _lazyAttributes;
}
void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
{
throw ExceptionUtilities.Unreachable();
}
Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context)
{
return this;
}
CodeAnalysis.Symbols.ISymbolInternal Cci.IReference.GetInternalSymbol() => null;
bool Cci.ITypeReference.IsEnum
{
get
{
return UnderlyingNamedType.IsEnum;
}
}
bool Cci.ITypeReference.IsValueType
{
get
{
return UnderlyingNamedType.IsValueType;
}
}
Cci.ITypeDefinition Cci.ITypeReference.GetResolvedType(EmitContext context)
{
return this;
}
Cci.PrimitiveTypeCode Cci.ITypeReference.TypeCode
{
get
{
return Cci.PrimitiveTypeCode.NotPrimitive;
}
}
TypeDefinitionHandle Cci.ITypeReference.TypeDef
{
get
{
return default(TypeDefinitionHandle);
}
}
Cci.IGenericMethodParameterReference Cci.ITypeReference.AsGenericMethodParameterReference
{
get
{
return null;
}
}
Cci.IGenericTypeInstanceReference Cci.ITypeReference.AsGenericTypeInstanceReference
{
get
{
return null;
}
}
Cci.IGenericTypeParameterReference Cci.ITypeReference.AsGenericTypeParameterReference
{
get
{
return null;
}
}
Cci.INamespaceTypeDefinition Cci.ITypeReference.AsNamespaceTypeDefinition(EmitContext context)
{
return this;
}
Cci.INamespaceTypeReference Cci.ITypeReference.AsNamespaceTypeReference
{
get
{
return this;
}
}
Cci.INestedTypeDefinition Cci.ITypeReference.AsNestedTypeDefinition(EmitContext context)
{
return null;
}
Cci.INestedTypeReference Cci.ITypeReference.AsNestedTypeReference
{
get
{
return null;
}
}
Cci.ISpecializedNestedTypeReference Cci.ITypeReference.AsSpecializedNestedTypeReference
{
get
{
return null;
}
}
Cci.ITypeDefinition Cci.ITypeReference.AsTypeDefinition(EmitContext context)
{
return this;
}
ushort Cci.INamedTypeReference.GenericParameterCount
{
get
{
return 0;
}
}
bool Cci.INamedTypeReference.MangleName
{
get
{
return UnderlyingNamedType.MangleName;
}
}
#nullable enable
string? Cci.INamedTypeReference.AssociatedFileIdentifier
{
get
{
return UnderlyingNamedType.AssociatedFileIdentifier;
}
}
#nullable disable
string Cci.INamedEntity.Name
{
get
{
return UnderlyingNamedType.Name;
}
}
Cci.IUnitReference Cci.INamespaceTypeReference.GetUnit(EmitContext context)
{
return TypeManager.ModuleBeingBuilt;
}
string Cci.INamespaceTypeReference.NamespaceName
{
get
{
return UnderlyingNamedType.NamespaceName;
}
}
/// <remarks>
/// This is only used for testing.
/// </remarks>
public override string ToString()
{
return UnderlyingNamedType.GetInternalSymbol().GetISymbol().ToDisplayString(SymbolDisplayFormat.ILVisualizationFormat);
}
public sealed override bool Equals(object obj)
{
// It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
}
public sealed override int GetHashCode()
{
// It is not supported to rely on default equality of these Cci objects, an explicit way to compare and hash them should be used.
throw Roslyn.Utilities.ExceptionUtilities.Unreachable();
}
}
}
}
|