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.
Debug this code following execution step by step:
var
L : TDictionary
I : Int64;
S : String;
begin
L:=TDictionary
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.
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 :)
ReplyDeleteThe 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).
ReplyDeleteThis 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).
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