icsharpcode / ILSpy

.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
21.45k stars 3.35k forks source link

Bug: "Error near IL_X: name" - with MCS Compiler Generated's IEnumarator Pattern #1026

Closed Dentrax closed 5 years ago

Dentrax commented 6 years ago

Sometimes I see it in the iterator functions, not always. Is a decompile error ?

s

siegfriedpammer commented 6 years ago

Which assembly are you trying to decompile? Do know which compiler was used to create it?

Dentrax commented 6 years ago

I don't know, but the output of Protection ID tool ;

File Type : 32-Bit Dll (Subsystem : Win CUI / 3), Size : 3386368 (033AC00h) Byte(s) | Machine: 0x14C (I386)
Compilation TimeStamp : 0x57B4A466 -> Wed 17th Aug 2016 17:52:38 (GMT)
[TimeStamp] 0x57B4A466 -> Wed 17th Aug 2016 17:52:38 (GMT) | PE Header | - | Offset: 0x00000088 | VA: 0x00400088 | -
[File Heuristics] -> Flag #1 : 00000000000001001100000000110000 (0x0004C030)
[Entrypoint Section Entropy] : 5.80 (section #0) ".text   " | Size : 0x33A3C0 (3384256) byte(s)
[DllCharacteristics] -> Flag : (0x0000) -> NONE
[SectionCount] 3 (0x3) | ImageSize 0x342000 (3416064) byte(s)
[VersionInfo] Product Version : 0.0.0.0
[VersionInfo] File Version : 0.0.0.0
[VersionInfo] Original FileName : Assembly-CSharp.dll
[VersionInfo] Internal Name : Assembly-CSharp.dll
[ModuleReport] [IAT] Modules -> mscoree.dll
[.] .net @ FileOffset 0x13A644 | MetaData->Version 1.1 (struct version) -> v2.0.50727 (net version required)
[.] Flags : 0x0 | Streams : 0x5 (5)  -> #~ | #Strings | #US | #Blob | #GUID
[!] [.net scan core] dotNetReactor detected!
[COR20] MajorRuntimeVersion 0x2 (2) | MinorRuntimeVersion 0x2 (2) -> 0x2.2 (2.2)
[COR20] Flags 0x1
[COR20 Flags] [x] IL_ONLY [ ] 32BITREQUIRED [ ] IL_LIBRARY
[COR20 Flags] [ ] STRONGNAME [ ] NATIVE_EP [ ] TRACKDEBUGDATA
[COR20 Flags] [ ] 32BITPREFERRED | 0x0 UNKNOWN
[COR20 Flags] Assembly is NOT strong name signed
[CdKeySerial] found "TestVersion" @ VA: 0x002C5A30 / Offset: 0x002C3C30
[CdKeySerial] found "TestVersion" @ VA: 0x002C5AB9 / Offset: 0x002C3CB9
[CdKeySerial] found "TestVersion" @ VA: 0x002C5ACB / Offset: 0x002C3CCB
[CdKeySerial] found "TestVersion" @ VA: 0x002C5BE2 / Offset: 0x002C3DE2
[CdKeySerial] found "TestVersion" @ VA: 0x002EB387 / Offset: 0x002E9587
- Scan Took : 1.640 Second(s) [000000550h (1360) tick(s)] [246 of 580 scan(s) done]

DIE :

Library : .NET(v2.0.50727)[-]
Linker : Microsoft Linker(6.0)[DLL32,console]

@siegfriedpammer

tamlin-mike commented 6 years ago

The compiler is with high confidence mcs, since the code snippet is using the UNITY game engine.

ILSpy have problems with mcs-compiler-generated state-machine classes (read: the compiler generated FSM classes generated for yield return).

Until this is fixed, I can only suggest trying to decompile with dnSpy - in my experience it works for this scenario.

Dentrax commented 6 years ago

It was not in the previous ILSpy version. Possibility to be fix in the next release? @siegfriedpammer

tamlin-mike commented 6 years ago

ILSpy 2 and 3 are different beasts. dnSpy is (AFAIK) based on ILSpy 2, so it makes sense they both work.

From my understanding, ILSpy 3 has so far been (user-observable) mostly about getting the decompiler up to speed on new language constructs, on code generated by the Microsoft compilers.

Recognition of a few Mono-isms have been added, but yield-return is not yet one of them. Other problematic areas are array-field-initialization, where it often gets pushed into static constructor, and some foreach constructs.

Dentrax commented 6 years ago

Is there a chance that this problem is fixed? Because this problem is everywhere.

I just uploaded a sample of this problem: .cs file: https://pastebin.com/QEyr5Nvh .il file: https://pastebin.com/F5X4XMGX

Thanks... @siegfriedpammer

dgrunwald commented 6 years ago

Mono is not our main priority. We do somewhat support mcs's 'yield return' pattern, but here this seems to be some interaction between yield and lambdas.

Dentrax commented 6 years ago

It might be really fine to give support for mono/mcs. :)

christophwille commented 6 years ago

Our focus currently is on modern language features for modern platforms (think "Roslyn compilers"). We always try to strive for reach, and currently we are missing important language constructs such as tuples. Those definitely have priority over other more "niche" problems - and sorry to say, this is a niche problem.

Having said all that - we do absolutely welcome contributions in such areas.

siegfriedpammer commented 5 years ago

I was able to partially assemble the IL file. Most of the problems visible in the corresponding C# file have been fixed (probably by the PR mentioned above). However, there's still some strange interaction between lambdas and the yield FSM going on.

For example:

private IEnumerator SignInToGoogleProcess(AsyncOperation op)
{
    EventManager.Instance.Send<DisableNewsContentEventArgs>(new DisableNewsContentEventArgs(true));
    bool gotInternet = false;
    yield return CoroutineHelper.RunCoroutine(SingletonMonoBehaviour<Game>.Instance.IsInternetReachableCoroutineWithRetryPrompt(delegate(bool result)
    {
        base.<gotInternet>__0 = result;
    }));

The DelegateConstruction transform would need some info that the <gotInternet>__0 field access should be transformed to a reference to the local variable introduced by the yield transform.

Dentrax commented 5 years ago

@siegfriedpammer

I can't figure out why there is an base.<gotInternet>__0 variable instead of gotInternet. Why is decompiler doing it this way? Is there a conflict situation? Like:

For Conflict-1: base.<gotInternet>__0 For Conflict-2: base.<gotInternet>__1 For Conflict-3: base.<gotInternet>__2 ... etc.

siegfriedpammer commented 5 years ago

There is no conflict. We just have no support for this special case generated by mcs. Normally there is a separate type generated for each lambda. In this case, however, mcs reuses the type generated for the yield state machine to store the captured variables.

siegfriedpammer commented 5 years ago

Minimal repro:

using System;
using System.Collections;

public class MonoTest
{
    public static IEnumerator Issue1026()
    {
        bool captured = true;
        yield return Test(a => captured = a);
        if (captured) {
            yield return 3;
        }
    }

    public static object Test(Action<bool> test)
    {
        return null;
    }
}

Is decompiled as:

using System;
using System.Collections;

public class MonoTest
{
    public static IEnumerator Issue1026()
    {
        bool captured = true;
        yield return Test(delegate(bool a)
        {
            base.<captured>__0 = a;
        });
        if (captured)
        {
            yield return 3;
        }
    }

    public static object Test(Action<bool> test)
    {
        return null;
    }
}
github-actions[bot] commented 4 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.