Skip to content

abstract methods are not excluded (since Delphi 2010) #3

Description

@eugeneilyin

Something was changed in .map file format since Delphi 2010 and CodeCoverage32 threat abstract methods as executed non-covered code.

Project settings

Linking / Map file: Detailed

Command Line (32-bit)

CodeCoverage32 -e Project.exe -m Project.map -u SampleUnit -od Coverage -html

Command Line (64-bit)

CodeCoverage64 -e Project.exe -m Project.map -u SampleUnit -od Coverage -html

Minimal code to reproduce

Project.dpr

program Project;

{$APPTYPE CONSOLE}

uses
  SampleUnit in 'SampleUnit.pas';

begin
  with TClassB.Create do
    try
      Method;
    finally
      Free;
    end;
end.

SampleUnit.pas

unit SampleUnit;

interface

type

  TClassA = class
  public
    procedure Method; virtual; abstract;
  end;

  TClassB = class(TClassA)
  public
    procedure Method; override;
  end;

implementation

procedure TClassB.Method;
begin
end;

end.

Coverage Report

Compiler Lines Covered Covered % Report
Delphi 5 2 2 100 % Report
Delphi 6 2 2 100 % Report
Delphi 7 2 2 100 % Report
Delphi 2005 2 2 100 % Report
Delphi 2006 1 1 100 % Report
Delphi 2009 1 1 100 % Report
Delphi 2010 2 1 50 % Report
Delphi XE 3 2 66 % Report
Delphi XE2
32-bit
3 2 66 % Report
Delphi XE2
64-bit
4 3 75 % Report
Delphi XE3
32-bit
3 2 66 % Report
Delphi XE3
64-bit
4 3 75 % Report
Delphi XE4
32-bit
3 2 66 % Report
Delphi XE4
64-bit
4 3 75 % Report
Delphi XE5
32-bit
3 2 66 % Report
Delphi XE5
64-bit
4 3 75 % Report
Delphi XE6
32-bit
3 2 66 % Report
Delphi XE6
64-bit
4 3 75 % Report
Delphi XE7
32-bit
3 2 66 % Report
Delphi XE7
64-bit
4 3 75 % Report
Delphi XE8
32-bit
3 2 66 % Report
Delphi XE8
64-bit
4 3 75 % Report
Delphi 10.0 Seattle
32-bit
3 2 66 % Report
Delphi 10.0 Seattle
64-bit
4 3 75 % Report
Delphi 10.1 Berlin
32-bit
3 2 66 % Report
Delphi 10.1 Berlin
64-bit
4 3 75 % Report
Delphi 10.2 Tokyo
32-bit
3 2 66 % Report
Delphi 10.2 Tokyo
64-bit
4 3 75 % Report
Delphi 10.3 Rio
32-bit
3 2 66 % Report
Delphi 10.3 Rio
64-bit
4 3 75 % Report

Activity

  1. sglienke commented on Sep 15, 2022

    @sglienke

    This is the result of the enhanced RTTI information emitted into the binary and thus the linker keeping this method in.
    There in fact exists code for every abstract method: a jump to System._AbstractError.

    The proof is this code:

    var
      proc: procedure(obj: TObject);
    begin
      proc := @TClassA.Method;
      proc(nil);
    

    If you now place a breakpoint into the line that is being marked blue in your reports you will noticed that it calls that and then jumps to the _AbstractError proc.

    Now because since Delphi 2010 enhanced RTTI is enabled for all public methods the linker does not remove this method body which is not being called otherwise. When doing a virtual call to that method it directly goes to _AbstractError as the compiler has its address put into every VMT slot that is abstract for that class - you can see that with this code:

      with TClassA.Create do
        try
          Method; // put breakpoint here and step into (with debug dcus enabled you will end up in System._AbstractError
        finally
          Free;
        end;
    

    You can solve this be either implicitly disabling RTTI for your class (has to be put into every affected unit - see https://docwiki.embarcadero.com/RADStudio/Alexandria/de/RTTI-Direktive_(Delphi)
    or let the linker not keep methods just for RTTI by using the https://docwiki.embarcadero.com/RADStudio/Alexandria/en/WEAKLINKRTTI_directive_(Delphi)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions