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 之前覆盖它来电者。
编译器应该执行以下操作之一:
- 使用不同的寄存器暂存结果值,使其使用
EBX
不破坏结果值,或
- 在调用
Grob
或 时未使用 EBX
- 将结果值存储在比寄存器更持久的东西中,比如在堆栈中。
显然选项 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;
我向您展示了 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 之前覆盖它来电者。
编译器应该执行以下操作之一:
- 使用不同的寄存器暂存结果值,使其使用
EBX
不破坏结果值,或 - 在调用
Grob
或 时未使用 - 将结果值存储在比寄存器更持久的东西中,比如在堆栈中。
EBX
显然选项 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;