|
// 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"),
};
}
}
|