Autor Téma: Portace FreeAndNil do starších verzí Delphi  (Přečteno 403 krát)

Online Jirka

  • Hrdina
  • ****
  • Příspěvků: 333
  • Karma: 9
    • Verze Delphi: XE2
Portace FreeAndNil do starších verzí Delphi
« kdy: 09-10-2020, 17:00:28 »
V souvislosti jiným příspěvkem mě napadla taková kacířská myšlenka
Zda by šlo "přepsat"  procedura FreeAndNil ve starších verzích  Delphi (u mě XE2)
a bezpečně nahradit volání původní procedury touto novou .

Vyzkoušel jsem definovat proceduru jako
Kód: Delphi [Vybrat]
  1. procedure FreeAndNilSydney(const [ref] Obj: TObject);
  2. var
  3.   Temp: TObject;
  4. begin
  5.   Temp := Obj;
  6.   TObject(Pointer(@Obj)^) := nil;
  7.   Temp.Free;
  8. end;

a volání této nové procedury probíhá bez viditelných problémů

1. Lze nějakým bezpečným způsobem přepsat původní FreeAndNil() proceduru  v dané aplikaci ?
2. Pokud nelze postupovat podle bodu 1 , alespoň nahradit volání FreeAndNil() na FreeAndNilSydney() ?


Offline Morrison

  • Hrdina
  • ****
  • Příspěvků: 298
  • Karma: 12
    • Verze Delphi: D5, XE2, 10.4
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #1 kdy: 09-10-2020, 18:36:08 »
Nějak nerozumím tomu, v čem je problém s bodem č.2
Nahradit snad je záležitost nějakého Ctrl R (Vyhledat a nahradit). Nebo snad myslíš nahradit to i v komponentách atd.?
Jinak co se bodu č.1 týče, možná ti tu někteří zdejší zkušenější borci poradí, jak to zahákovat, přepsat odkaz v tabulce metod nebo nějaký podobný low-level voodoo, kterému já nerozumím :)
« Poslední změna: 09-10-2020, 18:38:19 od Morrison »
nil

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1524
  • Karma: 37
    • Pepak.net
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #2 kdy: 09-10-2020, 20:23:45 »
1. Lze nějakým bezpečným způsobem přepsat původní FreeAndNil() proceduru  v dané aplikaci ?
Co si představuješ pod pojmem "bezpečným" v tomto kontextu? Je pro tebe přepsání prvních pěti bajtů funkce (adresu zjistíš přes @FreeAndNil) instrukcí skoku a čtyřmi bajty adresy (zjistíš přes @FreeAndNilSydney, nutno přpočítat na relativní adresu) dostatečně bezpečné, zvlášť když to zaIFDEFuješ jenom na ty verze Delphi, ve kterých sis ověřil, že FreeAndNil má aspoň 5 bajtů?

Offline pepak

  • Padawan
  • ******
  • Příspěvků: 1524
  • Karma: 37
    • Pepak.net
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #3 kdy: 10-10-2020, 03:16:49 »
Akorát mě tak napadá, a co přesně si od toho "nahrazení" slibuješ? Moje odpověď byla na to, jak to udělat, ale nebude to plnit účel - pokud tedy správně chápu, že bys chtěl, aby se FreeAndNil dalo zavolat jenom na objekty.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #4 kdy: 10-10-2020, 17:48:02 »
V souvislosti jiným příspěvkem mě napadla taková kacířská myšlenka
Zda by šlo "přepsat"  procedura FreeAndNil ve starších verzích  Delphi (u mě XE2)
a bezpečně nahradit volání původní procedury touto novou .
No clovece, zkusil jsem tvoji myslenku v Delphi 10.2 a nestacil, jsem se divit, co to prekladac vytvari  :o :o :o

Treba u generickeho TList<T> vypada definice tridy takto:
Kód: Delphi [Vybrat]
  1.   private type
  2.     arrayofT = array of T;
  3.   var
  4.     FListHelper: TListHelper; // FListHelper must always be followed by FItems
  5.     FItems: arrayofT; // FItems must always be preceded by FListHelper
  6.   ...
  7.  
coz je tezky hack, protoze na FreeAndNilSydney(list[n]) prekladac jako const ref parametr preda adresu elementu TList.FItems[n], cili se hrabe primo v pameti zapouzdrenehio objektu, private/ne-private members   :o A tim to funguje.

Ale kdyz tomu napr. u TStrings predhodim FreeAndNilSydney(list.Objects[n]), tak tam bez uzardeni pise nil buhvi kam (skutecne se mi nechce pocitat ty adresyt v prelozenem kodu) a do sl.Objects[n] zadne nil samozrejme nezapise, protoze hodnota muze byt ve skutecnosti treba v utrobach Windows...

Jak se chova presne 10.4 nevim, protoze ho nemam a zrejme nikdy mit nebudu, ale docela na me pada uzkost pri pomysleni, ze si udelam nejaky
TMyObject a jeho property nekdo preda jako const ref parametr a prekladac misto aby to vykopal tam bude psat buhvi kam... A nekdo pak bude tezce lovit nejake prepisy pameti nebo AV...

Takze kdyz mluvis o bezpecnosti ve spojeni se starsimi verzemi, tak bych osobne byl rad, ze ti to prekladac neprelozi a ohlasi chybu, ze trva na var argumentu.

Offline František

  • Guru
  • *****
  • Příspěvků: 596
  • Karma: 6
    • Verze Delphi: primárne v XE5, občas 10.2.3 comunity
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #5 kdy: 11-10-2020, 14:52:33 »
myslim ze cele toto vzniklo novinkou https://delphi.cz/post/Delphi-104-ocekavane-novinky-RTL-aktualizace.aspx

Kód: Delphi [Vybrat]
  1. procedure FreeAndNil(const [ref] Obj: TObject); inline;

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2711
  • Karma: 104
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #6 kdy: 11-10-2020, 16:01:25 »
myslim ze cele toto vzniklo novinkou https://delphi.cz/post/Delphi-104-ocekavane-novinky-RTL-aktualizace.aspx

Kód: Delphi [Vybrat]
  1. procedure FreeAndNil(const [ref] Obj: TObject); inline;


No hlavne se pak upravila i RTL, kdyz na to kompilator upozornil. Takže, jednoduše nové FreeAndNil se starou RTL použít nelze.
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #7 kdy: 11-10-2020, 16:34:44 »
No hlavne se pak upravila i RTL, kdyz na to kompilator upozornil. Takže, jednoduše nové FreeAndNil se starou RTL použít nelze.
Takze v nove verzi uz nelze napsat FreeAndNil(TList<T>[ x ]) ani FreeAndNil(TStrings.Objects[ x ]) aj.  a prekladac oznami chybu?

Nebo to propusti a u TList<T> to bude fungovat spravne diky silenemu hacku a u TStrings to nebude fungovat a navic to bude psat buhvi kam do pameti?

Nebo upravili RTL a prekladac tak, ze to bude fungovat vzdy a vsude, kdyz to prekladac propusti (tj. nil priradi setterem)?

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2711
  • Karma: 104
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #8 kdy: 11-10-2020, 21:24:35 »
Já myslím, že došlo k nepochopení o co šlo: tato změna má interně stejnou funkci jak generický var obj z předchozí signatury, až na to, že předešlý případ umožňoval předání čehokoliv (var bez typu umožní předat úplně vše), během vývoje tak bylo ukázáno různými třetími stranami i R&D, že tato změna díky kompiláru zachytila špatné volání FreeAndNil s parametrem typu interface (nejčasteji), array, THandle atd. Takže chápu tazatele.

Nyní lze předat jen následníka TObject. Ohledně zápisových property se to chová stejně, tj. není to ideální, ale i tak je to IMHO krok vpřed. Pro zápisové property mi to přijde a co si matně pamatuji, že se vytvoří temp pointer na stacku a ten se nuluje, takže by to mělo být bezpečné, ale setter se nezavolá. Prý i jeden z důvodů pro méně komplexní řešení bylo zachování rozhraní v rámci 10.4.x řady. Jinak FreeAndNil je inline, níže varianta bez inline, s ní jsem byl už úplně ztracen.

Kód: Delphi [Vybrat]
  1. 0060E509 8BEC             mov ebp,esp
  2. 0060E50B 83C4F4           add esp,-$0c
  3. 0060E50E 8955F8           mov [ebp-$08],edx
  4. 0060E511 8945FC           mov [ebp-$04],eax
  5. Unit29.pas.39: FreeAndNil(sl.Objects[0]);
  6. 0060E514 8B45FC           mov eax,[ebp-$04]
  7. 0060E517 8B80E4030000     mov eax,[eax+$000003e4]
  8. 0060E51D 33D2             xor edx,edx
  9. 0060E51F 8B08             mov ecx,[eax]
  10. 0060E521 FF5118           call dword ptr [ecx+$18]
  11. 0060E524 8945F4           mov [ebp-$0c],eax
  12. 0060E527 8D45F4           lea eax,[ebp-$0c]
  13. 0060E52A E859EBE1FF       call FreeAndNil
  14. Unit29.pas.40: if sl.Objects[0] = nil then
  15. 0060E52F 8B45FC           mov eax,[ebp-$04]
  16. 0060E532 8B80E4030000     mov eax,[eax+$000003e4]
  17. 0060E538 33D2             xor edx,edx
  18. 0060E53A 8B08             mov ecx,[eax]
  19. 0060E53C FF5118           call dword ptr [ecx+$18]
  20. 0060E53F 85C0             test eax,eax
  21. 0060E541 750A             jnz $0060e54d
  22. Unit29.pas.41: ShowMessage('nil');
  23. 0060E543 B860E56000       mov eax,$0060e560
  24.  




No hlavne se pak upravila i RTL, kdyz na to kompilator upozornil. Takže, jednoduše nové FreeAndNil se starou RTL použít nelze.
Takze v nove verzi uz nelze napsat FreeAndNil(TList<T>[ x ]) ani FreeAndNil(TStrings.Objects[ x ]) aj.  a prekladac oznami chybu?

Nebo to propusti a u TList<T> to bude fungovat spravne diky silenemu hacku a u TStrings to nebude fungovat a navic to bude psat buhvi kam do pameti?

Nebo upravili RTL a prekladac tak, ze to bude fungovat vzdy a vsude, kdyz to prekladac propusti (tj. nil priradi setterem)?

Embarcadero MVP - Czech republic

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2711
  • Karma: 104
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #9 kdy: 11-10-2020, 21:27:04 »
Takze v nove verzi uz nelze napsat FreeAndNil(TList<T>[ x ]) ani FreeAndNil(TStrings.Objects[ x ]) aj.  a prekladac oznami chybu?

Druhý případ právě že jo, protože je TObject, první asi podle T.
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #10 kdy: 11-10-2020, 23:26:19 »
Já myslím, že došlo k nepochopení o co šlo: tato změna má interně stejnou funkci jak generický var obj z předchozí signatury, až na to, že předešlý případ umožňoval předání čehokoliv (var bez typu umožní předat úplně vše), během vývoje tak bylo ukázáno různými třetími stranami i R&D, že tato změna díky kompiláru zachytila špatné volání FreeAndNil s parametrem typu interface (nejčasteji), array, THandle atd. Takže chápu tazatele.
Tohle ja chapu: puvodni implementace byla hrubka, protoze v signature byl untyped pointer (var obj) a uvnitr tela natvrdo pretypovani na TObject s temi dusledky, co popisujes. Co nechapu, proc nepouzili primocare reseni, ktere se nabizi tj. zmenit signaturu na var TObject. To by zachovalo puvodni kontrolu var + zajistilo silnou typovou kontrolu parametru.

Me jde o neco jineho: kdyz jsi predal do var argumentu property, tak prekladac zcela spravne rval, ze neumi predat const parametr do var argumentu. Ale  kdyz pouzili tu divnou referenci na TObject, tak jsem v D10.2 zjistil, ze tam muzu predavat property, prekladac nerve, ale funkcionalita je v kyblu (a je nejspis nebezpecna, protoze to pise nil buhvi kam). A jako vrchol vseho hnusu jsem narazil u generickeho TList<T> kod, ktery to property predat umoznuje (ne do puvodni signatury var obj):

Kód: ASM [Vybrat]
  1. 0072A007 8B45F8           mov eax,[ebp-$08]     ;instance TList<T>
  2. 0072A00A 8B4020           mov eax,[eax+$20]     ;adresa private memberu TList<T>.FItems (to je ta poznamka, ze musi byt jako 1. za recordem s helperem)
  3. 0072A00D 83C00C           add eax,$0c           ; + offset elementu [3]*sizeof(TObject)
  4. 0072A010 E8BFEAFFFF       call FreeAndNilSydney
  5.  

A ohledne toho Objects[] jsem to zkousel s TListBoxem, protoze tam vim, ze je hodnota Objects v utrobach Windows jako ITEMDATA a jinak nez setterem potazmo volanim LB_SETITEMDATA se vynulovat neda...

Tak me zajimalo, jak to vyresili v 10.4, kde by jak pises melo jit finalni verzi, ktera ma tohle dokoncene. Ale ten priklad s generickym TList<T> uz ve verzi 10.2 naznacuje, ze leccos zacali prasit jeste prted zmenou te FreeAndNil.

Az budu mit cas, tak napisu test, at se bavime o kontkretnich vecech.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #11 kdy: 11-10-2020, 23:28:12 »
Druhý případ právě že jo, protože je TObject
Ale to je IMHO prave spatne

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2711
  • Karma: 104
    • Verze Delphi: D2007, DXE + 2 poslední
    • O Delphi v češtině
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #12 kdy: 12-10-2020, 08:55:40 »
Druhý případ právě že jo, protože je TObject
Ale to je IMHO prave spatne

A to je IMHO právě dobře.
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 3004
  • Karma: 135
    • Verze Delphi: D2007, XE3, DX10
Re:Portace FreeAndNil do starších verzí Delphi
« Odpověď #13 kdy: 12-10-2020, 22:44:39 »
A to je IMHO právě dobře.
Az budu mit cas, tak zkusim napsat priklady, proc si myslim, ze je to vic nez spatne, protoze si myslim, ze je to s property obecne pres const ref neresitelne.