Getting MSIL

Topics: APIs
Jun 13, 2014 at 10:19 PM
Edited Jun 13, 2014 at 10:20 PM
Hi, is there any way how to get the compiled MSIL for a member, having the syntax node?

(I mean inside a VS project, like in refactoring provider, not necessarily for arbitrary string of code out of any context.)
Jun 15, 2014 at 3:54 PM
Edited Jun 15, 2014 at 4:07 PM
Which compiled IL?

The compiled IL in the current configuration or a different configuration?
The compiled IL with or without optimizations and debugging sequence points (which sometimes includes nops to stop on)?
The compiled IL before or after post-build events that weave the IL, like PostSharp, obfuscators or other tools?

The source code is one and the same for all of these but the IL could be wildly different.

Additionally, the IL even for a single method can contain an async/await state machine, an iterator state machine, several levels of nested "display classes" used in capturing variables in lambdas - or the method could be inlined, optimized away or not even included like an unused partial method.

Assuming you work that out, if you compile everything to a stream/byte array, construct an Assembly object and follow this Stack Overflow answer you can work with the IL and attempt to locate the code - but you will have to put it all back together and it's not a trivial task.

I think that the semantic analysis APIs are meant to answer questions like which variables are in scope or written to without you needing to go do all this, if that's what you're after.
Jun 15, 2014 at 4:41 PM
Edited Jun 15, 2014 at 4:42 PM
The first one is easy, the one that would be build by Visual Studio if the user hit Build, so using the current configuration.

The machines and display classes are also easy to answer, since I am looking for the IL of a member, this is out of context. If the compiler changed it to a state machine, good, but I am interested only in the code of the particular member, not really caring if it does something I am unaware of. Now I can see it would be useful to get these hidden classes and members when investigating a type, but I am for the member only (in this discussion).

Inlining a method does not remove it as a member, so I see this as irrelevant as well.

The question of post-build events is interesting for the usefulness of the result indeed, but handling this in general is impossible and definitely not something I would expect the Roslyn API to care about. So, without any after-compilation modifications.

This is what I thought the Visual Studio does in the background, compiles the code on the fly and notifies you of the errors that the compiler generates automatically. So I am basically asking whether result of this background compilation is available. However, if no real compilation happens and the errors are generated from the semantic analysis only, than of course this means clear 'no'.
Jun 15, 2014 at 5:26 PM
Edited Jun 15, 2014 at 5:28 PM
You're saying you want the code of a particular member. If you're writing an iterator method or async/await method, that code gets spread across the member and a state machine type generated for that purpose. So if you want to hunt for a line of code's implementation in IL and that's after let's say an await point, you're not going to find it inside that member's code.

I'm not sure what VS does with the compilation used to generate the diagnostics. I do know that all compilation happens in a separate process and it doesn't seem like there's a way to get the generated assembly back, but someone from Microsoft who actually knows the tooling will be better equipped to answer that.

(I'm taking all this like you by member mean "the code implementation of a member", not just the IL declaring the signature (name, visibility, type, attributes, arguments) of the individual member. Getting at the IL declaring the individual member should be a lot easier and a few lines of Cecil - given you get the assembly, as you say.)
Jun 15, 2014 at 5:34 PM
Edited Jun 15, 2014 at 5:35 PM
I see your concern, but no, I do not need to match individual lines of code to the individual IL bytes, just whatever the compiler ended up generating in the body under that signature.

If that is not available, indeed loading the compiled assembly either using GetMethodBody (thanks for that tip) or Cecil would do, it's just that I would have out of date data until the users recompiles again.
Jun 15, 2014 at 6:14 PM
Just me being nosy, but I suppose could also help people answer this: what are you trying to do?
Jun 15, 2014 at 8:59 PM
I'm doing this via the method outlined in this answer, https://roslyn.codeplex.com/discussions/544763#PostContent_1251621. It uses Roslyn to create the IL stream in-memory and then Cecil to read it back and process it.

You can see the code here https://github.com/mattwarren/RoslynHeapAllocations/blob/master/RoslynHeapAllocations/Program.cs#L23
Jun 16, 2014 at 8:30 PM
Thanks MattWarren for the tip.

Jesper, sure, I am trying to make an adorner that would show some MSIL info for the members, the simplest one being the number of bytes the member takes.