#1 2014-02-06 16:48:19

Arioch
Member
Registered: 2011-11-17
Posts: 28

XE2: different elements of the same array :-/

...or how i learned not to worry about type safety and hate the code generator.

PS: also featuring: different elements' types in static and dynamic arrays of the same element type

-----------

Working with legacy code i met a TFrame that used to intercept events of a dataset in another DataModule but did not nilled them on its destruction.

Wanted to make it safer and DRY i set to only change the whole list of event handlers in one procedure, and just call it twice.

...i thought that "procedure of object" is one of procedure types. Yet DCC XE2 has a varying opinions on the topic.

------------

type
  _PEventNtf = ^TDataSetNotifyEvent; // iniially was an internal type inside the TFrame1, made it global - no difference
  _PEventErr = ^TDataSetErrorEvent;

  TFrame1 = class(TFrameBase)

....

    type _EventsNtf = array [1 .. 10] of TDataSetNotifyEvent;
    var  dmDBXSaveNtf: _EventsNtf;
    type _EventsErr = array [1 .. 3] of TDataSetErrorEvent;
    var  dmDBXSaveErr: _EventsErr;

    // потом эти массивы одной функцией обмениваются с соотв. датасетами
    // так что нельзя забыть восстановить то, что перехватывали
...

{$T+, D+} // $O- and $O+ generate the same code here

procedure TFrame1.Loaded;
 var i_n, i_e: integer;
 function Cell: _PEventNtf;
 begin
   Inc(i_n);
   Result := @dmDBXSaveNtf[i_n];  // compiles and looks innocent...
 end;
 function Err: _PEventErr;
 begin
   Inc(i_e);
   {$T-} // forcing to compile the next line: incorrect pascal sources generates the correct binary code
   Result := @@dmDBXSaveErr[i_e];
 end;
begin
  inherited Loaded;

  i_n := 0; i_e := 0;

  Cell^ := quSQLQuery1AfterOpen;
  Cell^ := quSQLQuery1AfterClose;

  etc

And the 1st Cell^ := ... line suddenly fallen with attempt at writing @000004   :-[...]

It turned out, the function really did returned nil

CurrencyFrames.pas.648: begin  // nested fn Cell
0AACE9F4 55               push ebp
0AACE9F5 8BEC             mov ebp,esp
0AACE9F7 51               push ecx

CurrencyFrames.pas.649: Inc(i_n);
0AACE9F8 8B4508           mov eax,[ebp+$08]
0AACE9FB FF40FC           inc dword ptr [eax-$04]

CurrencyFrames.pas.650: Result := @dmDBXSaveNtf[i_n];
0AACE9FE 8B4508           mov eax,[ebp+$08]
0AACEA01 8B40FC           mov eax,[eax-$04]
0AACEA04 8B5508           mov edx,[ebp+$08]
0AACEA07 8B52F8           mov edx,[edx-$08]
0AACEA0A 8B84C208030000   mov eax,[edx+eax*8+$0308] // reading the value inside a cell instead of taking address
0AACEA11 8945FC           mov [ebp-$04],eax

CurrencyFrames.pas.651: end;
0AACEA14 8B45FC           mov eax,[ebp-$04]
0AACEA17 59               pop ecx
0AACEA18 5D               pop ebp
0AACEA19 C3               ret 
0AACEA1A 8BC0             mov eax,eax // gap filler

CurrencyFrames.pas.653: begin  // nested fn Err
0AACEA1C 55               push ebp
0AACEA1D 8BEC             mov ebp,esp
0AACEA1F 51               push ecx

CurrencyFrames.pas.654: Inc(i_e);
0AACEA20 8B4508           mov eax,[ebp+$08]
0AACEA23 FF40F4           inc dword ptr [eax-$0c]

CurrencyFrames.pas.656: Result := @@dmDBXSaveErr[i_e];  // line forced by {$T-}
0AACEA26 8B4508           mov eax,[ebp+$08]
0AACEA29 8B40F4           mov eax,[eax-$0c]
0AACEA2C 8B5508           mov edx,[ebp+$08]
0AACEA2F 8B52F8           mov edx,[edx-$08]

0AACEA32 8D84C258030000   lea eax,[edx+eax*8+$0358] // correctly taking the address of the array cell itself!
0AACEA39 8945FC           mov [ebp-$04],eax

CurrencyFrames.pas.657: end;
0AACEA3C 8B45FC           mov eax,[ebp-$04]
0AACEA3F 59               pop ecx
0AACEA40 5D               pop ebp
0AACEA41 C3               ret

Last edited by Arioch (2014-02-06 17:17:45)

Offline

#2 2014-02-06 16:51:03

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

Trying to use Pointer Math instead

 var  p_n: _PEventNtf;  p_e: _PEventErr;
 function Cell: _PEventNtf;
 begin
   Result := p_n; // here we moved compiler scizoidness into the main fun body
   Inc( p_n );
 end;
 function Err: _PEventErr;
 begin
   Result := p_e;
   Inc( p_e );
 end;
begin
  inherited Loaded;

 {$T+}
  p_n := @dmDBXSaveNtf; // [DCC Error]  E2010 Incompatible types: '_PEventNtf' and 'Pointer'
  p_n := @@dmDBXSaveNtf; // [DCC Error] E2036 Variable required
  p_e := @dmDBXSaveErr[1];
  p_e := @@dmDBXSaveErr[1]; // [DCC Error] E2010 Incompatible types: '_PEventErr' and 'Pointer'

{$T-}
  p_n := @dmDBXSaveNtf;
  p_n := @@dmDBXSaveNtf; // [DCC Error] E2036 Variable required
  p_e := @dmDBXSaveErr[1];
  p_e := @@dmDBXSaveErr[1];

  Cell^ := ...

So... is procedure of object variable even a procedure ???

Offline

#3 2014-02-06 16:53:18

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

Outcome of pointers math

{$T-}
  p_e := @dmDBXSaveErr;
  p_e := @@dmDBXSaveErr[1];

  p_e := @dmDBXSaveErr[1];
{$T+}
  p_e := @dmDBXSaveErr[1];



CurrencyFrames.pas.678: p_e := @dmDBXSaveErr;  {$T-}
092FEA3D 8B45F4           mov eax,[ebp-$0c]
092FEA40 0560030000       add eax,$00000360  // Correct result, but type-unsafe
092FEA45 8945F8           mov [ebp-$08],eax   

CurrencyFrames.pas.679: p_e := @@dmDBXSaveErr[1]; {$T-}
092FEA48 8B45F4           mov eax,[ebp-$0c]
092FEA4B 0560030000       add eax,$00000360  // Correct result, but type-unsafe
092FEA50 8945F8           mov [ebp-$08],eax    

CurrencyFrames.pas.681: p_e := @dmDBXSaveErr[1];  {$T-}
092FEA53 8B45F4           mov eax,[ebp-$0c]
092FEA56 8B8060030000     mov eax,[eax+$00000360]   // WRONG!!! Value instead of address! but "type-safe"
092FEA5C 8945F8           mov [ebp-$08],eax

CurrencyFrames.pas.683: p_e := @dmDBXSaveErr[1];  {$T+}
092FEA5F 8B45F4           mov eax,[ebp-$0c]
092FEA62 8B8060030000     mov eax,[eax+$00000360]    // WRONG!!! Value instead of address! but "type-safe"
092FEA68 8945F8           mov [ebp-$08],eax

Then i tried to address Watches window - it failed to calculate values like @dmDbxSaveErr[0] and
@@dmDbxSaveErr[0]   

I made one of the arrays dynamic - just to see if it counts - and suddenly Wathes started to work with both dynamic and static arrays

Offline

#4 2014-02-06 16:57:35

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

Oh, yes... about dynamic arrays... wpould they be any different from static ones ?

  type _EventsErr = array { [1 .. 3] } of TDataSetErrorEvent; // dynamic - one more indirection level

  SetLength(dmDBXSaveErr, 3);

{$T+}
  p_e := @dmDBXSaveErr;  // [DCC Error] E2010 Incompatible types: '_PEventErr' and 'Pointer'


{$T-} p_e := @dmDBXSaveErr;

{$T-} p_e := @dmDBXSaveErr[0];

{$T+} p_e := @dmDBXSaveErr[0];

{$T-} p_e := @@dmDBXSaveErr[0];

{$T+} p_e := @@dmDBXSaveErr[0];
CurrencyFrames.pas.679: {$T-} p_e := @dmDBXSaveErr;
094FEA7A 8B45F4           mov eax,[ebp-$0c]
094FEA7D 0560030000       add eax,$00000360
094FEA82 8945F8           mov [ebp-$08],eax
// Here we obtain the pointer to the DynArray controlling structure
// It is correct but is not what we want
// Just want to notice that DCC - both CodeGen and TypeChecker - worked coherently here

CurrencyFrames.pas.681: {$T-} p_e := @dmDBXSaveErr[0];
094FEA85 8B45F4           mov eax,[ebp-$0c]
094FEA88 8B8060030000     mov eax,[eax+$00000360]  // here we obtain the cell address we crave for...
094FEA8E 8B00             mov eax,[eax]                      // and screw it, replacing with the VALUE of the cell instead
094FEA90 8945F8           mov [ebp-$08],eax  

CurrencyFrames.pas.683: {$T+} p_e := @dmDBXSaveErr[0]; // same as above
094FEA93 8B45F4           mov eax,[ebp-$0c]
094FEA96 8B8060030000     mov eax,[eax+$00000360] 
094FEA9C 8B00             mov eax,[eax]  
094FEA9E 8945F8           mov [ebp-$08],eax 

CurrencyFrames.pas.685: {$T-} p_e := @@dmDBXSaveErr[0];
094FEAA1 8B45F4           mov eax,[ebp-$0c]
094FEAA4 8B8060030000     mov eax,[eax+$00000360]  // With dynamic array we have a correct working code...
094FEAAA 8945F8           mov [ebp-$08],eax

CurrencyFrames.pas.687: {$T+} p_e := @@dmDBXSaveErr[0];
094FEAAD 8B45F4           mov eax,[ebp-$0c]
094FEAB0 8B8060030000     mov eax,[eax+$00000360]   // ...that even can be compiled without killing type safety!!!
094FEAB6 8945F8           mov [ebp-$08],eax

Offline

#5 2014-02-06 17:05:00

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

But how can it be that dynamic arrays would work correctly in Delphi ? I think we missed some details. What if we would just change the index?

SetLength(dmDBXSaveErr, 3);

{$T-} p_e := @dmDBXSaveErr;

{$T-} p_e := @dmDBXSaveErr[1];

{$T+} p_e := @dmDBXSaveErr[1];

{$T-} p_e := @@dmDBXSaveErr[1];

//remember? this compiles fine... or should i say "this at least compiles" ?
{$T+} p_e := @@dmDBXSaveErr[0];

// SUDDENLY! 
{$T+} p_e := @@dmDBXSaveErr[1]; // [DCC Error]  E2010 Incompatible types: '_PEventErr' and 'Pointer'

Type compatibility is different for 0th and 1st elements

CurrencyFrames.pas.681: {$T-} p_e := @dmDBXSaveErr[1]; 
0AB5EA85 8B45F4           mov eax,[ebp-$0c]
0AB5EA88 8B8060030000     mov eax,[eax+$00000360]      // so we're fetching the address of the 0th element....
0AB5EA8E 8B4008           mov eax,[eax+$08]                   // and screw it overwriting it by 1st element value
0AB5EA91 8945F8           mov [ebp-$08],eax

CurrencyFrames.pas.683: {$T+} p_e := @dmDBXSaveErr[1];  // same as above
0AB5EA94 8B45F4           mov eax,[ebp-$0c]
0AB5EA97 8B8060030000     mov eax,[eax+$00000360]
0AB5EA9D 8B4008           mov eax,[eax+$08]
0AB5EAA0 8945F8           mov [ebp-$08],eax

CurrencyFrames.pas.685: {$T-} p_e := @@dmDBXSaveErr[1];
0AB5EAA3 8B45F4           mov eax,[ebp-$0c]
0AB5EAA6 8B8060030000     mov eax,[eax+$00000360]    // so we're fetching the address of the 0th element....
0AB5EAAC 83C008           add eax,$08                          // and just correctly shift it to the next element!
0AB5EAAF 8945F8           mov [ebp-$08],eax                 // but as usual, to get a correct valuewe had to kill type safety :-/

Offline

#6 2014-02-07 08:52:39

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

Re: XE2: different elements of the same array :-/

Weird...

$T+- is something I never changed...

We force in Synopse.inc $T-:

{$T-} // Typed @ operator

Online

#7 2014-02-07 09:03:27

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

I think that avoiding type safety is not a thing for Pascal. So in my project i do the opposite and enforce T+  everywhere, like other safety net measures. And where i need to violate it, i do it by explicit typecasts or absolute vars.

But probably Delphi developers do not set T+ either, otherwise i wopuld not say how could they let that craze pass below radars

Offline

#8 2014-02-07 15:38:43

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

Re: XE2: different elements of the same array :-/

Most of our code uses explicit pointer types, with strong typing (e.g. for string/text process).

We try to do everything as explicit as possible, and rely on FastMM4 full debugg mode to ensure that we do not have any unexpected buffer overflows.

Online

#9 2014-02-07 16:34:09

Arioch
Member
Registered: 2011-11-17
Posts: 28

Re: XE2: different elements of the same array :-/

> code uses explicit pointer types,

It means nothing if they are not checked

> with strong typing

and you said you disabled typing check (as $T- }

Cannot link those two claims together

Last edited by Arioch (2014-02-07 16:34:27)

Offline

#10 2014-02-07 20:20:22

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

Re: XE2: different elements of the same array :-/

I almost never use @

Online

Board footer

Powered by FluxBB