我向您展示了 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)对象的地址:
装配
代码实际上确实开始将结果设置为 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
中以返回结果给调用者。
编译器应该执行以下操作之一:
- 使用不同的寄存器临时存储结果值,以便其使用
EBX
不会破坏结果值,或者 - 在调用
Grob
时未使用EBX
,或者 - 将结果值存储在比寄存器更持久的地方,例如堆栈。
显然,选项 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/