I forrige indlæg viste jeg, hvordan vi får returværdien fra en metode ved hjælp af WinDbg. Denne gang skal vi se på en lidt mere kompliceret situation. Hvordan finder vi returværdien fra en anonym metode?
Givet nedenstående kode:
Func<int, int> f = x => x + 1; Console.WriteLine(f.Invoke(1));
Hvordan finder vi returværdien for den metode, f repræsenterer?
Faktisk er problemstillingen ikke så kompliceret, så man måske skulle tro. Anonyme metoder er nemlig ikke spor anonyme, når det kommer til stykket. De er blot pakket ind i lidt compilerhekseri, så den første opgave er, at finde ud af hvilket navn den anonyme metode gemmer sig under.
Det kan vi læse ud af IL-koden, så lad os se nærmere på den for den omsluttende metode. I det her tilfælde er det Main(), så lad os se på den. For at se på IL-koden via WinDbg, har vi brug for Main()s MethodDesc. Den kan vi finde med !name2ee:
0:000> !name2ee * TestBench.Program.Main Module: 6db11000 (mscorlib.dll) -------------------------------------- Module: 00162c5c (TestBench.exe) Token: 0x06000001 MethodDesc: 00163010 Name: TestBench.Program.Main() JITTED Code Address: 00270070
MethodDesc for Main() er altså 00163010. Lad os se på IL for metoden.
0:000> !dumpil 00163010 ilAddr = 013c2064 IL_0000: nop IL_0001: ldstr "press enter" IL_0006: call System.Console::WriteLine IL_000b: nop IL_000c: call System.Console::ReadLine IL_0011: pop IL_0012: ldsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 IL_0017: brtrue.s IL_002c IL_0019: ldnull IL_001a: ldftn TestBench.Program::<Main>b__0 IL_0020: newobj class [System.Core]System.Func`2<int32,int32>::.ctor IL_0025: stsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 IL_002a: br.s IL_002c IL_002c: ldsfld TestBench.Program::CS$<>9__CachedAnonymousMethodDelegate1 IL_0031: stloc.0 IL_0032: ldloc.0 IL_0033: ldc.i4.1 IL_0034: callvirt class [System.Core]System.Func`2<int32,int32>::Invoke IL_0039: call System.Console::WriteLine IL_003e: nop IL_003f: ret
Læg mærke til de spøjse navne. Det er navnene på henholdsvis den generede delegate type og den faktiske metode, der implementerer vores anonyme metode. Vores metode hedder altså TestBench.Program::. Lad os se nærmere på den:
0:000> !name2ee * TestBench.Program.<Main>b__0 Module: 6db11000 (mscorlib.dll) -------------------------------------- Module: 00152c5c (TestBench.exe) Token: 0x06000003 MethodDesc: 00153024 Name: TestBench.Program.<Main>b__0(Int32) Not JITTED yet. Use !bpmd -md 00153024 to break on run.
Som det fremgår, er den endnu ikke oversat, så vi er nødt til at gå via et breakpoint på MD som foreslået i udskriften.
0:000> !bpmd -md 00153024 MethodDesc = 00153024 Adding pending breakpoints...
Så er det blot at køre videre, til vi rammer vores breakpoint.
0:000> g (1ad0.ecc): CLR notification exception - code e0444143 (first chance) JITTED TestBench!TestBench.Program.<Main>b__0(Int32) Setting breakpoint: bp 00260118 [TestBench.Program.<Main>b__0(Int32)] Breakpoint 0 hit eax=00153024 ebx=0038efdc ecx=00000001 edx=00000001 esi=006255f0 edi=00000000 eip=00260118 esp=0038efa0 ebp=0038efb0 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 00260118 55 push ebp
Metoden er JIT-oversat, og vi er stoppet i begyndelsen af den. Som i første eksempel skal vi altså blot køre til slutningen og inspicere det korrekte register.
0:000> g $ra eax=00000002 ebx=0038efdc ecx=00000001 edx=00000000 esi=006255f0 edi=00000000 eip=002600f0 esp=0038efa4 ebp=0038efb0 iopl=0 nv up ei pl nz na po nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202 002600f0 8945fc mov dword ptr [ebp-4],eax ss:002b:0038efac=0038f1d8 0:000> ? $retreg Evaluate expression: 2 = 00000002
Og der har vi vores returværdi. Havde metoden returneret long i stedet for int, skulle vi blot bruge $retreg64 i stedet.