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

标签 delphi delphi-5 compiler-bug

我向您展示了 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,它应该返回一个:

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

    Result := 2;
end;

该函数应返回一个。相反,它返回接口(interface)对象的地址:

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

但finally block (调用接口(interface)方法)是问题所在。它会清除 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

在“调用”finally block 之后,它返回到跳转,将其发送到:

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

返回值不是一或二,而是接口(interface)指针的地址。

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

"What would you like me to say?"

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

最佳答案

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

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

  1. 使用不同的寄存器临时存储结果值,以便其使用 EBX 不会破坏结果值,或者
  2. 在调用 Grob 时未使用 EBX,或者
  3. 将结果值存储在比寄存器更持久的地方,例如堆栈。

显然,选项 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;

关于Delphi 5 编译器错误返回接口(interface)指针而不是返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32529922/

相关文章:

delphi - 绕过(禁用)Delphi 的接口(interface)引用计数

android - XE8 Twebbrowser 本地 img 文件

delphi - 通过Delphi在单词中创建Y的页码X

delphi - 如何获取正在运行的可执行文件的版本?

excel - VBA:是什么导致传递给 ParamArray 的字符串参数更改为数字(看起来可疑地像指针)?

delphi - 阅读当前项目Delphi 2010的数据版本

delphi - 在 Delphi 中调整 png 图像大小 - 不正确的 alpha channel

sql-server-2005 - Delphi - BOF 或 EOF 为真,或者当前记录已被删除

c++ - Consexpr if 具有非 bool 条件

java - 令人惊讶的 "inferred type does not conform to upper bound"错误