Delphi 5 编译器错误 returning 接口指针而不是 return 值

Delphi 5 compiler bug returning interface pointer rather than return value

我向您展示了 Delphi 5 编译器中的一个错误。我知道不会有任何解决办法;但解决方法是 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.

真正的胆量是 DoStuff 函数,应该 return 一个:

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

    Result := 2;
end;

函数应该return一个。相反,它 return 是接口对象的地址:

大会

代码确实开始将结果设置为一个:

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

并且由于函数即将 return,它确实将 EBX 复制到 EAX 以便 return 它:

    mov eax,ebx           ;EBX into EAX for return

但最终块(调用接口方法)是问题所在。它吹走了存储在 EBX 中的 return 值:

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

"call" 到 finally 块之后,它 return 跳转到:

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

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

我知道你们 none 有 Delphi 5。即使你有,

"What would you like me to say?"

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

如您所见,编译器将结果存储到 EBX,但随后在将 EBX 复制到 EAX 到 return 之前覆盖它来电者。

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

  1. 使用不同的寄存器暂存结果值,使其使用EBX不破坏结果值,或
  2. 在调用 Grob
  3. 时未使用 EBX
  4. 将结果值存储在比寄存器更持久的东西中,比如在堆栈中。

显然选项 1 和 2 对您来说不是现成的,但后者是您需要在此示例中实施的解决方法——使用局部变量来保存您想要的 Result 值,直到您准备好至 return 它:

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;