Unity2017下async函数修复后执行异常
版本:Unity2017.4.40c1

重现代码:
using IFix.Core;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UnityEngine;
public class Test : MonoBehaviour
{
void Start()
{
var patchPath = "./Assembly-CSharp.patch.bytes";
if (File.Exists(patchPath))
{
PatchManager.Load(new FileStream(patchPath, FileMode.Open));
Debug.Log("patch file loaded:" + patchPath);
}
CallOneAsync("hello");
}
[IFix.Patch]
public async void CallOneAsync(string name)
{
Debug.Log(name);
await Task.Delay(100);
Debug.Log(name);
await Task.Delay(100);
Debug.Log(name);
}
}
生成Patch并Inject后启动游戏:

期望结果: 日志输出三次hello
同样的代码在Unity2019.4.19f1是能够正常工作的。
触发这个问题的原因:
Unity2017和Unity2019为运行时环境生成的IAsyncStateMachine状态机前者是struct后者是class

在CodeTranslator里为类似<CallOneAsync>c__async0桥接类型时,在addAnonymousCtor中,误把第一个方法作为匿名类型的构造方法。在为class的情况下是正常的.ctor,但在struct的情况下拿到的则是MoveNext。
在VirtualMachine运行时执行补丁代码时,会在Code.Newanon中构造出上面的桥接类型,并调用构造方法,但实际上调用的是MoveNext。所以看起来似乎是执行了async流程,调用到了CallOneAsync中第一行的Debug.Log(name)。而这时还没有执行真正的CallOneAsync,也就是<CallOneAsync>c__async的name这些属性都还没有赋值,所以日志输出的name就是Null了,同理整个状态机也没有启动,所以也不会执行后续阶段的异步代码。
处理构造方法比较简单,在生成匿名类型信息时,如果默认方法不是构造方法直接写-1,运行时执行Code.Newanon时跳过,这样就能正常执行CallOneAsync并启动async状态机,从而在后续正确的流程中调用MoveNext。
但还有个问题是MoveNext执行完第一阶段时,会进入到:
this.$builder.AwaitUnsafeOnCompleted<TaskAwaiter, Test.<CallOneAsync>c__async0>(ref this.$awaiter0, ref this);。
然后进入到m_coreState.PostBoxInitialization:

这里的stateMachine已经被包装成了注入后的桥接对象ILFixInterfaceBridge,也就是说m_coreState.m_stateMachine在第一行就已经被赋值了。
第二行会进入被补丁的SetStateMachine,从而执行到this.$builder.SetStateMachine(stateMachine);,这个$builder是个AsyncVoidMethodBuilder,里边的逻辑是:

这里的m_coreState是AsyncMethodBuilderCore,由于前面已经赋值过m_stateMachine,最终导致抛出异常:

而struct因为需要拷贝所以不会导致这个问题,另外Unity2019环境下虽然也是class,但编译器为async状态机对象生成出来的SetStateMachine是个空实现,所以也不会有这个问题。
目前想到的解决方法是在生成桥接对象时,如果是编译器生成的struct且方法为SetStateMachine,就直接留空实现。