BORLAND: Memory loss/strings/variant arrays TTable.Locate

This message if forwarded from Max Nilson who I thank for taking the
time to analyse it.

It is fo interest to anyone using Varient arrays to hold ling strings,
or calling and methods that do this (eg TTable.Locate and
TTable.Lookup when searching more than one field)



For those of you who don't like reading through reams of assembler I'm
sorry, but its the only way I can show that a bug exists in the
Variant code in Delphi 2.0 (build to be precise).

For those of you who haven't been following the bug that Wade has
detected, the following code will generate a continual memory leakage
in the Delphi memory manager:

procedure TForm1.Button1Click(Sender: TObject);
  procedure FindIt(a, b: string);
    Table1.Locate('Surname;Firstnames', VarArrayOf([a,b]), []);
    FindIt('Bloggs', 'Joe');
    Label1.Caption := IntToStr(GetHeapStatus.TotalAllocated);
  until Application.Terminated;

At first I wasn't sure that there was any problem in the code that I
checked (see my post of early this afternoon) but an exhaustive grub
though the assembler generated by Wade's example has revealed the
problem. To see for yourself read the annotated assembler list that I
have attached at the bottomof this post.

The bug seems to occur as the compiler is generating the Variant
copies of the parameter strings. It makes one copy of each string from
the parameters that it will later release, and it makes a second copy,
from the first copy, that uses to initialise the open array, for
VarArrayOf, that it "forgets" to release. Thus the memory leak.

Therefore at least one bug has been identified in the compiler! I
suspect that a second may be lurking somewhere as Wade's first example
doesn't use an open array.

What's the best way to start hassling Borland about this annoying
problem with the compiler handling of local temporary Variants? And
should the bug be posted on to the appropiate UseNet Delphi group so
that everyone can be made aware of the problem?


;procedure FindIt(a, b: string);
; Build us a stack frame
        push   ebp
        mov    ebp,esp
; Create lots of null Variants
        mov    ecx,0000000B
@1      push   00000000
        push   00000000
        dec    ecx
        jne    @1
; Save away registers
        push   ebx
        push   esi
        push   edi
; Save away paremeters
        mov    [ebp-08],edx                       ; Parameter b
        mov    [ebp-04],eax                       ; Parameter a
; Increment parameter string references
        mov    eax,[ebp-04]
        call   @LStrAddRef                        ; Inc param a
        mov    eax,[ebp-08]
        call   @LStrAddRef                        ; Inc param b
; Build exception handling frame
        xor    eax,eax
        push   ebp
        push   @finally
        push   fs:dword ptr [eax]
        mov    fs:[eax],esp
;Table1.Locate('Surname;Firstnames', VarArrayOf([a,b]), []);
; Push Options constant []
        mov    al,[TESTPROJ.004455B4]
        push   eax
; Convert parameter a into a local Variant at [ebp-48]
        lea    eax,[ebp-48]
        mov    edx,[ebp-04]
        call   @VarFromLStr
; Copy Variant [ebp-48] to [ebp-38] (into the open array)
        lea    edx,[ebp-48]
        lea    eax,[ebp-38]
        call   @VarCopy
; Convert parameter a into b local Variant at [ebp-58]
        lea    eax,[ebp-58]
        mov    edx,[ebp-08]
        call   @VarFromLStr
; Copy Variant [ebp-58] to [ebp-28] (into the open array)
        lea    edx,[ebp-58]
        lea    eax,[ebp-28]
        call   @VarCopy
; Construct Variant array [ebp-18] from [ebp-38] and [ebp-28]
; During this process the variants are copied into the newly created
; array and in the process the strings are converted to OLEStrings
; allocated via SysAllocStringLen
        lea    eax,[ebp-38]
        lea    ecx,[ebp-18]
        mov    edx,00000001
        call   VarArrayOf
; Call locate
        lea    ecx,[ebp-18]
        mov    eax,[ebp+08]
        mov    eax,[eax-04]
        mov    eax,[eax+000001B4]
        mov    edx,004455C0
        call   TDataSet.Locate
; Restore registers/unwind exception frame
        xor    eax,eax
        pop    edx
        pop    ecx
        pop    ecx
        mov    fs:[eax],edx
        push   @end
; end
; Clear the Variants [ebp-58] and [ebp-48]
        lea    eax,[ebp-58]
        mov    edx,0040109C
        mov    ecx,00000002
        call   @FinalizeArray
; Clear the Variant Array [ebp-18]
        lea    eax,[ebp-18]
        call   @VarClr
; Lower the reference counts on the parameter strings
        lea    eax,[ebp-08]
        mov    edx,00000002
        call   @LStrArrayClr
; <<<<<<<<<<<<<<<< BUG >>>>>>>>>>>>>>>>>>>>>
; What about the variants at [ebp-38] and [ebp-28] used to build
; the array? They havn't been cleared and thus their string
; reference counts are still at 1!!!!
; <<<<<<<<<<<<<<<< BUG >>>>>>>>>>>>>>>>>>>>>
        jmp    @HandleFinally
        jmp    @end


And there we have it. I certainly know a lot more about variants now!

Cheers, Max.