#1 2013-07-01 11:54:01

Dangph
Member
From: Australia
Registered: 2013-07-01
Posts: 5

Memory corruption bug in TDynArray.AddArray ?

Consider this program:

program Project49;

{$APPTYPE CONSOLE}

uses
  SysUtils, SynCommons;

type
  TRec = packed record
    val1: string;
    val2: string;
  end;

  TRecArray = array of TRec;

function recurse(i: integer): TRecArray;
var
  dyn: TDynArray;
  rec: TRec;
  childVals: TRecArray;
begin
  dyn.Init(TypeInfo(TRecArray), result);
  rec.Val1 := Format('This is Val1: %d', [i]);
  rec.Val2 := Format('This is Val2: %d', [i]);
  dyn.Add(rec);

  if i > 0 then
  begin
    childVals := recurse(i - 1);
    dyn.AddArray(childVals);
  end;
end;

var
  recs: TRecArray;
  rec: TRec;
begin
  recs := recurse(10);

  for rec in recs do
  begin
    WriteLn(rec.Val1);
    WriteLn(rec.Val2);
  end;
end.

I would expect to see this output:

This is Val1: 10
This is Val2: 10
This is Val1: 9
This is Val2: 9
This is Val1: 8
This is Val2: 8
This is Val1: 7
This is Val2: 7
This is Val1: 6
This is Val2: 6
This is Val1: 5
This is Val2: 5
This is Val1: 4
This is Val2: 4
This is Val1: 3
This is Val2: 3
This is Val1: 2
This is Val2: 2
This is Val1: 1
This is Val2: 1
This is Val1: 0
This is Val2: 0

But instead I see this:

This is Val1: 10
This is Val2: 10
This is Val1: 9
This is Val2: 9
This is Val1: 8
This is Val2: 8
This is Val1: 7
This is Val2: 7
This is Val1: 6
This is Val2: 6
This is Val1: 5
This is Val2: 5

(There are some blank lines at the end, but FluxBB has chopped them.) The last 5 records in the array contain empty strings. Is this a memory corruption bug?

I am using Delphi 2007, and the latest version of SynCommons.

Last edited by Dangph (2013-07-01 11:54:40)

Offline

#2 2013-07-01 23:49:55

Dangph
Member
From: Australia
Registered: 2013-07-01
Posts: 5

Re: Memory corruption bug in TDynArray.AddArray ?

If I change the code as follows, then it works as expected. The changed lines are the ones marked.

function recurse(i: integer): TRecArray;
var
  dyn: TDynArray;
  rec: TRec;
  childVals: TRecArray;
begin
  dyn.Init(TypeInfo(TRecArray), result);
  rec.Val1 := Format('This is Val1: %d', [i]);
  rec.Val2 := Format('This is Val2: %d', [i]);
  dyn.Add(rec);

  if i > 0 then
  begin
    childVals := recurse(i - 1);
    //dyn.AddArray(childVals);    // <-------
    for rec in childVals do       // <-------
      dyn.Add(rec);               // <-------
  end;
end;

Offline

#3 2013-07-02 09:33:35

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,659
Website

Re: Memory corruption bug in TDynArray.AddArray ?

Should be fixed by http://synopse.info/fossil/info/1e0ead2c47

I've also added the corresponding regression tests.

Thanks for the report.

Offline

#4 2013-07-04 01:51:40

Dangph
Member
From: Australia
Registered: 2013-07-01
Posts: 5

Re: Memory corruption bug in TDynArray.AddArray ?

Thanks, but that doesn't seem to have fixed the problem.

I'm thinking that the Delphi 2007 compiler has bugs when it comes to returning dynamic arrays from recursive functions.

I saw something like this happen:

function SomeFunc: TSomeArray;
begin
  // ...   <-------------
  result := SomeFunc;
end;

(That's plain Delphi code, nothing to do with TDynArray.)

At the indicated line, result retained its value from the previous iteration. It wasn't initialized. I think that a reference-counted local variable should be automatically initialized by the compiler. The fact that it wasn't suggests that the compiler has problems dealing with that case.

Offline

#5 2013-07-04 04:32:38

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,659
Website

Re: Memory corruption bug in TDynArray.AddArray ?

It is the same for strings.

You have to initialize the result variable AFAIK, or use an explicit local variable.

See http://docwiki.embarcadero.com/RADStudi … am_Control:

For a string, dynamic array, method pointer, or variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result.

As such:

procedure SomeFunc(var result: TSomeArray);
begin
  // ...   <-------------
  SomeFunc(result);
end;

So in your case, you have to take care that no temporary parameter is passed, but a shared instance.

Offline

Board footer

Powered by FluxBB