| File: Symbols\TypedConstant.cs | Web Access |
| Project: src\roslyn\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis) |
// 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.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.Symbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// <summary> /// Represents a constant value used as an argument to a custom attribute. /// </summary> public readonly struct TypedConstant : IEquatable<TypedConstant> { private readonly TypedConstantKind _kind; private readonly ITypeSymbolInternal? _type; private readonly object? _value; internal TypedConstant(ITypeSymbolInternal? type, TypedConstantKind kind, object? value) { Debug.Assert(kind == TypedConstantKind.Array || !(value is ImmutableArray<TypedConstant>)); Debug.Assert(!(value is ISymbol) || value is ISymbolInternal); Debug.Assert(type is object || kind == TypedConstantKind.Error); _kind = kind; _type = type; _value = value; } internal TypedConstant(ITypeSymbolInternal type, ImmutableArray<TypedConstant> array) : this(type, TypedConstantKind.Array, value: array.IsDefault ? null : (object)array) { } /// <summary> /// The kind of the constant. /// </summary> public TypedConstantKind Kind { get { return _kind; } } /// <summary> /// Returns the <see cref="ITypeSymbol"/> of the constant, /// or null if the type can't be determined (error). /// </summary> public ITypeSymbol? Type { get { return _type?.GetITypeSymbol(); } } internal ITypeSymbolInternal? TypeInternal { get { return _type; } } /// <summary> /// True if the constant represents a null reference. /// </summary> public bool IsNull { get { return _value == null; } } /// <summary> /// The value for a non-array constant. /// </summary> public object? Value { get { object? result = ValueInternal; if (result is ISymbolInternal symbol) { return symbol.GetISymbol(); } return result; } } /// <summary> /// Unlike <see cref="Value"/> returns <see cref="ISymbolInternal"/> when the value is a symbol. /// </summary> internal object? ValueInternal { get { if (Kind == TypedConstantKind.Array) { throw new InvalidOperationException("TypedConstant is an array. Use Values property."); } return _value; } } /// <summary> /// The value for a <see cref="TypedConstant"/> array. /// Returns a <see langword="default" /> <c>ImmutableArray</c> if <see langword="null"/> was passed as the array value; /// <see cref="IsNull"/> can be used to check for this. /// </summary> public ImmutableArray<TypedConstant> Values { get { if (Kind != TypedConstantKind.Array) { throw new InvalidOperationException("TypedConstant is not an array. Use Value property."); } if (this.IsNull) { return default; } return (ImmutableArray<TypedConstant>)_value!; } } internal T? DecodeValue<T>(SpecialType specialType) { TryDecodeValue(specialType, out T? value); return value; } internal bool TryDecodeValue<T>(SpecialType specialType, [MaybeNullWhen(false)] out T value) { if (_kind == TypedConstantKind.Error) { value = default; return false; } if (_type!.SpecialType == specialType || (_type.TypeKind == TypeKind.Enum && specialType == SpecialType.System_Enum)) { value = (T)_value!; return true; } // the actual argument type doesn't match the type of the parameter - an error has already been reported by the binder value = default; return false; } /// <remarks> /// TypedConstant isn't computing its own kind from the type symbol because it doesn't /// have a way to recognize the well-known type System.Type. /// </remarks> internal static TypedConstantKind GetTypedConstantKind(ITypeSymbolInternal type, Compilation compilation) { RoslynDebug.Assert(type != null); switch (type.SpecialType) { case SpecialType.System_Boolean: case SpecialType.System_SByte: case SpecialType.System_Int16: case SpecialType.System_Int32: case SpecialType.System_Int64: case SpecialType.System_Byte: case SpecialType.System_UInt16: case SpecialType.System_UInt32: case SpecialType.System_UInt64: case SpecialType.System_Single: case SpecialType.System_Double: case SpecialType.System_Char: case SpecialType.System_String: case SpecialType.System_Object: return TypedConstantKind.Primitive; default: switch (type.TypeKind) { case TypeKind.Array: return TypedConstantKind.Array; case TypeKind.Enum: return TypedConstantKind.Enum; case TypeKind.Error: return TypedConstantKind.Error; } if (compilation != null && compilation.IsSystemTypeReference(type)) { return TypedConstantKind.Type; } return TypedConstantKind.Error; } } public override bool Equals(object? obj) { return obj is TypedConstant && Equals((TypedConstant)obj); } public bool Equals(TypedConstant other) { return _kind == other._kind && object.Equals(_value, other._value) && object.Equals(_type, other._type); } public override int GetHashCode() { return Hash.Combine(_value, Hash.Combine(_type, (int)this.Kind)); } #region Testing & Debugging #if false /// <summary> /// Returns the System.String that represents the current TypedConstant. /// </summary> /// <returns>A System.String that represents the current TypedConstant.</returns> public override string ToString() { if (value.IsNull) { return "null"; } if (kind == TypedConstantKind.Array) { return "{" + string.Join(", ", Values.Select(v => new TypedConstant(v).ToString())) + "}"; } if (kind == TypedConstantKind.Type || type.SpecialType == SpecialType.System_Object) { return "typeof(" + value.Object.ToString() + ")"; } if (kind == TypedConstantKind.Enum) { // TODO (tomat): replace with SymbolDisplay return DisplayEnumConstant(); } return SymbolDisplay.FormatPrimitive(value.Object, quoteStrings: true, useHexadecimalNumbers: false); } // Decode the value of enum constant private string DisplayEnumConstant() { Debug.Assert(Kind == TypedConstantKind.Enum); // Create a ConstantValue of enum underlying type SpecialType splType = this.Type.GetEnumUnderlyingType().SpecialType; ConstantValue valueConstant = ConstantValue.Create(this.Value, splType); string typeName = this.Type.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat); if (valueConstant.IsUnsigned) { return DisplayUnsignedEnumConstant(splType, valueConstant.UInt64Value, typeName); } else { return DisplaySignedEnumConstant(splType, valueConstant.Int64Value, typeName); } } private string DisplayUnsignedEnumConstant(SpecialType specialType, ulong constantToDecode, string typeName) { // Specified valueConstant might have an exact matching enum field // or it might be a bitwise Or of multiple enum fields. // For the later case, we keep track of the current value of // bitwise Or of possible enum fields. ulong curValue = 0; // Initialize the value string to empty PooledStringBuilder pooledBuilder = null; StringBuilder valueStringBuilder = null; // Iterate through all the constant members in the enum type ImmutableArray<Symbol> members = this.Type.GetMembers(); foreach (Symbol member in members) { var field = member as FieldSymbol; if ((object)field != null && field.HasConstantValue) { ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue, specialType); ulong memberValue = memberConstant.UInt64Value; // Do we have an exact matching enum field if (memberValue == constantToDecode) { if (pooledBuilder != null) { pooledBuilder.Free(); } return typeName + "." + field.Name; } // specifiedValue might be a bitwise Or of multiple enum fields // Is the current member included in the specified value? if ((memberValue & constantToDecode) == memberValue) { // update the current value curValue = curValue | memberValue; if (valueStringBuilder == null) { pooledBuilder = PooledStringBuilder.GetInstance(); valueStringBuilder = pooledBuilder.Builder; } else { valueStringBuilder.Append(" | "); } valueStringBuilder.Append(typeName); valueStringBuilder.Append("."); valueStringBuilder.Append(field.Name); } } } if (pooledBuilder != null) { if (curValue == constantToDecode) { // return decoded enum constant return pooledBuilder.ToStringAndFree(); } // Unable to decode the enum constant pooledBuilder.Free(); } // Unable to decode the enum constant, just display the integral value return this.Value.ToString(); } private string DisplaySignedEnumConstant(SpecialType specialType, long constantToDecode, string typeName) { // Specified valueConstant might have an exact matching enum field // or it might be a bitwise Or of multiple enum fields. // For the later case, we keep track of the current value of // bitwise Or of possible enum fields. long curValue = 0; // Initialize the value string to empty PooledStringBuilder pooledBuilder = null; StringBuilder valueStringBuilder = null; // Iterate through all the constant members in the enum type ImmutableArray<Symbol> members = this.Type.GetMembers(); foreach (Symbol member in members) { var field = member as FieldSymbol; if ((object)field != null && field.HasConstantValue) { ConstantValue memberConstant = ConstantValue.Create(field.ConstantValue, specialType); long memberValue = memberConstant.Int64Value; // Do we have an exact matching enum field if (memberValue == constantToDecode) { if (pooledBuilder != null) { pooledBuilder.Free(); } return typeName + "." + field.Name; } // specifiedValue might be a bitwise Or of multiple enum fields // Is the current member included in the specified value? if ((memberValue & constantToDecode) == memberValue) { // update the current value curValue = curValue | memberValue; if (valueStringBuilder == null) { pooledBuilder = PooledStringBuilder.GetInstance(); valueStringBuilder = pooledBuilder.Builder; } else { valueStringBuilder.Append(" | "); } valueStringBuilder.Append(typeName); valueStringBuilder.Append("."); valueStringBuilder.Append(field.Name); } } } if (pooledBuilder != null) { if (curValue == constantToDecode) { // return decoded enum constant return pooledBuilder.ToStringAndFree(); } // Unable to decode the enum constant pooledBuilder.Free(); } // Unable to decode the enum constant, just display the integral value return this.Value.ToString(); } #endif #endregion } }