Use a specialized comparer instead of `Comparer<T>.Default` that brings the implementation of `IComparable.ComparerTo` on everything. Also get rid of the generic virtual method call.
Saves 0.9% on Hello World.
// Other
//==============================================================================================
public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle);
- public abstract EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeTypeHandle typeHandle)
- where TUnderlyingValue : struct, INumber<TUnderlyingValue>;
-
+ public abstract void GetEnumInfo(RuntimeTypeHandle typeHandle, out string[] names, out object[] values, out bool isFlags);
public abstract IntPtr GetDynamicInvokeThunk(MethodInvoker invoker);
//==============================================================================================
if (info != null)
return info;
- info = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo<TUnderlyingValue>(runtimeType.TypeHandle);
+ ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetEnumInfo(runtimeType.TypeHandle, out string[] unsortedNames, out object[] unsortedValues, out bool isFlags);
+
+ // Call into IntrospectiveSort directly to avoid the Comparer<T>.Default codepath.
+ // That codepath would bring functionality to compare everything that was ever allocated in the program.
+ ArraySortHelper<object, string>.IntrospectiveSort(unsortedValues, unsortedNames, EnumUnderlyingTypeComparer.Instance);
+
+ // Only after we've sorted, create the underlying array.
+ var values = new TUnderlyingValue[unsortedValues.Length];
+ for (int i = 0; i < unsortedValues.Length; i++)
+ values[i] = (TUnderlyingValue)unsortedValues[i];
+
+ info = new EnumInfo<TUnderlyingValue>(RuntimeAugments.GetEnumUnderlyingType(runtimeType.TypeHandle), values, unsortedNames, isFlags);
runtimeType.GenericCache = info;
return info;
}
+ private class EnumUnderlyingTypeComparer : IComparer<object>
+ {
+ public static readonly EnumUnderlyingTypeComparer Instance = new EnumUnderlyingTypeComparer();
+
+ public int Compare(object? x, object? y)
+ => x switch
+ {
+ int i => i.CompareTo((int)y!),
+ uint ui => ui.CompareTo((uint)y!),
+ byte b => b.CompareTo((byte)y!),
+ ushort us => us.CompareTo((ushort)y!),
+ short s => s.CompareTo((short)y!),
+ sbyte sb => sb.CompareTo((sbyte)y!),
+ long l => l.CompareTo((long)y!),
+ _ => ((ulong)x!).CompareTo((ulong)y!),
+ };
+ }
+
public sealed override DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type)
{
RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo();
return new LiteralFieldAccessor(value, fieldTypeHandle);
}
- public sealed override EnumInfo<TUnderlyingValue> GetEnumInfo<TUnderlyingValue>(RuntimeTypeHandle typeHandle)
+ public sealed override void GetEnumInfo(RuntimeTypeHandle typeHandle, out string[] names, out object[] values, out bool isFlags)
{
// Handle the weird case of an enum type nested under a generic type that makes the
// enum itself generic
// If the type is reflection blocked, we pretend there are no enum values defined
if (ReflectionExecution.ExecutionEnvironment.IsReflectionBlocked(typeDefHandle))
{
- return new EnumInfo<TUnderlyingValue>(RuntimeAugments.GetEnumUnderlyingType(typeHandle), Array.Empty<TUnderlyingValue>(), Array.Empty<string>(), false);
+ names = Array.Empty<string>();
+ values = Array.Empty<object>();
+ isFlags = false;
+ return;
}
QTypeDefinition qTypeDefinition;
if (qTypeDefinition.IsNativeFormatMetadataBased)
{
- return NativeFormatEnumInfo.Create<TUnderlyingValue>(typeHandle, qTypeDefinition.NativeFormatReader, qTypeDefinition.NativeFormatHandle);
+ NativeFormatEnumInfo.GetEnumValuesAndNames(
+ qTypeDefinition.NativeFormatReader,
+ qTypeDefinition.NativeFormatHandle,
+ out values,
+ out names,
+ out isFlags);
+ return;
}
#if ECMA_METADATA_SUPPORT
if (qTypeDefinition.IsEcmaFormatMetadataBased)
return EcmaFormatEnumInfo.Create<TUnderlyingValue>(typeHandle, qTypeDefinition.EcmaFormatReader, qTypeDefinition.EcmaFormatHandle);
}
#endif
- return null;
+ names = Array.Empty<string>();
+ values = Array.Empty<object>();
+ isFlags = false;
+ return;
}
public override IntPtr GetDynamicInvokeThunk(MethodInvoker invoker)
{
static class NativeFormatEnumInfo
{
- private static void GetEnumValuesAndNames(MetadataReader reader, TypeDefinitionHandle typeDefHandle,
- out object[] sortedBoxedValues, out string[] sortedNames, out bool isFlags)
+ public static void GetEnumValuesAndNames(MetadataReader reader, TypeDefinitionHandle typeDefHandle,
+ out object[] unsortedBoxedValues, out string[] unsortedNames, out bool isFlags)
{
TypeDefinition typeDef = reader.GetTypeDefinition(typeDefHandle);
}
}
- var names = new string[staticFieldCount];
- var boxedValues = new object[staticFieldCount]; // TODO: Avoid boxing the values
+ unsortedNames = new string[staticFieldCount];
+ unsortedBoxedValues = new object[staticFieldCount]; // TODO: Avoid boxing the values
int i = 0;
foreach (FieldHandle fieldHandle in typeDef.Fields)
Field field = fieldHandle.GetField(reader);
if (0 != (field.Flags & FieldAttributes.Static))
{
- names[i] = field.Name.GetString(reader);
- boxedValues[i] = field.DefaultValue.ParseConstantNumericValue(reader);
+ unsortedNames[i] = field.Name.GetString(reader);
+ unsortedBoxedValues[i] = field.DefaultValue.ParseConstantNumericValue(reader);
i++;
}
}
- // Using object overload to avoid generic expansion for every underlying enum type
- Array.Sort<object, string>(boxedValues, names);
-
- sortedBoxedValues = boxedValues;
- sortedNames = names;
-
isFlags = false;
foreach (CustomAttributeHandle cah in typeDef.CustomAttributes)
{
isFlags = true;
}
}
-
- public static EnumInfo<TUnderlyingValue> Create<TUnderlyingValue>(RuntimeTypeHandle typeHandle, MetadataReader reader, TypeDefinitionHandle typeDefHandle)
- where TUnderlyingValue : struct, INumber<TUnderlyingValue>
- {
- GetEnumValuesAndNames(reader, typeDefHandle, out object[] boxedValues, out string[] names, out bool isFlags);
-
- var values = new TUnderlyingValue[boxedValues.Length];
- for (int i = 0; i < boxedValues.Length; i++)
- values[i] = (TUnderlyingValue)boxedValues[i];
-
- return new EnumInfo<TUnderlyingValue>(RuntimeAugments.GetEnumUnderlyingType(typeHandle), values, names, isFlags);
- }
}
}