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

时间:2015-09-11 18:27:13

标签: delphi delphi-5 compiler-bug

我在Delphi 5编译器中向您展示了一个错误。我知道不会有任何解决方法;但解决方法是超级

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.

真正的胆量是 DoStuff 的功能,应该返回一个:

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

    Result := 2;
end;

该功能应返回一个。相反,它返回接口对象的地址:

enter image description here

程序集

代码实际上开始将结果设置为1:

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

并且当函数即将返回时,它会将EBX复制到EAX中以便返回它:

    mov eax,ebx           ;EBX into EAX for return

但是最后阻塞(调用interfaced方法)就是问题所在。它会消除存储在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

“call”到finally块之后,它返回跳转,将其发送到:

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

返回值而不是一个或两个,是接口指针的地址。

我知道你们都没有Delphi 5.即使你这样做了,

  

“你想让我说什么?”

我知道困难。我真正需要的是某种解决方法。

1 个答案:

答案 0 :(得分:3)

正如您所观察到的,编译器将结果存储到EBX中,然后在将EBX复制到EAX之前覆盖它,以便将结果返回给调用者。

编译器应该执行以下操作之一:

  1. 使用不同的寄存器临时存储结果值,以便使用EBX不会破坏结果值,或
  2. EBX
  3. 的调用中未使用Grob
  4. 将结果值存储在比寄存器更持久的内容中,就像在堆栈上一样。
  5. 显然,选项1和2并不是您可以使用的,但后者是您需要在此示例中实现的解决方法 - 使用局部变量保存您想要的Result值,直到您准备好返回它:

    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;