I am analyzing types and methods with reflection and there is a method that contains a nested method.
In the typename I can see the "mangled" name that contains the method where the nested method was defined, as well as the nested methods name.
However I'm not sure how to parse mangled names like this one:
<<LoopMethod>g__NestedFunction5001_1>d
(this example would be the IAsyncStateMachine of a nested async Task)
...d, g__..., 1_1. What other things are there? How are those names formatted/mangled exactly?The part inside <>d is generated by MakeLocalFunctionName called by SynthesizedLambdaMethod.MakeName called by LambdaRewriter:
< hardcoded
parent method name
> hardcoded
g means "nested method" (officially called local function)
__ is SuffixSeparator
1 is method ordinal (index of the method in its containing type member list, if applicable)
_ is IdSeparator
1 is entity ordinal (index of the lambda)
not in your sample: # GenerationSeparator followed by EnC generation number
No, this is an implementation detail.
As hinted above g is the part that identifies this as a local function in this implementation.
And following the same rules, <...>d is similar to <...>g, but means a state machine. So technically, <<LoopMethod>g__NestedFunction5001_1>d is not a local function name.
FYI @agocke
Yeah, not sure what @rikimaru0345's scenario is, but I wondered whether there were any considerations to make public API for interpreting the names, something like the GeneratedNames.TryParse... methods? I can imagine it being useful for tools and I assume it would have been a better solution than everyone writing their ad-hoc parsers...
If we did that, then we'd be unable to change the names here. We'd effectively be signing up to our internal implementation details being part of our public contract.
Note: i do know that we take care to not intentionally change what we emit (becuase some tools do interpret it). But i see a difference in not-intentionally breaking, vs. actually committing to a specific contract here.
Note: i think it would be fine for someone to make a nuget package for this. The syntax is simple enough that it wouldn't be hard to do.
@miloush Thanks for the help!
So technically, <
g__NestedFunction5001_1>d is not a local function name.
Well the local function is declared async Task so when you get the method from a stacktrace it will yield "MoveNext" as name.
Thats why in my code I check if the .DeclaringType implements IEnumerable, IEnumerator, IAsyncStateMachine.
And if it does, I take the name of the type as method name and keep working with that :)
<<LoopMethod>g__NestedFunction5001_1>d
One thing that I'm still not sure about is separating the numbers.
The actual method name is NestedFunction500, but immediately after that there's a 1,
If that is the method index, then that means I would need to check what index the method has so i can trim out those numbers?
Or maybe I'm guaranteed that the suffix is always 1_1 because every statemachine gets its own type anyway, right?
@CyrusNajmabadi That's why I suggested providing an API, thinking that might still allow you to change what is emitted, rather than people relying on the actual name syntax.
@rikimaru0345 If you didn't know the original method name or the index then you might have hard time splitting that indeed.
Or maybe I'm guaranteed that the suffix is always
1_1because every statemachine gets its own type anyway, right?
Not really. The 1_1 belongs to the original method, not to the state machine, so how is the state machine implemented does not matter. For example,
```C#
class Program
{
static void Test()
{
Console.Write(T());
async Task
}
static void Main() ...
gives you `0_0` and
```C#
class Program
{
static void Main() { }
static void Test() ...
gives you 1_0
That's why I suggested providing an API, thinking that might still allow you to change what is emitted
If we provide an API, then we can't change it. You might be using an old version of the API against a new version of the compiler. If the compiler changes how it emits these things, then this will break people using the old API.
@rikimaru0345 What is your analysis via reflection trying to achieve?
@tmat
I'm writing a thing that outputs nicely formatted stacktraces.
It skips over methods that do not help with understanding at all (ThrowHelper, ExceptionServices.*, and many more...).
It "unpacks" method names as best it can by walking up the type hierarchy.
For example showing <>c__ or <<Method>b__0>d is not as nice as simply showing LambdaExpression or async Method.
It also does some basic formatting to make filenames+line numbers much easier to read; only showing filename not including the path, right-aligning the numbers, ...
What I have right now works for all scenarios except some crazy constructed ones. And even when it cannot reliably determine the original name it will select a best guess so its still really good. :)
Thanks again to @miloush for the great help.
I hope your question has been answered. Any further questions about reflection should probably be directed at a repo that covers the reflection APIs.