LoginSignup
3
2

More than 5 years have passed since last update.

macOSでC#のasync/awaitを逆コンパイルする

Last updated at Posted at 2018-05-11

tl;dr

macOSでC#の逆コンパイルの記事の

$ git clone https://github.com/icsharpcode/ILSpy.git

$ git clone https://github.com/tatsumack/ILSpy.git
$ cd ILSpy
$ git checkout -t origin/visible_async_await_decompile
$ cd ../

に置き換えて、ILSpyのインストールを行う。

背景

Unite2018の講演「さては非同期だなオメー!async/await完全に理解しよう」でasync/awaitのコードを逆コンパイルしていたので、実際に自分もやってみようと思った。
手元にあるPCがmacOSだったので、オープンソースのデコンパイラであるILSpyを利用した逆コンパイル方法を調べ、macOSでC#の逆コンパイルの記事にまとめた。

しかし、ILSpyのCLIツールでは、なぜかasync/awaitの逆コンパイル結果が表示されず。。
ILSpyのコードを読んでみると、CLIツールではasync/awaitの逆コンパイル結果が表示されないようなオプションとなっていることが分かった。

ということで、


static CSharpDecompiler GetDecompiler(string assemblyFileName)
{
- return new CSharpDecompiler(assemblyFileName, new DecompilerSettings() {  ThrowOnAssemblyResolveErrors = false });
+ return new CSharpDecompiler(assemblyFileName, new DecompilerSettings() {  ThrowOnAssemblyResolveErrors = false, AsyncAwait = false });
}

と修正を行ったところ、async/awaitの逆コンパイル結果が表示されるようになった:tada:
元レポジトリをforkして修正したブランチはこちら
https://github.com/tatsumack/ILSpy/tree/visible_async_await_decompile

逆コンパイル結果

async/awaitを逆コンパイルしてみると、state machineが内部に生成されていることが分かる。

コンパイル前

using System;
using System.Threading;
using System.Threading.Tasks;

class HelloWorld {
    public static int Main( )
    {
        var result = AsyncTest().Result;
        Console.WriteLine(result);
        return 0;
    }

    static async Task<string> AsyncTest() {
        var before = DateTime.Now;
        Console.WriteLine(before);

        await Task.Delay(5000);

        var after = DateTime.Now;
        Console.WriteLine(after);
        return before + "-" + after;
    }
}

逆コンパイル後

using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
internal class HelloWorld
{
    [StructLayout(LayoutKind.Auto)]
    [CompilerGenerated]
    private struct <AsyncTest>c__async0 : IAsyncStateMachine
    {
        internal DateTime <before>__0;

        internal DateTime <after>__0;

        internal AsyncTaskMethodBuilder<string> $builder;

        internal int $PC;

        private TaskAwaiter $awaiter0;

        public void MoveNext()
        {
            uint num = (uint)$PC;
            $PC = -1;
            string result;
            try
            {
                switch (num)
                {
                default:
                    return;
                case 0u:
                    <before>__0 = DateTime.Now;
                    Console.WriteLine((object)<before>__0);
                    $awaiter0 = Task.Delay(5000).GetAwaiter();
                    if ($awaiter0.IsCompleted)
                    {
                        break;
                    }
                    $PC = 1;
                    $builder.AwaitUnsafeOnCompleted(ref $awaiter0, ref this);
                    return;
                case 1u:
                    break;
                }
                $awaiter0.GetResult();
                <after>__0 = DateTime.Now;
                Console.WriteLine((object)<after>__0);
                result = <before>__0 + "-" + <after>__0;
            }
            catch (Exception exception)
            {
                $PC = -1;
                $builder.SetException(exception);
                return;
            }
            $PC = -1;
            $builder.SetResult(result);
        }

        [DebuggerHidden]
        public void SetStateMachine(IAsyncStateMachine stateMachine)
        {
            $builder.SetStateMachine(stateMachine);
        }
    }

    public static int Main()
    {
        string result = AsyncTest().Result;
        Console.WriteLine(result);
        return 0;
    }

    [DebuggerStepThrough]
    [AsyncStateMachine(typeof(<AsyncTest>c__async0))]
    private static Task<string> AsyncTest()
    {
        <AsyncTest>c__async0 <AsyncTest>c__async = default(<AsyncTest>c__async0);
        <AsyncTest>c__async.$builder = AsyncTaskMethodBuilder<string>.Create();
        ref AsyncTaskMethodBuilder<string> $builder = ref <AsyncTest>c__async.$builder;
        $builder.Start(ref <AsyncTest>c__async);
        return $builder.Task;
    }
}

解説は下記ページが参考になると思います。
https://www.slideshare.net/UnityTechnologiesJapan/unite-tokyo-2018asyncawait
http://blog.xin9le.net/entry/2012/08/06/123916

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2