Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Generics    [ Add a report in this area ]  
Report #:  98942   Status: Closed
Default comparer for methods does not work
Project:  Delphi Build #:  14.0.3593.25826
Version:    14.0 Submitted By:   David Heffernan
Report Type:  Crash / Data loss / Total failure Date Reported:  9/15/2011 7:11:46 AM
Severity:    Commonly encountered problem Last Updated: 9/5/2012 8:09:10 PM
Platform:    All platforms Internal Tracking #:   287684
Resolution: Fixed (Resolution Comments) Resolved in Build: : 17.0.4625.53395
Duplicate of:  None
Voting and Rating
Overall Rating: No Ratings Yet
0.00 out of 5
Total Votes: 21
Description
The default comparer for methods does not work.  This is implemented by the Compare_Method routine in Generics.Defaults.  It is very obviously broken for 64 bit code (apparently the implementation wasn't changed for XE2) and it turns out that it doesn't work for 32 bit code.

What I mean by "does not work" is that it returns 0 for inputs that are not equal.  It probably fails in many other ways too.

Please please don't look at the code for Compare_Method and decide that the bug only affects 64 bit code.  That code doesn't work anywhere.

This bug came to light at this Stack Overflow question:

http://stackoverflow.com/questions/7428244/is-tlistt-indexof-using-fcomparer-compare-correct
Steps to Reproduce:
The following code shows the problem:


program TMethodComparer;

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Defaults, Generics.Collections;

type
  TMyMethod = procedure of object;

type
  TMyClass = class
  published
    procedure P1;
    procedure P2;
    procedure P3;
  end;

procedure TMyClass.P1;
begin
end;

procedure TMyClass.P2;
begin
end;

procedure TMyClass.P3;
begin
end;

var
  List: TList<TMyMethod>;
  MyObject1, MyObject2: TMyClass;

begin
  MyObject1 := TMyClass.Create;
  MyObject2 := TMyClass.Create;
  List := TList<TMyMethod>.Create;
  List.Add(MyObject1.P1);
  List.Add(MyObject1.P2);
  List.Add(MyObject2.P1);
  List.Add(MyObject2.P2);
  Writeln(List.IndexOf(MyObject1.P1));
  Writeln(List.IndexOf(MyObject1.P2));
  Writeln(List.IndexOf(MyObject2.P1));
  Writeln(List.IndexOf(MyObject2.P2));
  Writeln(List.IndexOf(MyObject1.P3));
end.



Expected output:

0
1
2
3
-1

Actual output:

0
0
0
0
0
Workarounds
Implement your own comparer that performs the comparison properly.
Attachment
None
Comments

Gabriel Corneanu at 5/14/2012 6:53:13 AM -
@Left is the code address (same as TMethod(Left).Code), and NOT( as probably the someone wanted) the address of the TMethod record.
Therefore the construct returns the first 8 bytes of "code content"!
The bug arrise when the generated code is very similar. I found that usually 7 of 8 bytes is the stake frame, and the 8th byte is a "8B", the beginning of a 32bit move.

The correct code would be "int64(TMethod(Left))" or "pint64(@TMethod(Left))^"

Server Response from: ETNACODE01