|
// 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.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE
{
/// <summary>
/// The class to represent all method parameters imported from a PE/module.
/// </summary>
internal class PEParameterSymbol : ParameterSymbol
{
[Flags]
private enum WellKnownAttributeFlags
{
HasIDispatchConstantAttribute = 0x1 << 0,
HasIUnknownConstantAttribute = 0x1 << 1,
HasCallerFilePathAttribute = 0x1 << 2,
HasCallerLineNumberAttribute = 0x1 << 3,
HasCallerMemberNameAttribute = 0x1 << 4,
IsCallerFilePath = 0x1 << 5,
IsCallerLineNumber = 0x1 << 6,
IsCallerMemberName = 0x1 << 7,
}
private struct PackedFlags
{
// Layout:
// |u|ss|fffffffff|n|rrr|cccccccc|vvvvvvvv|
//
// v = decoded well known attribute values. 8 bits.
// c = completion states for well known attributes. 1 if given attribute has been decoded, 0 otherwise. 8 bits.
// r = RefKind. 3 bits.
// n = hasNameInMetadata. 1 bit.
// f = FlowAnalysisAnnotations. 9 bits (8 value bits + 1 completion bit).
// s = Scope. 2 bits.
// u = HasUnscopedRefAttribute. 1 bit.
// Current total = 32 bits.
private const int WellKnownAttributeDataOffset = 0;
private const int WellKnownAttributeCompletionFlagOffset = 8;
private const int RefKindOffset = 16;
private const int FlowAnalysisAnnotationsOffset = 21;
private const int ScopeOffset = 29;
private const int RefKindMask = 0x7;
private const int WellKnownAttributeDataMask = 0xFF;
private const int WellKnownAttributeCompletionFlagMask = WellKnownAttributeDataMask;
private const int FlowAnalysisAnnotationsMask = 0xFF;
private const int ScopeMask = 0x3;
private const int HasNameInMetadataBit = 0x1 << 19;
private const int FlowAnalysisAnnotationsCompletionBit = 0x1 << 20;
private const int HasUnscopedRefAttributeBit = 0x1 << 31;
private const int AllWellKnownAttributesCompleteNoData = WellKnownAttributeCompletionFlagMask << WellKnownAttributeCompletionFlagOffset;
private int _bits;
public RefKind RefKind
{
get { return (RefKind)((_bits >> RefKindOffset) & RefKindMask); }
}
public bool HasNameInMetadata
{
get { return (_bits & HasNameInMetadataBit) != 0; }
}
public ScopedKind Scope
{
get { return (ScopedKind)((_bits >> ScopeOffset) & ScopeMask); }
}
public bool HasUnscopedRefAttribute
{
get { return (_bits & HasUnscopedRefAttributeBit) != 0; }
}
#if DEBUG
static PackedFlags()
{
// Verify masks are sufficient for values.
Debug.Assert(EnumUtilities.ContainsAllValues<WellKnownAttributeFlags>(WellKnownAttributeDataMask));
Debug.Assert(EnumUtilities.ContainsAllValues<RefKind>(RefKindMask));
Debug.Assert(EnumUtilities.ContainsAllValues<FlowAnalysisAnnotations>(FlowAnalysisAnnotationsMask));
Debug.Assert(EnumUtilities.ContainsAllValues<ScopedKind>(ScopeMask));
}
#endif
public PackedFlags(RefKind refKind, bool attributesAreComplete, bool hasNameInMetadata, ScopedKind scope, bool hasUnscopedRefAttribute)
{
int refKindBits = ((int)refKind & RefKindMask) << RefKindOffset;
int attributeBits = attributesAreComplete ? AllWellKnownAttributesCompleteNoData : 0;
int hasNameInMetadataBits = hasNameInMetadata ? HasNameInMetadataBit : 0;
int scopeBits = ((int)scope & ScopeMask) << ScopeOffset;
int hasUnscopedRefAttributeBits = hasUnscopedRefAttribute ? HasUnscopedRefAttributeBit : 0;
_bits = refKindBits | attributeBits | hasNameInMetadataBits | scopeBits | hasUnscopedRefAttributeBits;
}
public bool SetWellKnownAttribute(WellKnownAttributeFlags flag, bool value)
{
// a value has been decoded:
int bitsToSet = (int)flag << WellKnownAttributeCompletionFlagOffset;
if (value)
{
// the actual value:
bitsToSet |= ((int)flag << WellKnownAttributeDataOffset);
}
ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
return value;
}
public bool TryGetWellKnownAttribute(WellKnownAttributeFlags flag, out bool value)
{
int theBits = _bits; // Read this.bits once to ensure the consistency of the value and completion flags.
value = (theBits & ((int)flag << WellKnownAttributeDataOffset)) != 0;
return (theBits & ((int)flag << WellKnownAttributeCompletionFlagOffset)) != 0;
}
public bool SetFlowAnalysisAnnotations(FlowAnalysisAnnotations value)
{
int bitsToSet = FlowAnalysisAnnotationsCompletionBit | (((int)value & FlowAnalysisAnnotationsMask) << FlowAnalysisAnnotationsOffset);
return ThreadSafeFlagOperations.Set(ref _bits, bitsToSet);
}
public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value)
{
int theBits = _bits; // Read this.bits once to ensure the consistency of the value and completion flags.
value = (FlowAnalysisAnnotations)((theBits >> FlowAnalysisAnnotationsOffset) & FlowAnalysisAnnotationsMask);
var result = (theBits & FlowAnalysisAnnotationsCompletionBit) != 0;
Debug.Assert(value == 0 || result);
return result;
}
}
private readonly Symbol _containingSymbol;
private readonly string _name;
private readonly TypeWithAnnotations _typeWithAnnotations;
private readonly ParameterHandle _handle;
private readonly ParameterAttributes _flags;
private readonly PEModuleSymbol _moduleSymbol;
private ImmutableArray<CSharpAttributeData> _lazyCustomAttributes;
private ConstantValue? _lazyDefaultValue = ConstantValue.Unset;
[Flags]
private enum IsParamsValues : byte
{
NotInitialized = 0,
Initialized = 1,
Array = 2,
Collection = 4,
}
private IsParamsValues _lazyIsParams;
private static readonly ImmutableArray<int> s_defaultStringHandlerAttributeIndexes = ImmutableArray.Create(int.MinValue);
private ImmutableArray<int> _lazyInterpolatedStringHandlerAttributeIndexes = s_defaultStringHandlerAttributeIndexes;
/// <summary>
/// The index of a CallerArgumentExpression. The value -2 means uninitialized, -1 means
/// not found. Otherwise, the index of the CallerArgumentExpression.
/// </summary>
private int _lazyCallerArgumentExpressionParameterIndex = -2;
/// <summary>
/// Attributes filtered out from m_lazyCustomAttributes, ParamArray, etc.
/// </summary>
private ImmutableArray<CSharpAttributeData> _lazyHiddenAttributes;
private readonly ushort _ordinal;
private PackedFlags _packedFlags;
internal static PEParameterSymbol Create(
PEModuleSymbol moduleSymbol,
PEMethodSymbol containingSymbol,
bool isContainingSymbolVirtual,
int ordinal,
ParamInfo<TypeSymbol> parameterInfo,
Symbol nullableContext,
bool isReturn,
out bool isBad)
{
return Create(
moduleSymbol, containingSymbol, isContainingSymbolVirtual, ordinal,
parameterInfo.IsByRef, parameterInfo.RefCustomModifiers, parameterInfo.Type,
parameterInfo.Handle, nullableContext, parameterInfo.CustomModifiers, isReturn, out isBad);
}
/// <summary>
/// Construct a parameter symbol for a property loaded from metadata.
/// </summary>
/// <param name="moduleSymbol"></param>
/// <param name="containingSymbol"></param>
/// <param name="ordinal"></param>
/// <param name="handle">The property parameter doesn't have a name in metadata,
/// so this is the handle of a corresponding accessor parameter, if there is one,
/// or of the ParamInfo passed in, otherwise.</param>
/// <param name="parameterInfo" />
/// <param name="isBad" />
internal static PEParameterSymbol Create(
PEModuleSymbol moduleSymbol,
PEPropertySymbol containingSymbol,
bool isContainingSymbolVirtual,
int ordinal,
ParameterHandle handle,
ParamInfo<TypeSymbol> parameterInfo,
Symbol nullableContext,
out bool isBad)
{
return Create(
moduleSymbol, containingSymbol, isContainingSymbolVirtual, ordinal,
parameterInfo.IsByRef, parameterInfo.RefCustomModifiers, parameterInfo.Type,
handle, nullableContext, parameterInfo.CustomModifiers, isReturn: false, out isBad);
}
private PEParameterSymbol(
PEModuleSymbol moduleSymbol,
Symbol containingSymbol,
int ordinal,
bool isByRef,
TypeWithAnnotations typeWithAnnotations,
ParameterHandle handle,
Symbol nullableContext,
int countOfCustomModifiers,
bool isReturn,
out bool isBad)
{
Debug.Assert((object)moduleSymbol != null);
Debug.Assert((object)containingSymbol != null);
Debug.Assert(ordinal >= 0);
Debug.Assert(typeWithAnnotations.HasType);
isBad = false;
_moduleSymbol = moduleSymbol;
_containingSymbol = containingSymbol;
_ordinal = (ushort)ordinal;
_handle = handle;
RefKind refKind = RefKind.None;
ScopedKind scope = ScopedKind.None;
bool hasUnscopedRefAttribute = false;
if (handle.IsNil)
{
refKind = isByRef ? RefKind.Ref : RefKind.None;
byte? value = nullableContext.GetNullableContextValue();
if (value.HasValue)
{
typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, value.GetValueOrDefault(), default);
}
_lazyCustomAttributes = ImmutableArray<CSharpAttributeData>.Empty;
_lazyHiddenAttributes = ImmutableArray<CSharpAttributeData>.Empty;
_lazyDefaultValue = ConstantValue.NotAvailable;
_lazyIsParams = IsParamsValues.Initialized;
}
else
{
try
{
moduleSymbol.Module.GetParamPropsOrThrow(handle, out _name, out _flags);
}
catch (BadImageFormatException)
{
isBad = true;
}
if (isByRef)
{
ParameterAttributes inOutFlags = _flags & (ParameterAttributes.Out | ParameterAttributes.In);
if (inOutFlags == ParameterAttributes.Out)
{
refKind = RefKind.Out;
}
else if (!isReturn && moduleSymbol.Module.HasRequiresLocationAttribute(handle))
{
refKind = RefKind.RefReadOnlyParameter;
}
else if (moduleSymbol.Module.HasIsReadOnlyAttribute(handle))
{
refKind = RefKind.In;
}
else
{
refKind = RefKind.Ref;
}
}
var typeSymbol = DynamicTypeDecoder.TransformType(typeWithAnnotations.Type, countOfCustomModifiers, handle, moduleSymbol, refKind);
typeSymbol = NativeIntegerTypeDecoder.TransformType(typeSymbol, handle, moduleSymbol, containingSymbol.ContainingType);
typeWithAnnotations = typeWithAnnotations.WithTypeAndModifiers(typeSymbol, typeWithAnnotations.CustomModifiers);
// Decode nullable before tuple types to avoid converting between
// NamedTypeSymbol and TupleTypeSymbol unnecessarily.
// The containing type is passed to NullableTypeDecoder.TransformType to determine access
// for property parameters because the property does not have explicit accessibility in metadata.
var accessSymbol = containingSymbol.Kind == SymbolKind.Property ? containingSymbol.ContainingSymbol : containingSymbol;
typeWithAnnotations = NullableTypeDecoder.TransformType(typeWithAnnotations, handle, moduleSymbol, accessSymbol: accessSymbol, nullableContext: nullableContext);
typeWithAnnotations = TupleTypeDecoder.DecodeTupleTypesIfApplicable(typeWithAnnotations, handle, moduleSymbol);
hasUnscopedRefAttribute = _moduleSymbol.Module.HasUnscopedRefAttribute(_handle);
if (hasUnscopedRefAttribute)
{
if (_moduleSymbol.Module.HasScopedRefAttribute(_handle))
{
isBad = true;
}
scope = ScopedKind.None;
}
else if (_moduleSymbol.Module.HasScopedRefAttribute(_handle))
{
if (isByRef)
{
Debug.Assert(refKind != RefKind.None);
scope = ScopedKind.ScopedRef;
}
else if (typeWithAnnotations.Type.IsRefLikeOrAllowsRefLikeType())
{
scope = ScopedKind.ScopedValue;
}
else
{
isBad = true;
}
}
else if (ParameterHelpers.IsRefScopedByDefault(_moduleSymbol.UseUpdatedEscapeRules, refKind))
{
scope = ScopedKind.ScopedRef;
}
}
_typeWithAnnotations = typeWithAnnotations;
bool hasNameInMetadata = !string.IsNullOrEmpty(_name);
if (!hasNameInMetadata)
{
// As was done historically, if the parameter doesn't have a name, we give it the name "value".
_name = "value";
}
_packedFlags = new PackedFlags(refKind, attributesAreComplete: handle.IsNil, hasNameInMetadata: hasNameInMetadata, scope, hasUnscopedRefAttribute);
Debug.Assert(refKind == this.RefKind);
Debug.Assert(hasNameInMetadata == this.HasNameInMetadata);
Debug.Assert(_name is not null);
}
private bool HasNameInMetadata
{
get
{
return _packedFlags.HasNameInMetadata;
}
}
private static PEParameterSymbol Create(
PEModuleSymbol moduleSymbol,
Symbol containingSymbol,
bool isContainingSymbolVirtual,
int ordinal,
bool isByRef,
ImmutableArray<ModifierInfo<TypeSymbol>> refCustomModifiers,
TypeSymbol type,
ParameterHandle handle,
Symbol nullableContext,
ImmutableArray<ModifierInfo<TypeSymbol>> customModifiers,
bool isReturn,
out bool isBad)
{
// We start without annotation (they will be decoded below)
var typeWithModifiers = TypeWithAnnotations.Create(type, customModifiers: CSharpCustomModifier.Convert(customModifiers));
PEParameterSymbol parameter = customModifiers.IsDefaultOrEmpty && refCustomModifiers.IsDefaultOrEmpty
? new PEParameterSymbol(moduleSymbol, containingSymbol, ordinal, isByRef, typeWithModifiers, handle, nullableContext, 0, isReturn: isReturn, out isBad)
: new PEParameterSymbolWithCustomModifiers(moduleSymbol, containingSymbol, ordinal, isByRef, refCustomModifiers, typeWithModifiers, handle, nullableContext, isReturn: isReturn, out isBad);
bool hasInAttributeModifier = parameter.RefCustomModifiers.HasInAttributeModifier();
if (isReturn)
{
// A RefReadOnly return parameter should always have this modreq, and vice versa.
Debug.Assert(parameter.RefKind != RefKind.RefReadOnlyParameter);
isBad |= (parameter.RefKind == RefKind.RefReadOnly) != hasInAttributeModifier;
}
else if (parameter.RefKind is RefKind.In or RefKind.RefReadOnlyParameter)
{
// An in/ref readonly parameter should not have this modreq, unless the containing symbol was virtual or abstract.
isBad |= isContainingSymbolVirtual != hasInAttributeModifier;
}
else if (hasInAttributeModifier)
{
// This modreq should not exist on non-in parameters.
isBad = true;
}
return parameter;
}
private sealed class PEParameterSymbolWithCustomModifiers : PEParameterSymbol
{
private readonly ImmutableArray<CustomModifier> _refCustomModifiers;
public PEParameterSymbolWithCustomModifiers(
PEModuleSymbol moduleSymbol,
Symbol containingSymbol,
int ordinal,
bool isByRef,
ImmutableArray<ModifierInfo<TypeSymbol>> refCustomModifiers,
TypeWithAnnotations type,
ParameterHandle handle,
Symbol nullableContext,
bool isReturn,
out bool isBad) :
base(moduleSymbol, containingSymbol, ordinal, isByRef, type, handle, nullableContext,
refCustomModifiers.NullToEmpty().Length + type.CustomModifiers.Length,
isReturn: isReturn, out isBad)
{
_refCustomModifiers = CSharpCustomModifier.Convert(refCustomModifiers);
Debug.Assert(_refCustomModifiers.IsEmpty || isByRef);
}
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get
{
return _refCustomModifiers;
}
}
}
public override RefKind RefKind
{
get
{
return _packedFlags.RefKind;
}
}
public override string Name
{
get
{
return _name;
}
}
public override string MetadataName
{
get
{
return HasNameInMetadata ? _name : string.Empty;
}
}
public override int MetadataToken
{
get { return MetadataTokens.GetToken(_handle); }
}
internal ParameterAttributes Flags
{
get
{
return _flags;
}
}
public override int Ordinal
{
get
{
return _ordinal;
}
}
public override bool IsDiscard
{
get
{
return false;
}
}
// might be Nil
internal ParameterHandle Handle
{
get
{
return _handle;
}
}
public override Symbol ContainingSymbol
{
get
{
return _containingSymbol;
}
}
internal override bool HasMetadataConstantValue
{
get
{
return (_flags & ParameterAttributes.HasDefault) != 0;
}
}
/// <remarks>
/// Internal for testing. Non-test code should use <see cref="ExplicitDefaultConstantValue"/>.
/// </remarks>
internal ConstantValue? ImportConstantValue(bool ignoreAttributes = false)
{
Debug.Assert(!_handle.IsNil);
// Metadata Spec 22.33:
// 6. If Flags.HasDefault = 1 then this row [of Param table] shall own exactly one row in the Constant table [ERROR]
// 7. If Flags.HasDefault = 0, then there shall be no rows in the Constant table owned by this row [ERROR]
ConstantValue? value = null;
if ((_flags & ParameterAttributes.HasDefault) != 0)
{
value = _moduleSymbol.Module.GetParamDefaultValue(_handle);
}
if (value == null && !ignoreAttributes)
{
value = GetDefaultDecimalOrDateTimeValue();
}
return value;
}
internal override ConstantValue? ExplicitDefaultConstantValue
{
get
{
// The HasDefault flag has to be set, it doesn't suffice to mark the parameter with DefaultParameterValueAttribute.
if (_lazyDefaultValue == ConstantValue.Unset)
{
// From the C# point of view, there is no need to import a parameter's default value
// if the language isn't going to treat it as optional. However, we might need metadata constant value for NoPia.
// NOTE: Ignoring attributes for non-Optional parameters disrupts round-tripping, but the trade-off seems acceptable.
ConstantValue? value = ImportConstantValue(ignoreAttributes: !IsMetadataOptional);
Interlocked.CompareExchange(ref _lazyDefaultValue, value, ConstantValue.Unset);
}
return _lazyDefaultValue;
}
}
private ConstantValue? GetDefaultDecimalOrDateTimeValue()
{
Debug.Assert(!_handle.IsNil);
ConstantValue? value = null;
// It is possible in Visual Basic for a parameter of object type to have a default value of DateTime type.
// If it's present, use it. We'll let the call-site figure out whether it can actually be used.
if (_moduleSymbol.Module.HasDateTimeConstantAttribute(_handle, out value))
{
return value;
}
// It is possible in Visual Basic for a parameter of object type to have a default value of decimal type.
// If it's present, use it. We'll let the call-site figure out whether it can actually be used.
if (_moduleSymbol.Module.HasDecimalConstantAttribute(_handle, out value))
{
return value;
}
return value;
}
internal override bool IsMetadataOptional
{
get
{
return (_flags & ParameterAttributes.Optional) != 0;
}
}
internal override bool IsIDispatchConstant
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.HasIDispatchConstantAttribute;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
value = _packedFlags.SetWellKnownAttribute(flag, _moduleSymbol.Module.HasAttribute(_handle,
AttributeDescription.IDispatchConstantAttribute));
}
return value;
}
}
internal override bool IsIUnknownConstant
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.HasIUnknownConstantAttribute;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
value = _packedFlags.SetWellKnownAttribute(flag, _moduleSymbol.Module.HasAttribute(_handle,
AttributeDescription.IUnknownConstantAttribute));
}
return value;
}
}
private bool HasCallerLineNumberAttribute
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.HasCallerLineNumberAttribute;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
value = _packedFlags.SetWellKnownAttribute(flag, _moduleSymbol.Module.HasAttribute(_handle,
AttributeDescription.CallerLineNumberAttribute));
}
return value;
}
}
private bool HasCallerFilePathAttribute
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.HasCallerFilePathAttribute;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
value = _packedFlags.SetWellKnownAttribute(flag, _moduleSymbol.Module.HasAttribute(_handle,
AttributeDescription.CallerFilePathAttribute));
}
return value;
}
}
private bool HasCallerMemberNameAttribute
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.HasCallerMemberNameAttribute;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
value = _packedFlags.SetWellKnownAttribute(flag, _moduleSymbol.Module.HasAttribute(_handle,
AttributeDescription.CallerMemberNameAttribute));
}
return value;
}
}
internal override bool IsCallerLineNumber
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.IsCallerLineNumber;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bool isCallerLineNumber = HasCallerLineNumberAttribute
&& ContainingAssembly.TypeConversions.HasCallerLineNumberConversion(this.Type, ref discardedUseSiteInfo);
value = _packedFlags.SetWellKnownAttribute(flag, isCallerLineNumber);
}
return value;
}
}
internal override bool IsCallerFilePath
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.IsCallerFilePath;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bool isCallerFilePath = !HasCallerLineNumberAttribute
&& HasCallerFilePathAttribute
&& ContainingAssembly.TypeConversions.HasCallerInfoStringConversion(this.Type, ref discardedUseSiteInfo);
value = _packedFlags.SetWellKnownAttribute(flag, isCallerFilePath);
}
return value;
}
}
internal override bool IsCallerMemberName
{
get
{
const WellKnownAttributeFlags flag = WellKnownAttributeFlags.IsCallerMemberName;
bool value;
if (!_packedFlags.TryGetWellKnownAttribute(flag, out value))
{
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bool isCallerMemberName = !HasCallerLineNumberAttribute
&& !HasCallerFilePathAttribute
&& HasCallerMemberNameAttribute
&& ContainingAssembly.TypeConversions.HasCallerInfoStringConversion(this.Type, ref discardedUseSiteInfo);
value = _packedFlags.SetWellKnownAttribute(flag, isCallerMemberName);
}
return value;
}
}
internal override int CallerArgumentExpressionParameterIndex
{
get
{
if (_lazyCallerArgumentExpressionParameterIndex != -2)
{
return _lazyCallerArgumentExpressionParameterIndex;
}
var info = _moduleSymbol.Module.FindTargetAttribute(_handle, AttributeDescription.CallerArgumentExpressionAttribute);
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
bool isCallerArgumentExpression = info.HasValue
&& !HasCallerLineNumberAttribute
&& !HasCallerFilePathAttribute
&& !HasCallerMemberNameAttribute
&& ContainingAssembly.TypeConversions.HasCallerInfoStringConversion(this.Type, ref discardedUseSiteInfo);
if (isCallerArgumentExpression)
{
_moduleSymbol.Module.TryExtractStringValueFromAttribute(info.Handle, out var parameterName);
var parameters = ContainingSymbol.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
if (parameters[i].Name.Equals(parameterName, StringComparison.Ordinal))
{
_lazyCallerArgumentExpressionParameterIndex = i;
return i;
}
}
}
_lazyCallerArgumentExpressionParameterIndex = -1;
return -1;
}
}
internal override FlowAnalysisAnnotations FlowAnalysisAnnotations
{
get
{
FlowAnalysisAnnotations value;
if (!_packedFlags.TryGetFlowAnalysisAnnotations(out value))
{
value = DecodeFlowAnalysisAttributes(_moduleSymbol.Module, _handle);
_packedFlags.SetFlowAnalysisAnnotations(value);
}
return value;
}
}
private static FlowAnalysisAnnotations DecodeFlowAnalysisAttributes(PEModule module, ParameterHandle handle)
{
FlowAnalysisAnnotations annotations = FlowAnalysisAnnotations.None;
if (module.HasAttribute(handle, AttributeDescription.AllowNullAttribute)) annotations |= FlowAnalysisAnnotations.AllowNull;
if (module.HasAttribute(handle, AttributeDescription.DisallowNullAttribute)) annotations |= FlowAnalysisAnnotations.DisallowNull;
if (module.HasAttribute(handle, AttributeDescription.MaybeNullAttribute))
{
annotations |= FlowAnalysisAnnotations.MaybeNull;
}
else if (module.HasMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(handle, AttributeDescription.MaybeNullWhenAttribute, out bool when))
{
annotations |= (when ? FlowAnalysisAnnotations.MaybeNullWhenTrue : FlowAnalysisAnnotations.MaybeNullWhenFalse);
}
if (module.HasAttribute(handle, AttributeDescription.NotNullAttribute))
{
annotations |= FlowAnalysisAnnotations.NotNull;
}
else if (module.HasMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(handle, AttributeDescription.NotNullWhenAttribute, out bool when))
{
annotations |= (when ? FlowAnalysisAnnotations.NotNullWhenTrue : FlowAnalysisAnnotations.NotNullWhenFalse);
}
if (module.HasMaybeNullWhenOrNotNullWhenOrDoesNotReturnIfAttribute(handle, AttributeDescription.DoesNotReturnIfAttribute, out bool condition))
{
annotations |= (condition ? FlowAnalysisAnnotations.DoesNotReturnIfTrue : FlowAnalysisAnnotations.DoesNotReturnIfFalse);
}
return annotations;
}
#nullable enable
internal override ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes
{
get
{
EnsureInterpolatedStringHandlerArgumentAttributeDecoded();
ImmutableArray<int> indexes = _lazyInterpolatedStringHandlerAttributeIndexes;
Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes);
return indexes.NullToEmpty();
}
}
internal override bool HasInterpolatedStringHandlerArgumentError
{
get
{
EnsureInterpolatedStringHandlerArgumentAttributeDecoded();
ImmutableArray<int> indexes = _lazyInterpolatedStringHandlerAttributeIndexes;
Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes);
return indexes.IsDefault;
}
}
private void EnsureInterpolatedStringHandlerArgumentAttributeDecoded()
{
ImmutableArray<int> indexes = _lazyInterpolatedStringHandlerAttributeIndexes;
if (indexes == s_defaultStringHandlerAttributeIndexes)
{
indexes = DecodeInterpolatedStringHandlerArgumentAttribute();
Debug.Assert(indexes != s_defaultStringHandlerAttributeIndexes);
var initialized = ImmutableInterlocked.InterlockedCompareExchange(ref _lazyInterpolatedStringHandlerAttributeIndexes, value: indexes, comparand: s_defaultStringHandlerAttributeIndexes);
Debug.Assert(initialized == s_defaultStringHandlerAttributeIndexes || indexes == initialized || indexes.SequenceEqual(initialized));
}
}
private ImmutableArray<int> DecodeInterpolatedStringHandlerArgumentAttribute()
{
var (paramNames, hasAttribute) = _moduleSymbol.Module.GetInterpolatedStringHandlerArgumentAttributeValues(_handle);
if (!hasAttribute)
{
return ImmutableArray<int>.Empty;
}
else if (paramNames.IsDefault || Type is not NamedTypeSymbol { IsInterpolatedStringHandlerType: true })
{
return default;
}
if (paramNames.IsEmpty)
{
return ImmutableArray<int>.Empty;
}
var builder = ArrayBuilder<int>.GetInstance(paramNames.Length);
var parameters = ContainingSymbol.GetParameters();
foreach (var name in paramNames)
{
switch (name)
{
case null:
case "" when !ContainingSymbol.RequiresInstanceReceiver() || ContainingSymbol is MethodSymbol { MethodKind: MethodKind.Constructor or MethodKind.DelegateInvoke }:
// Invalid data, bail
builder.Free();
return default;
case "":
builder.Add(BoundInterpolatedStringArgumentPlaceholder.InstanceParameter);
break;
default:
var param = parameters.FirstOrDefault(static (p, name) => string.Equals(p.Name, name, StringComparison.Ordinal), name);
if (param is not null && (object)param != this)
{
builder.Add(param.Ordinal);
break;
}
else
{
builder.Free();
return default;
}
}
}
return builder.ToImmutableAndFree();
}
#nullable disable
internal override ImmutableHashSet<string> NotNullIfParameterNotNull
{
get
{
return _moduleSymbol.Module.GetStringValuesOfNotNullIfNotNullAttribute(_handle);
}
}
public override TypeWithAnnotations TypeWithAnnotations
{
get
{
return _typeWithAnnotations;
}
}
public override ImmutableArray<CustomModifier> RefCustomModifiers
{
get
{
return ImmutableArray<CustomModifier>.Empty;
}
}
internal override bool IsMetadataIn
{
get { return (_flags & ParameterAttributes.In) != 0; }
}
internal override bool IsMetadataOut
{
get { return (_flags & ParameterAttributes.Out) != 0; }
}
internal override bool IsMarshalledExplicitly
{
get
{
return (_flags & ParameterAttributes.HasFieldMarshal) != 0;
}
}
internal override MarshalPseudoCustomAttributeData MarshallingInformation
{
get
{
// the compiler doesn't need full marshalling information, just the unmanaged type or descriptor
return null;
}
}
internal override ImmutableArray<byte> MarshallingDescriptor
{
get
{
if ((_flags & ParameterAttributes.HasFieldMarshal) == 0)
{
return default(ImmutableArray<byte>);
}
Debug.Assert(!_handle.IsNil);
return _moduleSymbol.Module.GetMarshallingDescriptor(_handle);
}
}
internal override UnmanagedType MarshallingType
{
get
{
if ((_flags & ParameterAttributes.HasFieldMarshal) == 0)
{
return 0;
}
Debug.Assert(!_handle.IsNil);
return _moduleSymbol.Module.GetMarshallingType(_handle);
}
}
public override bool IsParamsArray
{
get
{
return (GetIsParamsValues() & IsParamsValues.Array) != 0;
}
}
public override bool IsParamsCollection
{
get
{
return (GetIsParamsValues() & IsParamsValues.Collection) != 0;
}
}
private IsParamsValues GetIsParamsValues()
{
// This is also populated by loading attributes, but loading
// attributes is more expensive, so we should only do it if
// attributes are requested.
if ((_lazyIsParams & IsParamsValues.Initialized) == 0)
{
IsParamsValues result = IsParamsValues.Initialized;
if (_moduleSymbol.Module.HasParamArrayAttribute(_handle))
{
result |= IsParamsValues.Array;
}
if (_moduleSymbol.Module.HasParamCollectionAttribute(_handle))
{
result |= IsParamsValues.Collection;
}
_lazyIsParams = result;
}
return _lazyIsParams;
}
public override ImmutableArray<Location> Locations
{
get
{
return _containingSymbol.Locations;
}
}
public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
{
get
{
return ImmutableArray<SyntaxReference>.Empty;
}
}
internal sealed override ScopedKind EffectiveScope => _packedFlags.Scope;
internal override bool HasUnscopedRefAttribute => _packedFlags.HasUnscopedRefAttribute;
internal sealed override bool UseUpdatedEscapeRules => _moduleSymbol.UseUpdatedEscapeRules;
public override ImmutableArray<CSharpAttributeData> GetAttributes()
{
if (_lazyCustomAttributes.IsDefault)
{
Debug.Assert(!_handle.IsNil);
var containingPEModuleSymbol = (PEModuleSymbol)this.ContainingModule;
// Filter out Params attributes if necessary and cache
// the attribute handle for GetCustomAttributesToEmit
bool filterOutParamArrayAttribute = ((_lazyIsParams & (IsParamsValues.Initialized | IsParamsValues.Array)) is 0 or (IsParamsValues.Initialized | IsParamsValues.Array));
bool filterOutParamCollectionAttribute = ((_lazyIsParams & (IsParamsValues.Initialized | IsParamsValues.Collection)) is 0 or (IsParamsValues.Initialized | IsParamsValues.Collection));
ConstantValue defaultValue = this.ExplicitDefaultConstantValue;
AttributeDescription filterOutConstantAttributeDescription = default(AttributeDescription);
if ((object)defaultValue != null)
{
if (defaultValue.Discriminator == ConstantValueTypeDiscriminator.DateTime)
{
filterOutConstantAttributeDescription = AttributeDescription.DateTimeConstantAttribute;
}
else if (defaultValue.Discriminator == ConstantValueTypeDiscriminator.Decimal)
{
filterOutConstantAttributeDescription = AttributeDescription.DecimalConstantAttribute;
}
}
bool filterIsReadOnlyAttribute = this.RefKind == RefKind.In;
bool filterRequiresLocationAttribute = this.RefKind == RefKind.RefReadOnlyParameter;
CustomAttributeHandle paramArrayAttribute;
CustomAttributeHandle paramCollectionAttribute;
CustomAttributeHandle constantAttribute;
ImmutableArray<CSharpAttributeData> attributes =
containingPEModuleSymbol.GetCustomAttributesForToken(
_handle,
out paramArrayAttribute,
filterOutParamArrayAttribute ? AttributeDescription.ParamArrayAttribute : default,
out paramCollectionAttribute,
filterOutParamCollectionAttribute ? AttributeDescription.ParamCollectionAttribute : default,
out constantAttribute,
filterOutConstantAttributeDescription,
out _,
filterIsReadOnlyAttribute ? AttributeDescription.IsReadOnlyAttribute : default,
out _,
filterRequiresLocationAttribute ? AttributeDescription.RequiresLocationAttribute : default,
out _,
AttributeDescription.ScopedRefAttribute);
if (!paramArrayAttribute.IsNil || !constantAttribute.IsNil || !paramCollectionAttribute.IsNil)
{
var builder = ArrayBuilder<CSharpAttributeData>.GetInstance();
if (!paramArrayAttribute.IsNil)
{
builder.Add(new PEAttributeData(containingPEModuleSymbol, paramArrayAttribute));
}
if (!paramCollectionAttribute.IsNil)
{
builder.Add(new PEAttributeData(containingPEModuleSymbol, paramCollectionAttribute));
}
if (!constantAttribute.IsNil)
{
builder.Add(new PEAttributeData(containingPEModuleSymbol, constantAttribute));
}
ImmutableInterlocked.InterlockedInitialize(ref _lazyHiddenAttributes, builder.ToImmutableAndFree());
}
else
{
ImmutableInterlocked.InterlockedInitialize(ref _lazyHiddenAttributes, ImmutableArray<CSharpAttributeData>.Empty);
}
if ((_lazyIsParams & IsParamsValues.Initialized) == 0)
{
Debug.Assert(filterOutParamArrayAttribute);
Debug.Assert(filterOutParamCollectionAttribute);
IsParamsValues result = IsParamsValues.Initialized;
if (!paramArrayAttribute.IsNil)
{
result |= IsParamsValues.Array;
}
if (!paramCollectionAttribute.IsNil)
{
result |= IsParamsValues.Collection;
}
_lazyIsParams = result;
}
ImmutableInterlocked.InterlockedInitialize(
ref _lazyCustomAttributes,
attributes);
}
Debug.Assert(!_lazyHiddenAttributes.IsDefault);
return _lazyCustomAttributes;
}
internal override IEnumerable<CSharpAttributeData> GetCustomAttributesToEmit(PEModuleBuilder moduleBuilder)
{
foreach (CSharpAttributeData attribute in GetAttributes())
{
yield return attribute;
}
// Yield hidden attributes last, order might be important.
foreach (CSharpAttributeData attribute in _lazyHiddenAttributes)
{
yield return attribute;
}
}
internal sealed override CSharpCompilation DeclaringCompilation // perf, not correctness
{
get { return null; }
}
public sealed override bool Equals(Symbol other, TypeCompareKind compareKind)
{
return other is NativeIntegerParameterSymbol nps ?
nps.Equals(this, compareKind) :
base.Equals(other, compareKind);
}
#nullable enable
internal DiagnosticInfo? DeriveCompilerFeatureRequiredDiagnostic(MetadataDecoder decoder)
=> PEUtilities.DeriveCompilerFeatureRequiredAttributeDiagnostic(this, (PEModuleSymbol)ContainingModule, Handle, allowedFeatures: CompilerFeatureRequiredFeatures.None, decoder);
public override bool HasUnsupportedMetadata
{
get
{
var containingModule = (PEModuleSymbol)ContainingModule;
var decoder = ContainingSymbol switch
{
PEMethodSymbol method => new MetadataDecoder(containingModule, method),
PEPropertySymbol => new MetadataDecoder(containingModule, (PENamedTypeSymbol)ContainingType),
_ => throw ExceptionUtilities.UnexpectedValue(this.ContainingSymbol.Kind)
};
return DeriveCompilerFeatureRequiredDiagnostic(decoder) is { Code: (int)ErrorCode.ERR_UnsupportedCompilerFeature } || base.HasUnsupportedMetadata;
}
}
}
}
|