| File: System\Reflection\Emit\CustomAttributeWrapper.cs | Web Access |
| Project: src\runtime\src\libraries\System.Reflection.Emit\src\System.Reflection.Emit.csproj (System.Reflection.Emit) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; using System.Reflection.Metadata; namespace System.Reflection.Emit { internal readonly struct CustomAttributeWrapper { private readonly ConstructorInfo _constructorInfo; private readonly byte[] _binaryAttribute; public CustomAttributeWrapper(ConstructorInfo constructorInfo, ReadOnlySpan<byte> binaryAttribute) { _constructorInfo = constructorInfo; _binaryAttribute = binaryAttribute.ToArray(); // TODO: Update to BlobHandle when public API public APi for MetadataBuilder.GetOrAddBlob(ReadOnlySpan<byte>) added } public ConstructorInfo Ctor => _constructorInfo; public byte[] Data => _binaryAttribute; } internal struct CustomAttributeInfo { public ConstructorInfo _ctor; public object?[] _ctorArgs; public string[] _namedParamNames; public object?[] _namedParamValues; private const int Field = 0x53; private const int EnumType = 0x55; private const int NullValue = 0xff; private const int OneByteMask = 0x7f; private const int TwoByteMask = 0x3f; private const int FourByteMask = 0x1f; internal static CustomAttributeInfo DecodeCustomAttribute(ConstructorInfo ctor, ReadOnlySpan<byte> binaryAttribute) { int pos = 2; CustomAttributeInfo info = default; if (binaryAttribute.Length < 2) { throw new ArgumentException(SR.Format(SR.Argument_InvalidCustomAttributeLength, ctor.DeclaringType, binaryAttribute.Length), nameof(binaryAttribute)); } if ((binaryAttribute[0] != 0x01) || (binaryAttribute[1] != 0x00)) { throw new ArgumentException(SR.Format(SR.Argument_InvalidProlog, ctor.DeclaringType), nameof(binaryAttribute)); } ParameterInfo[] pi = ctor.GetParameters(); info._ctor = ctor; info._ctorArgs = new object?[pi.Length]; for (int i = 0; i < pi.Length; ++i) { info._ctorArgs[i] = DecodeCustomAttributeValue(pi[i].ParameterType, binaryAttribute, pos, out pos); } int numNamed = BinaryPrimitives.ReadUInt16LittleEndian(binaryAttribute.Slice(pos)); pos += 2; info._namedParamNames = new string[numNamed]; info._namedParamValues = new object[numNamed]; for (int i = 0; i < numNamed; ++i) { int namedType = binaryAttribute[pos++]; int dataType = binaryAttribute[pos++]; if (dataType == EnumType) { // skip bytes for Enum type name; int len2 = DecodeLen(binaryAttribute, pos, out pos); pos += len2; } int len = DecodeLen(binaryAttribute, pos, out pos); string name = StringFromBytes(binaryAttribute, pos, len); info._namedParamNames[i] = name; pos += len; if (namedType == Field) { // For known pseudo custom attributes underlying Enum type is int Type fieldType = dataType == EnumType ? typeof(int) : ElementTypeToType((SerializationTypeCode)dataType); info._namedParamValues[i] = DecodeCustomAttributeValue(fieldType, binaryAttribute, pos, out pos); ; } else { throw new ArgumentException(SR.Format(SR.Argument_UnknownNamedType, ctor.DeclaringType, namedType), nameof(binaryAttribute)); } } return info; } private static string StringFromBytes(ReadOnlySpan<byte> data, int pos, int len) { return Text.Encoding.UTF8.GetString(data.Slice(pos, len)); } private static int DecodeLen(ReadOnlySpan<byte> data, int pos, out int rpos) { int len; if ((data[pos] & 0x80) == 0) { len = (data[pos++] & OneByteMask); } else if ((data[pos] & 0x40) == 0) { len = ((data[pos] & TwoByteMask) << 8) + data[pos + 1]; pos += 2; } else { len = ((data[pos] & FourByteMask) << 24) + (data[pos + 1] << 16) + (data[pos + 2] << 8) + data[pos + 3]; pos += 4; } rpos = pos; return len; } private static object? DecodeCustomAttributeValue(Type t, ReadOnlySpan<byte> data, int pos, out int rpos) { switch (Type.GetTypeCode(t)) { case TypeCode.String: if (data[pos] == NullValue) { rpos = pos + 1; return null; } int len = DecodeLen(data, pos, out pos); rpos = pos + len; return StringFromBytes(data, pos, len); case TypeCode.Int32: rpos = pos + 4; return BinaryPrimitives.ReadInt32LittleEndian(data.Slice(pos)); case TypeCode.Int16: rpos = pos + 2; return BinaryPrimitives.ReadInt16LittleEndian(data.Slice(pos)); case TypeCode.Boolean: rpos = pos + 1; return (data[pos] == 0) ? false : true; case TypeCode.Object: int subtype = data[pos]; pos += 1; if (subtype >= 0x02 && subtype <= 0x0e) { return DecodeCustomAttributeValue(ElementTypeToType((SerializationTypeCode)subtype), data, pos, out rpos); } break; } throw new NotImplementedException(SR.Format(SR.NotImplemented_TypeForValue, t)); } private static Type ElementTypeToType(SerializationTypeCode elementType) => elementType switch { SerializationTypeCode.Boolean => typeof(bool), SerializationTypeCode.Char => typeof(char), SerializationTypeCode.SByte => typeof(sbyte), SerializationTypeCode.Byte => typeof(byte), SerializationTypeCode.Int16 => typeof(short), SerializationTypeCode.UInt16 => typeof(ushort), SerializationTypeCode.Int32 => typeof(int), SerializationTypeCode.UInt32 => typeof(uint), SerializationTypeCode.Int64 => typeof(long), SerializationTypeCode.UInt64 => typeof(ulong), SerializationTypeCode.Single => typeof(float), SerializationTypeCode.Double => typeof(double), SerializationTypeCode.String => typeof(string), SerializationTypeCode.Type => typeof(string), // the type name written in string format _ => throw new ArgumentException(SR.Argument_InvalidTypeCodeForTypeArgument, "binaryAttribute"), }; } }