Skip to content

Instantly share code, notes, and snippets.

@illnyang
Last active July 22, 2021 10:02
Show Gist options
  • Save illnyang/5137913a2fe6ebd252a84c424a904939 to your computer and use it in GitHub Desktop.
Save illnyang/5137913a2fe6ebd252a84c424a904939 to your computer and use it in GitHub Desktop.
Evaluates all calls to Enum.GetValues at build time, since it's not supported in NativeAOT's reflection-free mode yet.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace EnumGetValuesEvaluator
{
internal static class Program
{
// [0] = path to assemblies
// [1] = output path
internal static int Main(string[] args)
{
foreach (var assemblyPath in Directory.GetFiles(args[0], "*.dll"))
{
try
{
var saveModule = false;
ModuleContext modCtx = ModuleDef.CreateModuleContext();
ModuleDefMD mod = ModuleDefMD.Load(assemblyPath, modCtx);
var importer = new Importer(mod);
foreach (var type in mod.GetTypes().ToArray())
{
foreach (var method in type.Methods.ToArray())
{
if (!method.HasBody)
continue;
if (!method.Body.HasInstructions)
continue;
var initialInstructions = method.Body.Instructions.ToArray();
for (var i = 0; i < initialInstructions.Length; i++)
{
var curInstr = initialInstructions[i];
if (curInstr.Operand == null)
continue;
if (curInstr.OpCode != OpCodes.Call)
continue;
var callToGetValues = (curInstr.Operand as IMethodDefOrRef);
if (!(callToGetValues?.FullName?.Equals(
"System.Array System.Enum::GetValues(System.Type)") ?? false)) continue;
var arrayRef = importer.ImportAsTypeSig(typeof(int[]));
var listTypeDef = importer.ImportAsTypeSig(typeof(List<int>));
var enumDef = (method.Body.Instructions[i - 2].Operand as TypeRef)?.Resolve();
if (enumDef is not { IsEnum: true })
continue;
var valueStubName = enumDef.ReflectionFullName.Replace(".", "_") + "_ValueStub";
var values = (from field in enumDef.Fields.ToArray() where field.Constant?.Value != null select (int)field.Constant.Value).ToArray();
var stubField = new FieldDefUser(valueStubName, new FieldSig(arrayRef))
{
Access = FieldAttributes.Private,
Attributes = FieldAttributes.Static
};
type.Fields.Add(stubField);
method.Body.Instructions.RemoveAt(i);
method.Body.Instructions.RemoveAt(i - 1);
method.Body.Instructions.RemoveAt(i - 2);
method.Body.Instructions.Insert(i - 2, OpCodes.Ldsfld.ToInstruction(stubField));
method.Body.UpdateInstructionOffsets();
var listTypeSpec = new TypeSpecUser(listTypeDef);
var listCtor = new MemberRefUser(mod, ".ctor", MethodSig.CreateInstance(mod.CorLibTypes.Void), listTypeSpec);
var listAdd = new MemberRefUser(mod, "Add",
MethodSig.CreateInstance(mod.CorLibTypes.Void, new GenericVar(0)),
listTypeSpec);
var listToArray = new MemberRefUser(mod, "ToArray",
MethodSig.CreateInstance(new SZArraySig(new GenericVar(0))),
listTypeSpec);
var typeCCtor = type.FindOrCreateStaticConstructor();
var localList = typeCCtor.Body.Variables.Add(
new Local(listTypeDef, valueStubName + "_temp"));
var instructions = new List<Instruction>();
instructions.Add(OpCodes.Newobj.ToInstruction(listCtor));
instructions.Add(OpCodes.Stloc.ToInstruction(localList));
foreach (var val in values)
{
instructions.Add(OpCodes.Ldloc.ToInstruction(localList));
instructions.Add(OpCodes.Ldc_I4.ToInstruction(val));
instructions.Add(OpCodes.Call.ToInstruction(listAdd));
}
instructions.Add(OpCodes.Ldloc.ToInstruction(localList));
instructions.Add(OpCodes.Callvirt.ToInstruction(listToArray));
instructions.Add(OpCodes.Stsfld.ToInstruction(stubField));
for (var j = instructions.Count - 1; j >= 0; j--)
typeCCtor.Body.Instructions.Insert(0, instructions[j]);
typeCCtor.Body.Instructions.UpdateInstructionOffsets();
saveModule = true;
}
}
}
if (saveModule)
{
Console.WriteLine($"Processed: {Path.GetFileName(assemblyPath)}");
mod.Write(Path.Combine(args[1], Path.GetFileName(assemblyPath)));
}
}
catch (BadImageFormatException)
{
Console.WriteLine("Skipping bad image: " + Path.GetFileName(assemblyPath));
}
}
return 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment