| File: System\Reflection\TypeLoading\General\Ecma\EcmaDefaultValueProcessing.cs | Web Access |
| Project: src\runtime\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Reflection.Metadata; using System.Runtime.CompilerServices; namespace System.Reflection.TypeLoading.Ecma { internal static class EcmaDefaultValueProcessing { public static object? ToRawObject(this ConstantHandle constantHandle, MetadataReader metadataReader) { if (constantHandle.IsNil) throw new BadImageFormatException(); Constant constantValue = metadataReader.GetConstant(constantHandle); // Partition II section 24.2.4: // The first entry in both these heaps is the empty 'blob' that consists of the single byte 0x00. // // This means zero value is valid for string and is used to represent the empty string. if (constantValue.Value.IsNil && constantValue.TypeCode != ConstantTypeCode.String) throw new BadImageFormatException(); BlobReader reader = metadataReader.GetBlobReader(constantValue.Value); switch (constantValue.TypeCode) { case ConstantTypeCode.Boolean: return reader.ReadBoolean(); case ConstantTypeCode.Char: return reader.ReadChar(); case ConstantTypeCode.SByte: return reader.ReadSByte(); case ConstantTypeCode.Int16: return reader.ReadInt16(); case ConstantTypeCode.Int32: return reader.ReadInt32(); case ConstantTypeCode.Int64: return reader.ReadInt64(); case ConstantTypeCode.Byte: return reader.ReadByte(); case ConstantTypeCode.UInt16: return reader.ReadUInt16(); case ConstantTypeCode.UInt32: return reader.ReadUInt32(); case ConstantTypeCode.UInt64: return reader.ReadUInt64(); case ConstantTypeCode.Single: return reader.ReadSingle(); case ConstantTypeCode.Double: return reader.ReadDouble(); case ConstantTypeCode.String: return reader.ReadUTF16(reader.Length); case ConstantTypeCode.NullReference: // Partition II section 22.9: // The encoding of Type for the nullref value is ELEMENT_TYPE_CLASS with a Value of a 4-byte zero. // Unlike uses of ELEMENT_TYPE_CLASS in signatures, this one is not followed by a type token. if (reader.ReadUInt32() == 0) return null; break; } throw new BadImageFormatException(); } public static bool TryFindRawDefaultValueFromCustomAttributes(this CustomAttributeHandleCollection handles, EcmaModule module, out object? rawDefaultValue) { rawDefaultValue = default; MetadataReader reader = module.Reader; foreach (CustomAttributeHandle handle in handles) { CustomAttribute ca = handle.GetCustomAttribute(reader); EntityHandle declaringTypeHandle = ca.TryGetDeclaringTypeHandle(reader); if (declaringTypeHandle.IsNil) continue; if (declaringTypeHandle.TypeMatchesNameAndNamespace(Utf8Constants.SystemRuntimeCompilerServices, Utf8Constants.DateTimeConstantAttribute, reader)) { CustomAttributeData cad = handle.ToCustomAttributeData(module); IList<CustomAttributeTypedArgument> cats = cad.ConstructorArguments; if (cats.Count != 1) return false; CoreTypes ct = module.Loader.GetAllFoundCoreTypes(); if (cats[0].ArgumentType != ct[CoreType.Int64]) return false; long ticks = (long)(cats[0].Value!); rawDefaultValue = new DateTimeConstantAttribute(ticks).Value; return true; } if (declaringTypeHandle.TypeMatchesNameAndNamespace(Utf8Constants.SystemRuntimeCompilerServices, Utf8Constants.DecimalConstantAttribute, reader)) { CustomAttributeData cad = handle.ToCustomAttributeData(module); IList<CustomAttributeTypedArgument> cats = cad.ConstructorArguments; if (cats.Count != 5) return false; CoreTypes ct = module.Loader.GetAllFoundCoreTypes(); if (cats[0].ArgumentType != ct[CoreType.Byte] || cats[1].ArgumentType != ct[CoreType.Byte]) return false; byte scale = (byte)cats[0].Value!; byte sign = (byte)cats[1].Value!; if (cats[2].ArgumentType == ct[CoreType.Int32] && cats[3].ArgumentType == ct[CoreType.Int32] && cats[4].ArgumentType == ct[CoreType.Int32]) { int hi = (int)cats[2].Value!; int mid = (int)cats[3].Value!; int lo = (int)cats[4].Value!; rawDefaultValue = new DecimalConstantAttribute(scale, sign, hi, mid, lo).Value; return true; } if (cats[2].ArgumentType == ct[CoreType.UInt32] && cats[3].ArgumentType == ct[CoreType.UInt32] && cats[4].ArgumentType == ct[CoreType.UInt32]) { uint hi = (uint)cats[2].Value!; uint mid = (uint)cats[3].Value!; uint lo = (uint)cats[4].Value!; rawDefaultValue = new DecimalConstantAttribute(scale, sign, hi, mid, lo).Value; return true; } return false; } // Should we also look for CustomConstantAttribute too? Who uses that (other than DateTimeConstantAttribute which // we handled above?) CustomConstantAttribute is an abstract class which means we have to figure out how a subclass // we've never heard of would set the "Value" property which is kinda hard to do when you can't Invoke(). // Even the CLR doesn't return consistent values for this between the raw and non-raw versions. // Even doing the subclass check would open the door to resolving types and dependency assemblies and their // resulting FileNotFoundExceptions. Which is exactly what we're trying to avoid with this name-based lookup approach. } return false; } } }