3

Out of curiosity I decided to try decompiling my project's code. I took the Assembly .dll file and used ILSpy to decompile it. It seems to be working correctly, except for IEnumerator<> methods.

IEnumerator sP()
{
        for (int i = 0; i < maxEnemies; i++)
        {
            var p = Porczaks[Random.Range(0, Porczaks.Length)];
            Instantiate(p, new Vector3(Random.Range(245, 360), 16.8f, Random.Range(292, 366)), Quaternion.Euler(0f, Random.Range(0f, 359f), 0f));
            yield return new WaitForEndOfFrame();
        }
}

... is for example being decoded into this:

[DebuggerHidden]
private IEnumerator sP()
{
    WaveManager.<sP>c__Iterator7 <sP>c__Iterator = new WaveManager.<sP>c__Iterator7();
    <sP>c__Iterator.<>f__this = this;
    return <sP>c__Iterator;
}

Is there any way to accurately decompile that IEnumerator?

EDIT: I decompiled the same assembly using the dotPeek decompiler, and it created way more code. Though I'm still not sure if variables can have such names in .net:

// Method sP with token 060000AB
[/*Attribute with token 0C000051*/DebuggerHidden]
private IEnumerator sP()
{
    WaveManager.\u003CsP\u003Ec__Iterator7 sPCIterator7 = new WaveManager.\u003CsP\u003Ec__Iterator7();
    sPCIterator7.\u003C\u003Ef__this = this;
    return (IEnumerator) sPCIterator7;
}

// Type <sP>c__Iterator7 with token 02000031
[/*Attribute with token 0C000026*/CompilerGenerated]
private sealed class \u003CsP\u003Ec__Iterator7 : IEnumerator<object>, IEnumerator, IDisposable
{
    // Field <i>__0 with token 040000C7
    internal int \u003Ci\u003E__0;
    // Field <p>__1 with token 040000C8
    internal GameObject \u003Cp\u003E__1;
    // Field $PC with token 040000C9
    internal int \u0024PC;
    // Field $current with token 040000CA
    internal object \u0024current;
    // Field <>f__this with token 040000CB
    internal WaveManager \u003C\u003Ef__this;

    // Property System.Collections.Generic.IEnumerator<object>.Current with token 17000017
    object IEnumerator<object>.System\u002ECollections\u002EGeneric\u002EIEnumerator\u003Cobject\u003E\u002ECurrent
    {
      // Method System.Collections.Generic.IEnumerator<object>.get_Current with token 060000EA
      [/*Attribute with token 0C00006E*/DebuggerHidden] get
      {
        return this.\u0024current;
      }
    }

    // Property System.Collections.IEnumerator.Current with token 17000018
    object IEnumerator.Current
    {
      // Method System.Collections.IEnumerator.get_Current with token 060000EB
      [/*Attribute with token 0C00006F*/DebuggerHidden] get
      {
        return this.\u0024current;
      }
    }

    // Method .ctor with token 060000E9
    public \u003CsP\u003Ec__Iterator7()
    {
      base.\u002Ector();
    }

    // Method MoveNext with token 060000EC
    public bool MoveNext()
    {
      uint num = (uint) this.\u0024PC;
      this.\u0024PC = -1;
      switch (num)
      {
        case 0:
          this.\u003Ci\u003E__0 = 0;
          break;
        case 1:
          this.\u003Ci\u003E__0 = this.\u003Ci\u003E__0 + 1;
          break;
        default:
          return false;
      }
      if (this.\u003Ci\u003E__0 < this.\u003C\u003Ef__this.maxEnemies)
      {
        this.\u003Cp\u003E__1 = this.\u003C\u003Ef__this.Porczaks[UnityEngine.Random.Range(0, this.\u003C\u003Ef__this.Porczaks.Length)];
        UnityEngine.Object.Instantiate((UnityEngine.Object) this.\u003Cp\u003E__1, new Vector3((float) UnityEngine.Random.Range(245, 360), 16.8f, (float) UnityEngine.Random.Range(292, 366)), Quaternion.Euler(0.0f, UnityEngine.Random.Range(0.0f, 359f), 0.0f));
        this.\u0024current = (object) new WaitForEndOfFrame();
        this.\u0024PC = 1;
        return true;
      }
      this.\u0024PC = -1;
      goto default;
    }

    // Method Dispose with token 060000ED
    [/*Attribute with token 0C000070*/DebuggerHidden]
    public void Dispose()
    {
      this.\u0024PC = -1;
    }

    // Method Reset with token 060000EE
    [/*Attribute with token 0C000071*/DebuggerHidden]
    public void Reset()
    {
      throw new NotSupportedException();
    }
}

Seems dotPeek didn't process < and > correctly, but is this code worth anything?

Reynevan
  • 1,475
  • 1
  • 18
  • 35
  • That does indeed look like what the compiler would generate. – Enigmativity Aug 03 '16 at 03:07
  • Having said that JetBrains dotPeek seems to do a better job at this. – Enigmativity Aug 03 '16 at 03:09
  • I've checked dotPeek and when showing tokens is disabled in code it looks almost the same as code from ILSpy, but with that enabled it becomes really confusing. I posted the code in the first post. Does it make any sense? Would it work if I just changed the names from `__+@!#` to something normal? – Reynevan Aug 03 '16 at 12:01
  • If you change the odd looking names to valid C# it should work fine. It's just a state machine. When I tested it earlier dotPeek fully decompiled though - but I did use a simpler code example. – Enigmativity Aug 03 '16 at 12:25
  • There's a major problem though. I'd love to be able to just change all these names directly in dotPeek, since when I save the code as VS project it seems to be skipping all these `<>_enumerator` classes, so I have to directly copy them from the dotPeek code viewer, and then manually pix all unicode characters, declarations and names and basically the entire interface implementation. I did it for one `IEnumerator`s in my code, but can't test it since there are 6-7 more to go if I want to compile the dll. Are there any extensions to dP that would let me do most of this stuff directly from it? – Reynevan Aug 03 '16 at 12:41
  • Sorry, never looked to see if there are. I doubt it though. – Enigmativity Aug 03 '16 at 12:46
  • I think I found something interesting. dnSpy seems to allow editing the assembly without exporting it to VS first, and it exports iterator classes correctly, as nested class of the one it references. So basically I just need to optimize my workflow. Most changes I can do with simple `Left click, Alt+Enter, namechange, Enter, Repeat`, then I export it as C# solution and fix remaining errors manually... seems I've found myself a new hobby. – Reynevan Aug 03 '16 at 13:17
  • Thanks for help with all this. I'm not exactly sure how to do this correctly jus tyet but it's been a huge kickstart nevertheless. – Reynevan Aug 03 '16 at 13:18

1 Answers1

2

You really see the boilerplate code the compiler generates to replace the yield return statement. And yes it is indeed a state machine.

Normally a decompiler should be able to recognize the compiler generated boilerplate code and replace it with the correct C# statement. That recognition, however, is done by pattern matching, i.e. the boilerplate code is expected to be structured in a very specific way. If the compiler produces equivalent code but with different structure (e.g. because of compiler upgrade, optimizations, etc.) then the decompiler fails to match the pattern and recognize the yield statement.

What you should do is submit a bug report to the decompiler teams so that this gets fixed and you don't have to rename stuff manually. Have you tried JustDecompile on that assembly? Does it fail, too? If so, you could post this in the Telerik forums and we'll take care of it.

On a side note, what compiler did you use?

TsviatkoYov
  • 297
  • 1
  • 5