Log On
Embarcadero Home
Watch, Follow, &
Connect with Us
Share This
QualityCentral
Communities
Articles
Blogs
Resources
Downloads
Help
QualityCentral
Delphi-BCB
Compiler
Delphi
Anonymous Methods
BASM
Code Generation/Optimization
Error Recovery
Errors - Warnings
Exceptions
Execution
Finalization
Generics
Header Generation
Interaction with UI
Interfaces
Language
Linker
Make Logic
Memory Manager
OBJ Generation
OBJ Support
Other Compiler
Packages
RTTI
String Resources
TD Debug Info
Thread Local Storage
Version resilience
You are not logged in.
Help
Print
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))^"
View Your Reports
Search
Server Response from: ETNACODE01
Developer Tools
Blackfish SQL
C++Builder
Delphi
FireMonkey
Prism
InterBase
JBuilder
J Optimizer
HTML5 Builder
3rdRail & TurboRuby
Database Tools
Change Manager
DBArtisan
DB Optimizer
ER/Studio
Performance Center
Rapid SQL
Technical Articles
Tutorials
White Papers
Press Releases
Newsletters
Add Content (GetPublished)
Audio
Audio & Video
Video
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Tags
Technology Partners
Downloads
Free Trials
Registered User Downloads
Beta Programs
Add Content (GetPublished)
Articles
Blogs
Bugs & Suggestions (QualityCentral)
Discussion Forums
Examples (CodeCentral)
Member Services
About
Connect with Us