Unusual C# operators in decompiled source...?
Asked Answered
S

3

14

I've just decompiled some 3rd party source to debug an issue, using DotPeek. The output code contains some unusual operators, which AFAIK aren't valid C#, so I'm wondering what they mean...

The extract looks like (with Dotpeek comments included, as they are probably relevant);

protected internal void DoReceive(ref byte[] Buffer, int MaxSize, out int Written)
{
    Written = 0;
    .
    .
    .        
    // ISSUE: explicit reference operation
    // ISSUE: variable of a reference type
    int& local = @Written;
    int num = SomeMethod();
    .
    .
    .
    // ISSUE: explicit reference operation
    ^local = num;
}

So, 3 unusual operators in there... int& = @Written seems to be assigning a pointer to a variable that is named pointlessly with the @ character?

But what is ^local = num; ???

OK, here is the equivalent snippet from ILSpy, which makes more sense, I guess the decompile to C# didn't produce a valid equivalent?

'C#'

 int& local = @Written;
 byte[] numArray2 = this.FInSpool;
 int num = (int) __Global.Min(numArray2 == null ? 0L : (long) numArray2.Length, (long) MaxSize);
 ^local = num;

IL

 byte[] expr_22 = this.FInSpool;
 Written = (int)__Global.Min((long)((expr_22 == null) ? 0 : expr_22.Length), (long)MaxSize);

So, I guess the 'C#' isn't quite valid? That IL would be valid C#, not sure why DotPeek produced the output it did. Perhaps I'll stick to ILSpy for this one...?

Shirleenshirlene answered 17/12, 2012 at 22:22 Comment(6)
Are you sure you decompiled it to C#? THose are C++/CLI operators.Poteet
My guess would be ^local relates to the fact that Written is an out parameter. It seems to be dereferencing the pointer.Capitular
I don't think int& is a valid term in C#? Can you paste the original MSIL if possible? C# only exposes a subset of MSIL, so it has either been incorrectly decompiled, or the source language isn't C#.Italian
Can you show us a) the IL and b) the Reflector output in C# and c) the original source? MSIL frequently uses (safe!) managed pointers internally. You are seeing a manifestation of them.Greatgrandaunt
@Greatgrandaunt -> b) no sorry, no license for Reflector. c) no, it's non-open source 3rd party code. Will add the IL, it does make things a bit clearer.Shirleenshirlene
DotPeek still hasn't fixed this issue ten years later, so for those who stumble upon this question, this answer explains the cause of the issue quite well.Lakeishalakeland
L
9

If you look at the raw IL (from ildasm, not the C# equivalent via IL Spy), that may help you see what the decompiler is trying to say. 'Out' parameters are represented using a (managed) typed-reference, which isn't explicitly exposed as a type in C#. 'Instances' of this type can normally only be passed as parameters to methods accepting typed references ('ref' or 'out' parameters.) See OpCodes.Mkrefany for more information.

What dotPeek is complaining about is that this 'out' typed reference was stored from the argument into a local variable slot, then written to later via the local slot. The '@' and '^' are placeholders used to indicate this unexpected behavior detected by the decompiler (the one the ISSUE comments describe.)

It's possible the code was compiled from C++/CLI and thus the IL looks different from the typical C# compiler output. It's also possible this is some level of minor obfuscation to confuse decompilers (though I don't think so.) I don't think this is functionally any different from loading the reference from its argument onto the operation stack directly (avoiding the use of a local variable slot), but I could be wrong.

Lvov answered 17/12, 2012 at 23:32 Comment(2)
This does seem to fit the situation. Thanks for the explanation.Shirleenshirlene
Actually, I misspoke a bit; the particular method in your example is probably not using typed references (via mkrefany), but rather address references (i.e. type int&). That said, the basic issue is still the same; the decompiler wasn't expecting the int& to get stored into a local variable before being used.Lvov
G
3

Putting a @ before a name allows you to use a reserved name for a variable, For example if I wanted to have a variable called return I would need to do this.

public int Weird()
{
    int @return = 0;
    return @return;
}

See this SO question for more details.


Putting a ^ before the name ... umm, no clue. (will update as I research I could find any info on what ^ means when not being used as a XOR)

Gretna answered 17/12, 2012 at 22:30 Comment(4)
Thanks, understand the usage of @, I guess it's just not necessary in this case, as 'Written' is not a reserved word?Shirleenshirlene
written is not reserved from what I could tell, but it could one of the those context sensitive keywords like get or set.Gretna
Or, there could be a literal @ sign in the identifier name. It's not legal C#, but, for example, the C# compiler itself uses strange characters such as < and > in the names of lambda functions. Or maybe the decompiler uses @ for some special purpose.Pentup
In this case, @ is being generated because the decompiler is confused about ref locals and has nothing to do with reserved words. This SO answer explains it well.Lakeishalakeland
J
0

It's clearly a decompilation issue. Because of broad set of languages supported decompiler to any particular language may not always find exact match, but still tries to produce some output. Decompiler MAY try to produce somewhat equivalent output, like in this case could be:

protected internal void DoReceive(ref byte[] Buffer, int MaxSize, out int Written)
{
    Written = 0;
    .        
    int num = SomeMethod();
    .
    Written = num;
}

but SHOULD it really do this? in this case decompiler actually provided you with a hint, so you could decide if this is important for your particular case, as there MAY be some side effects.

Jest answered 2/4, 2013 at 15:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.