Is this what you aspect from debugger? (#D10Seattle)

Is this what you aspect from debugger? (#D10Seattle)

Debug this code following execution step by step:

var
  L : TDictionary;
  I : Int64;
  S : String;
begin
  L:=TDictionary.Create;
  try
    L.AddOrSetValue(1,'aaaaa');
    S:='';
    for I in L.Keys do
        S:=S+L.Items[I];  // <--- executed two times? NO
  finally
    L.Free;
  end;

It seems that the higlighted line is executed two times but its not. I suppose that the second time is related to the end of the for loop but this is a little bit misleading.

Comments

  1. Cesar Romero ok you are right but I often omit begin..end for very simple one instruction loops (for me is more clear in this way). Pascal allows me to do it so I aspect that the Delphi debugger works fine too :)

    ReplyDelete
  2. The compiler generates some code for a for-in loop that also gets debug symbols (which includes a line number). If you don't have begin/end for its body they get placed in the same line number as the body of the loop. That is why when stepping through it looks like its hitting the body one more time than it should (look into the disassembly for more info).

    This code for example:

    procedure Main;
    var
      l: TList;
      i: Integer;
    begin
      l := TList.Create;
      l.AddRange([1, 2, 3]);
      for i in l do
        Writeln(i);
    end;

    generates this asm (seattle, x86, $O+) for the loop:

    Project77.dpr.18: for i in l do
    004327C4 8BCB             mov ecx,ebx
    004327C6 B201             mov dl,$01
    004327C8 A1D84C4300       mov eax,[$00434cd8]
    004327CD E8F23D0000       call {System.Generics.Collections}TList.TEnumerator.Create
    004327D2 8945FC           mov [ebp-$04],eax
    004327D5 33C0             xor eax,eax
    004327D7 55               push ebp
    004327D8 682F284300       push $0043282f
    004327DD 64FF30           push dword ptr fs:[eax]
    004327E0 648920           mov fs:[eax],esp
    004327E3 EB20             jmp $00432805
    004327E5 8B45FC           mov eax,[ebp-$04]
    004327E8 E89F3D0000       call {System.Generics.Collections}TList.TEnumerator.GetCurrent
    004327ED 8BD8             mov ebx,eax
    Project77.dpr.19: Writeln(i);
    004327EF A18CB44300       mov eax,[$0043b48c]
    004327F4 8BD3             mov edx,ebx
    004327F6 E88532FDFF       call @Write0Long
    004327FB E86035FDFF       call @WriteLn
    00432800 E8BF26FDFF       call @_IOTest
    Project77.dpr.18: for i in l do
    00432805 8B45FC           mov eax,[ebp-$04]
    00432808 E8FF3D0000       call {System.Generics.Collections}TList.TEnumerator.MoveNext
    0043280D 84C0             test al,al
    0043280F 75D4             jnz $004327e5
    00432811 33C0             xor eax,eax
    00432813 5A               pop edx
    00432814 59               pop ecx
    00432815 59               pop ecx
    00432816 648910           mov fs:[eax],edx
    00432819 6836284300       push $00432836
    Project77.dpr.19: Writeln(i);
    0043281E 837DFC00         cmp dword ptr [ebp-$04],$00
    00432822 740A             jz $0043282e
    00432824 B201             mov dl,$01
    00432826 8B45FC           mov eax,[ebp-$04]
    00432829 8B08             mov ecx,[eax]
    0043282B FF51FC           call dword ptr [ecx-$04]
    0043282E C3               ret 
    0043282F E91847FDFF       jmp @HandleFinally
    00432834 EBE8             jmp $0043281e

    As you can see both statements are hit one time more than you would expect. The first extra hit on the loop header is caused by the (inlined) GetEnumerator call and the last hit on the body is caused by the cleanup code for the enumerator (which gets placed into the line of the end in case you have one).

    ReplyDelete
  3. I do not agree! IMHO when i started working with Delphi that behaviour of the debugger immediately made me more aware of the code being produced. When i get confused i press F7 (and sometimes follow up with Shift+F8), this shows me very clearly what happened during compilation. This resulting knowledge is very important, not the least when working with multi threaded code. In effect i am not for "hiding" stuff from developers.

    ReplyDelete

Post a Comment