// dnlib: See LICENSE.txt for more info using System.Collections.Generic; using System.Diagnostics; using dnlib.DotNet.Emit; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb.Symbols; namespace dnlib.DotNet.Pdb.WindowsPdb { static class PseudoCustomDebugInfoFactory { public static PdbAsyncMethodCustomDebugInfo TryCreateAsyncMethod(ModuleDef module, MethodDef method, CilBody body, int asyncKickoffMethod, IList asyncStepInfos, uint? asyncCatchHandlerILOffset) { var kickoffToken = new MDToken(asyncKickoffMethod); if (kickoffToken.Table != Table.Method) return null; var kickoffMethod = module.ResolveToken(kickoffToken) as MethodDef; var asyncMethod = new PdbAsyncMethodCustomDebugInfo(asyncStepInfos.Count); asyncMethod.KickoffMethod = kickoffMethod; if (asyncCatchHandlerILOffset is not null) { asyncMethod.CatchHandlerInstruction = GetInstruction(body, asyncCatchHandlerILOffset.Value); Debug.Assert(asyncMethod.CatchHandlerInstruction is not null); } int count = asyncStepInfos.Count; for (int i = 0; i < count; i++) { var rawInfo = asyncStepInfos[i]; var yieldInstruction = GetInstruction(body, rawInfo.YieldOffset); Debug.Assert(yieldInstruction is not null); if (yieldInstruction is null) continue; MethodDef breakpointMethod; Instruction breakpointInstruction; if (method.MDToken.Raw == rawInfo.BreakpointMethod) { breakpointMethod = method; breakpointInstruction = GetInstruction(body, rawInfo.BreakpointOffset); } else { var breakpointMethodToken = new MDToken(rawInfo.BreakpointMethod); Debug.Assert(breakpointMethodToken.Table == Table.Method); if (breakpointMethodToken.Table != Table.Method) continue; breakpointMethod = module.ResolveToken(breakpointMethodToken) as MethodDef; Debug.Assert(breakpointMethod is not null); if (breakpointMethod is null) continue; breakpointInstruction = GetInstruction(breakpointMethod.Body, rawInfo.BreakpointOffset); } Debug.Assert(breakpointInstruction is not null); if (breakpointInstruction is null) continue; asyncMethod.StepInfos.Add(new PdbAsyncStepInfo(yieldInstruction, breakpointMethod, breakpointInstruction)); } return asyncMethod; } static Instruction GetInstruction(CilBody body, uint offset) { if (body is null) return null; var instructions = body.Instructions; int lo = 0, hi = instructions.Count - 1; while (lo <= hi && hi != -1) { int i = (lo + hi) / 2; var instr = instructions[i]; if (instr.Offset == offset) return instr; if (offset < instr.Offset) hi = i - 1; else lo = i + 1; } return null; } } }