private void SetupTaskContinuation() { if (_returnPoint != null || _originalReturnPoint != null) { throw new InvalidOperationException("Something went wrong"); } var helper = GetOrCreateHelperClass(); var continuation = new MethodDefinition( string.Format(ContinuationMethodName, TargetMethod.Name), MethodAttributes.Assembly, TargetMethod.Module.TypeSystem.Void); helper.Methods.Add(continuation); var tcsType = ModuleContext.TypeSystem.TaskCompletionGeneric.MakeGenericType(_completionResultType); var taskTypedType = continuation.Module.Import(_hasResult ? TargetMethod.ReturnType : ModuleContext.TypeSystem.Task); var taskParameter = new ParameterDefinition(null, ParameterAttributes.None, taskTypedType); continuation.Parameters.Add(taskParameter); var tcsField = new FieldDefinition(string.Format(ContinuationFieldName, TargetMethod.Name), FieldAttributes.Public, tcsType); helper.Fields.Add(tcsField); var proc = ILProcessorFactory.GetOrCreateProcessor(continuation.Body); var ret = proc.Create(OpCodes.Ret); proc.Append(ret); var pointcut = new PointCut(proc, ret); pointcut.LoadParameterOntoStack(taskParameter); pointcut.InjectMethodCall(ModuleContext.TypeSystem.Task.Resolve().Properties.First(p => p.Name == "IsCompleted").GetMethod, new object[] { }); pointcut.TestValueOnStack(true, doIfTrue: pc => { pc.LoadParameterOntoStack(taskParameter); pc.InjectMethodCall(ModuleContext.TypeSystem.Task.Resolve().Properties.First(p => p.Name == "IsFaulted").GetMethod, new object[] { }); pc.TestValueOnStack(false, doIfTrue: pct => { if (_hasResult) { _resultVar = pct.CreateVariable(_completionResultType, loadData: c => { c.LoadParameterOntoStack(taskParameter); c.InjectMethodCall(taskTypedType.Resolve().Properties.First(p => p.Name == "Result").GetMethod.MakeGeneric(taskTypedType), new object[] { }); }); } var syncReturnPc = pct.InsertBefore(pct.CreateInstruction(OpCodes.Nop)); _returnPoint = new AsyncPointCut(_helperThisRefFiled, _helperArgumentsFiled, proc, syncReturnPc.InjectionPoint); var setresultMethod = tcsType.Resolve().Methods.First(m => m.Name == "SetResult").MakeGeneric(tcsType); pct.LoadSelfOntoStack(); pct.LoadField(tcsField); pct.InjectMethodCall(setresultMethod, new object[] { _resultVar ?? Markers.DefaultMarker }); }); }, doIfFalse: pc => { var setresultMethod = tcsType.Resolve().Methods.First(m => m.Name == "SetResult").MakeGeneric(tcsType); pc.LoadSelfOntoStack(); pc.LoadField(tcsField); pc.InjectMethodCall(setresultMethod, new object[] { Markers.DefaultMarker }); }); VariableDefinition taskResult = null; if (_isVoid) { taskResult = OriginalEntryPoint.CreateVariable(ModuleContext.TypeSystem.Task); } else { taskResult = OriginalEntryPoint.CreateVariable(TargetMethod.ReturnType); } var processor = ILProcessorFactory.GetOrCreateProcessor(TargetMethod.Body); var singleReturnPoint = processor.Create(OpCodes.Nop); _originalReturnPoint = new PointCut(processor, SetupSingleReturnPoint(processor.Create(OpCodes.Br, singleReturnPoint), taskResult)); //todo:: optimize if (_isVoid) { AsyncVoidRewriter.Rewrite(_originalReturnPoint, TargetMethod, taskResult); } processor.SafeAppend(singleReturnPoint); PointCut continuationPoint = null; if (!_isVoid) { continuationPoint = new PointCut(processor, processor.SafeAppend(processor.CreateOptimized(OpCodes.Ldloc, taskResult.Index))); processor.SafeAppend(processor.Create(OpCodes.Ret)); } else { continuationPoint = new PointCut(processor, processor.SafeAppend(processor.Create(OpCodes.Ret))); } // var tcs = new TaskContinuationSource<TResult>(); var tcsctor = tcsType.Resolve().Methods.First(m => m.IsConstructor && !m.IsStatic).MakeGeneric(tcsType); var tcsVar = continuationPoint.CreateVariable(tcsType, null, c => c.InjectMethodCall(tcsctor)); // var helper = new Helper(); var helperVar = continuationPoint.CreateVariable(helper, null, c => c.InjectMethodCall(helper.Methods.First(m => m.IsConstructor && !m.IsStatic))); // var args = new object[] { param1, param2 ... }; var argsvar = continuationPoint.CreateVariable(new ArrayType(TargetMethod.Module.TypeSystem.Object), null, c => c.LoadCallArgument(TargetMethod.Parameters.ToArray(), new ArrayType(TargetMethod.Module.TypeSystem.Object))); //helper.this_ref = this continuationPoint.LoadVariable(helperVar); continuationPoint.SetField(_helperThisRefFiled, c => c.LoadSelfOntoStack()); // helper.args = args continuationPoint.LoadVariable(helperVar); continuationPoint.SetField(_helperArgumentsFiled, c => c.LoadVariable(argsvar)); // helper.continuationSource = tcs continuationPoint.LoadVariable(helperVar); continuationPoint.SetField(tcsField, c => c.LoadVariable(tcsVar)); // task.ContinueWith(new Action<TResult>(helper.Continuation)) continuationPoint.LoadVariable(helperVar); continuationPoint.InsertBefore(continuationPoint.CreateInstruction(OpCodes.Ldftn, continuation)); var actionTr = continuation.Module.Import(ModuleContext.TypeSystem.ActionGeneric.MakeGenericType(taskTypedType)); var contActionCtor = continuation.Module.Import(actionTr.Resolve().Methods.First(m => m.IsConstructor && !m.IsStatic)) .MakeGeneric(actionTr); var actionVar = continuationPoint.CreateVariable(actionTr, null, c => c.InsertBefore(continuationPoint.CreateInstruction(OpCodes.Newobj, (MethodReference)c.CreateMemberReference(contActionCtor)))); MethodReference contWithMethod = continuation.Module.Import(taskTypedType.Resolve().Methods.First(m => m.Name == "ContinueWith" && m.Parameters.Count == 1)); if (_hasResult) { contWithMethod = contWithMethod.MakeGeneric(taskTypedType); } continuationPoint.LoadVariable(taskResult); continuationPoint.InjectMethodCall(contWithMethod, new object[] { actionVar }); continuationPoint.InsertBefore(continuationPoint.CreateInstruction(OpCodes.Pop)); // task = tcs.Task if (!_isVoid) { var getTask = continuation.Module.Import(tcsType.Resolve().Properties.First(p => p.Name == "Task").GetMethod) .MakeGeneric(tcsType); continuationPoint.SetVariable(taskResult, c => { c.LoadVariable(tcsVar); c.InjectMethodCall(getTask); }); } }