Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Diagnostics;

namespace System.Reflection.Metadata
Expand Down Expand Up @@ -144,150 +145,133 @@ internal static void WriteCompressedSignedInteger(BlobBuilder writer, int value)
}
}

internal static void WriteConstant(ref BlobWriter writer, object? value)
/// <summary>
/// Writes a scalar (non-string) constant to a span.
/// </summary>
/// <param name="bytes">The span where the content will be encoded.</param>
/// <param name="value">The constant value.</param>
Comment on lines +151 to +152
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation should clarify that bytes must be at least sizeof(ulong) bytes in length to accommodate the largest scalar type. Additionally, it should document the expected behavior when value is null (returns 4 bytes for a zero uint).

Suggested change
/// <param name="bytes">The span where the content will be encoded.</param>
/// <param name="value">The constant value.</param>
/// <param name="bytes">
/// The span where the content will be encoded. Must be at least <c>sizeof(ulong)</c> bytes in length to accommodate the largest scalar type.
/// </param>
/// <param name="value">
/// The constant value. If <paramref name="value"/> is <c>null</c>, writes 4 bytes representing a zero <c>uint</c>.
/// </param>

Copilot uses AI. Check for mistakes.
/// <returns>The number of bytes that was written.</returns>
internal static int WriteScalarConstant(Span<byte> bytes, object? value)
{
if (value == null)
{
// The encoding of Type for the nullref value for FieldInit is ELEMENT_TYPE_CLASS with a Value of a 32-bit.
writer.WriteUInt32(0);
return;
BinaryPrimitives.WriteUInt32LittleEndian(bytes, 0);
return sizeof(uint);
}

var type = value.GetType();
if (type.GetTypeInfo().IsEnum)
if (type.IsEnum)
{
type = Enum.GetUnderlyingType(type);
}

if (type == typeof(bool))
{
writer.WriteBoolean((bool)value);
bytes[0] = (byte)((bool)value ? 1 : 0);
return sizeof(bool);
}
else if (type == typeof(int))
{
writer.WriteInt32((int)value);
}
else if (type == typeof(string))
{
writer.WriteUTF16((string)value);
BinaryPrimitives.WriteInt32LittleEndian(bytes, (int)value);
return sizeof(int);
}
else if (type == typeof(byte))
{
writer.WriteByte((byte)value);
bytes[0] = (byte)value;
return sizeof(byte);
}
else if (type == typeof(char))
{
writer.WriteUInt16((char)value);
BinaryPrimitives.WriteUInt16LittleEndian(bytes, (char)value);
return sizeof(char);
}
else if (type == typeof(double))
{
writer.WriteDouble((double)value);
#if NET
BinaryPrimitives.WriteDoubleLittleEndian(bytes, (double)value);
#else
double v = (double)value;
unsafe
{
BinaryPrimitives.WriteUInt64LittleEndian(bytes, *(ulong*)(&v));
}
#endif
return sizeof(double);
}
else if (type == typeof(short))
{
writer.WriteInt16((short)value);
BinaryPrimitives.WriteInt16LittleEndian(bytes, (short)value);
return sizeof(short);
}
else if (type == typeof(long))
{
writer.WriteInt64((long)value);
BinaryPrimitives.WriteInt64LittleEndian(bytes, (long)value);
return sizeof(long);
}
else if (type == typeof(sbyte))
{
writer.WriteSByte((sbyte)value);
bytes[0] = (byte)(sbyte)value;
return sizeof(sbyte);
}
else if (type == typeof(float))
{
writer.WriteSingle((float)value);
#if NET
BinaryPrimitives.WriteSingleLittleEndian(bytes, (float)value);
#else
float v = (float)value;
unsafe
{
BinaryPrimitives.WriteUInt32LittleEndian(bytes, *(uint*)(&v));
}
#endif
return sizeof(float);
}
else if (type == typeof(ushort))
{
writer.WriteUInt16((ushort)value);
BinaryPrimitives.WriteUInt16LittleEndian(bytes, (ushort)value);
return sizeof(ushort);
}
else if (type == typeof(uint))
{
writer.WriteUInt32((uint)value);
BinaryPrimitives.WriteUInt32LittleEndian(bytes, (uint)value);
return sizeof(uint);
}
else if (type == typeof(ulong))
{
writer.WriteUInt64((ulong)value);
BinaryPrimitives.WriteUInt64LittleEndian(bytes, (ulong)value);
return sizeof(ulong);
}
else
{
throw new ArgumentException(SR.Format(SR.InvalidConstantValueOfType, type));
}
}

internal static void WriteConstant(BlobBuilder writer, object? value)
internal static void WriteConstant(ref BlobWriter writer, object? value)
{
if (value == null)
if (value is string s)
{
// The encoding of Type for the nullref value for FieldInit is ELEMENT_TYPE_CLASS with a Value of a 32-bit.
writer.WriteUInt32(0);
writer.WriteUTF16(s);
return;
}

var type = value.GetType();
if (type.GetTypeInfo().IsEnum)
{
type = Enum.GetUnderlyingType(type);
}
Span<byte> bytes = stackalloc byte[sizeof(ulong)];
int written = WriteScalarConstant(bytes, value);
writer.WriteBytes(bytes.Slice(0, written));
Comment on lines +259 to +261
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stack allocation size sizeof(ulong) is duplicated across multiple call sites (lines 259, 272, and in MetadataBuilder.Heaps.cs line 259). Consider defining a named constant like MaxScalarConstantSize to make the code more maintainable and self-documenting.

Copilot uses AI. Check for mistakes.
}

if (type == typeof(bool))
{
writer.WriteBoolean((bool)value);
}
else if (type == typeof(int))
{
writer.WriteInt32((int)value);
}
else if (type == typeof(string))
{
writer.WriteUTF16((string)value);
}
else if (type == typeof(byte))
{
writer.WriteByte((byte)value);
}
else if (type == typeof(char))
{
writer.WriteUInt16((char)value);
}
else if (type == typeof(double))
{
writer.WriteDouble((double)value);
}
else if (type == typeof(short))
{
writer.WriteInt16((short)value);
}
else if (type == typeof(long))
{
writer.WriteInt64((long)value);
}
else if (type == typeof(sbyte))
{
writer.WriteSByte((sbyte)value);
}
else if (type == typeof(float))
{
writer.WriteSingle((float)value);
}
else if (type == typeof(ushort))
{
writer.WriteUInt16((ushort)value);
}
else if (type == typeof(uint))
{
writer.WriteUInt32((uint)value);
}
else if (type == typeof(ulong))
{
writer.WriteUInt64((ulong)value);
}
else
internal static void WriteConstant(BlobBuilder writer, object? value)
{
if (value is string s)
{
throw new ArgumentException(SR.Format(SR.InvalidConstantValueOfType, type));
writer.WriteUTF16(s);
return;
}

Span<byte> bytes = stackalloc byte[sizeof(ulong)];
int written = WriteScalarConstant(bytes, value);
writer.WriteBytes(bytes.Slice(0, written));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,9 @@ public BlobHandle GetOrAddConstantBlob(object? value)
return GetOrAddBlobUTF16(str);
}

var builder = PooledBlobBuilder.GetInstance();
builder.WriteConstant(value);
var result = GetOrAddBlob(builder);
builder.Free();
return result;
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
int length = BlobWriterImpl.WriteScalarConstant(buffer, value);
return GetOrAddBlob(buffer.Slice(0, length));
}

/// <summary>
Expand Down
Loading