diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs index 0d4a575f362b6f..70d55feb6d9b28 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/BodySubstitutionParser.cs @@ -7,6 +7,7 @@ using System.IO; using System.Reflection.Metadata; using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; using System.Xml; using System.Xml.XPath; using System.Globalization; @@ -163,6 +164,26 @@ private static MethodDesc FindMethod(TypeDesc type, string signature) private static object TryCreateSubstitution(TypeDesc type, string value) { + if (type.IsEnum && type.UnderlyingType.Category == TypeFlags.Int32) + { + foreach (FieldDesc field in type.GetFields()) + { + if (field.IsStatic && + field.Name.StringEquals(value) && + field is EcmaField ecmaField) + { + MetadataReader reader = ecmaField.MetadataReader; + ConstantHandle constantHandle = reader.GetFieldDefinition(ecmaField.Handle).GetDefaultValue(); + if (!constantHandle.IsNil) + { + Constant constant = reader.GetConstant(constantHandle); + if (constant.TypeCode == ConstantTypeCode.Int32) + return reader.GetBlobReader(constant.Value).ReadInt32(); + } + } + } + } + switch (type.UnderlyingType.Category) { case TypeFlags.Int32: diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs index 86d94be1f14c7c..8bf26c389adb35 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs @@ -109,6 +109,7 @@ public void Substitutions(string t) { switch (t) { + case "EnumSubstitutions": case "FeatureGuardSubstitutions": Run(t); break; diff --git a/src/tools/illink/src/linker/Linker.Steps/ProcessLinkerXmlBase.cs b/src/tools/illink/src/linker/Linker.Steps/ProcessLinkerXmlBase.cs index 3d7bb2b2f2d6d9..30a55be0105813 100644 --- a/src/tools/illink/src/linker/Linker.Steps/ProcessLinkerXmlBase.cs +++ b/src/tools/illink/src/linker/Linker.Steps/ProcessLinkerXmlBase.cs @@ -650,6 +650,10 @@ public bool TryConvertValue(string value, TypeReference target, out object? resu result = Convert.ToInt32(enumField.Constant); return true; } + + var underlyingType = typeDefinition.GetEnumUnderlyingType(); + if (underlyingType.MetadataType == MetadataType.Int32) + return TryConvertValue(value, underlyingType, out result); } break; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.cs new file mode 100644 index 00000000000000..43b2ef866b8ac2 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Substitutions +{ + [ExpectedNoWarnings] + [SetupLinkerSubstitutionFile("EnumSubstitutions.xml")] + [IgnoreSubstitutions(false)] + [SkipKeptItemsValidation(By = Tool.NativeAot)] + public class EnumSubstitutions + { + [Kept] + [KeptMember("value__")] + [KeptBaseType(typeof(Enum))] + private enum SubstitutionValue + { + [Kept] + Initial, + + [Kept] + ByName, + + [Kept] + ByNumber, + } + + [Kept] + private static readonly SubstitutionValue FieldByName = SubstitutionValue.Initial; + + [Kept] + private static readonly SubstitutionValue FieldByNumber = SubstitutionValue.Initial; + + public static void Main() + { + VerifyFieldByNameSubstitution(); + VerifyFieldByNumberSubstitution(); + MethodByName(); + MethodByNumber(); + } + + [Kept] + [ExpectedInstructionSequence(new[] { + "nop", + "ldsfld Mono.Linker.Tests.Cases.Substitutions.EnumSubstitutions/SubstitutionValue Mono.Linker.Tests.Cases.Substitutions.EnumSubstitutions::FieldByName", + "ldc.i4.1", + "ceq", + "ldc.i4.0", + "ceq", + "stloc.0", + "ldloc.0", + "brfalse.s il_10", + "ret", + })] + private static void VerifyFieldByNameSubstitution() + { + if (FieldByName != SubstitutionValue.ByName) + ReachableOnUnexpectedFieldByNameValue(); + } + + private static void ReachableOnUnexpectedFieldByNameValue() + { + } + + [Kept] + [ExpectedInstructionSequence(new[] { + "nop", + "ldsfld Mono.Linker.Tests.Cases.Substitutions.EnumSubstitutions/SubstitutionValue Mono.Linker.Tests.Cases.Substitutions.EnumSubstitutions::FieldByNumber", + "ldc.i4.2", + "ceq", + "ldc.i4.0", + "ceq", + "stloc.0", + "ldloc.0", + "brfalse.s il_10", + "ret", + })] + private static void VerifyFieldByNumberSubstitution() + { + if (FieldByNumber != SubstitutionValue.ByNumber) + ReachableOnUnexpectedFieldByNumberValue(); + } + + private static void ReachableOnUnexpectedFieldByNumberValue() + { + } + + [Kept] + [ExpectedInstructionSequence(new[] { + "ldc.i4 0x1", + "ret", + })] + [ExpectedLocalsSequence(new string[0])] + [MethodImpl(MethodImplOptions.NoInlining)] + private static SubstitutionValue MethodByName() + { + return SubstitutionValue.Initial; + } + + [Kept] + [ExpectedInstructionSequence(new[] { + "ldc.i4 0x2", + "ret", + })] + [ExpectedLocalsSequence(new string[0])] + [MethodImpl(MethodImplOptions.NoInlining)] + private static SubstitutionValue MethodByNumber() + { + return SubstitutionValue.Initial; + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.xml b/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.xml new file mode 100644 index 00000000000000..f632582a3d2f99 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Substitutions/EnumSubstitutions.xml @@ -0,0 +1,10 @@ + + + + + + + + + +