问题
I have this method
public void Add(Action<Cursor> action)
{
// Decompile `action` to C#
}
Inside the method I want to decompile the anonymous method inside action
to C# code, or AST representation of C# code.
I tried using Mono.Cecil
and ICSharpCode.Decompiler
(from the ILSpy repository), but I couldn't find a way to decompile anonymous methods, as both libraries require you to load the assembly from a physical location and search the method through the types graph, which seems very convoluted, especially when looking for an anonymous method.
Is there a way use the IL Byte array of the method, and decompile that to C#?
public void Add(Action<Cursor> action)
{
var il = action.Method.GetMethodBody().GetILAsByteArray();
var ast = decompiler.DecompileIL(il); // Does such decompiler exist?
}
回答1:
Using the ICSharpCode.Decompiler
library (from the ILSpy repository), I was able to decompile a lambda method.
Like this
public void Add(Action<Cursor> action)
{
// Get the assembly in which the lambda resides
var asm = Assembly.GetCallingAssembly();
var file = asm.Location;
// Create a resolver (a callback for resolving referenced assemblies during decompilation)
var resolver = new CustomAssemblyResolver(asm);
// Create an instance of decompiler
var decompiler = new CSharpDecompiler(file, resolver, new DecompilerSettings());
// Get an "EntityHandle" instance for the lambda's underlying method
var method = MetadataTokenHelpers.TryAsEntityHandle(action.Method.MetadataToken);
var ast = decompiler.Decompile(new List<EntityHandle>() { method.Value });
}
As I mentioned, you provide the CSharpDecompiler
instance with a resolver (IAssemblyResolver
) whose job is to load referenced assemblies during the decompilation process.
ICSharpCode.Decompiler
has a UniversalAssemblyResolver
(source)
which does the job by searching common folders in the file system (it receives a framework version to know where to search). I didn't like it as it assumes too much and requires too much information (I don't always know the framework or other parts of my runtime), so I've created my own resolver which traverses the assemblies graph (starting from the calling assembly).
class CustomAssemblyResolver : IAssemblyResolver
{
private Dictionary<string, Assembly> _references;
public CustomAssemblyResolver(Assembly asm)
{
_references = new Dictionary<string, Assembly>();
var stack = new Stack<Assembly>();
stack.Push(asm);
while (stack.Count > 0)
{
var top = stack.Pop();
if (_references.ContainsKey(top.FullName)) continue;
_references[top.FullName] = top;
var refs = top.GetReferencedAssemblies();
if (refs != null && refs.Length > 0)
{
foreach (var r in refs)
{
stack.Push(Assembly.Load(r));
}
}
}
;
}
public PEFile Resolve(IAssemblyReference reference)
{
var asm = _references[reference.FullName];
var file = asm.Location;
return new PEFile(file, new FileStream(file, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
}
public PEFile ResolveModule(PEFile mainModule, string moduleName)
{
var baseDir = Path.GetDirectoryName(mainModule.FileName);
var moduleFileName = Path.Combine(baseDir, moduleName);
if (!File.Exists(moduleFileName))
{
throw new Exception($"Module {moduleName} could not be found");
}
return new PEFile(moduleFileName, new FileStream(moduleFileName, FileMode.Open, FileAccess.Read), PEStreamOptions.Default, MetadataReaderOptions.Default);
}
}
I don't know if it's the best approach for all use cases, but It works great for me, at least so far.
来源:https://stackoverflow.com/questions/57231579/decompiling-anonymous-method-il-in-net-core