Delphi 5 compiler bug returning interface pointer rather than return value

I present you a bug in the Delphi 5 compiler. I know there's not going to be any fix for it; but a workaround would be super

program Project1;

uses
  Dialogs, SysUtils;

{$R *.RES}

type
    IFoo = interface
        ['{D68DA49A-F870-433D-9343-4964BFECFF27}']
        procedure Grob(a: Integer; b: Integer);
    end;

    TFoo = class(TInterfacedObject, IFoo)
    public
        procedure Grob(a: Integer; b: Integer); virtual;
    end;

procedure TFoo.Grob(a: Integer; b: Integer);
begin

end;

function DoStuff(): Integer;
var
    foo: IFoo;
begin
    foo := TFoo.Create;
    try
        Result := 1;
        Exit;
    finally
        foo.Grob(0, 0);
    end;

    Result := 2;
end;

var
    n: Integer;
begin
    n := DoStuff;
    if n <> 0 then
        ShowMessage('Failed: '+IntToStr(n))
    else
        ShowMessage('Passed: '+IntToStr(n));

end.

The real guts is the function DoStuff which should return one:

function DoStuff(): Integer;
var
    foo: IFoo;
begin
    foo := TFoo.Create;
    try
        Result := 1;
        Exit;
    finally
        foo.Grob(0, 0);
    end;

    Result := 2;
end;

The function should return one . Instead it returns the address of the interfaced object:

在这里输入图像描述

The assembly

The code actually does start to set the result to one:

Project1.dpr.30: Result := 1;
    mov ebx,$00000001     ; place return value 1 in EBX
Project1.dpr.31: Exit;
    call @TryFinallyExit  ; call the finally block
    jmp DoStuff + $6E

and as the function is about to return, it does copy EBX into EAX in order to return it:

    mov eax,ebx           ;EBX into EAX for return

But finally block (calling the interfaced method) is the problem. It blows away the return value stored in EBX:

We arrive here from the call @TryFinallyExit
Project1.dpr.33: foo.Grob(0, 0);
    xor ecx,ecx
    xor edx,edx
    mov eax,[ebp-$04]
    mov ebx,[eax]  <----- overwriting ebx with interface address
    call dword ptr [ebx+$0c]
    ret

After the "call" to the finally block, it returns to a jump, which sends it to:

Project1.dpr.36: Result := 2;
...
    xor eax,eax
    pop edx
    pop ecx
    pop ecx
    mov fs:[eax],edx
    push $00442e1f
    lea eax,[ebp-$04]
    call @IntfClear
    ret
...
    mov eax,ebx  <----- places overwritten EBX into EAX for return
Project1.dpr.37: end;
    pop ebx
    pop ecx
    pop ebp
    ret

The return value rather than being one, or two, is the address of the interface pointer.

I know none of you have Delphi 5. And even if you did,

"What would you like me to say?"

I know the difficulty. What i actually need is some sort of workaround.


As you observed, the compiler is storing the result into EBX , but then overwriting it before it subsequently copies EBX into EAX to return the result to the caller.

The compiler should be doing one of the following:

  • Using a different register to store the result value temporarily, so that its use of EBX does not destroy the result value, or
  • Not using EBX in the call to Grob , or
  • Storing the result value in something more persistent than a register, like on the stack.
  • Obviously options 1 and 2 are not readily available to you, but the latter is the workaround that you need to implement in this example – use a local variable to hold your intended Result value until you are ready to return it:

    function DoStuff(): Integer;
    var
      foo: IFoo;
      MyResult: Integer;
    begin
      foo := TFoo.Create;
      try
        try
          MyResult := 1;
          Exit;
        finally
          foo.Grob(0, 0);
        end;
    
        MyResult := 2;
      finally
        Result := MyResult;
      end;
    end;
    
    链接地址: http://www.djcxy.com/p/89892.html

    上一篇: > ecx和edx寄存器中的内容歧义

    下一篇: Delphi 5编译器错误返回接口指针而不是返回值