Is the compiler treatment of implicit interface variables documented?

耗尽温柔 提交于 2019-11-28 19:02:49

If there is any documentation of this behavior, it will probably be in the area of compiler production of temporary variables to hold intermediate results when passing function results as parameters. Consider this code:

procedure UseInterface(foo: IInterface);
begin
end;

procedure Test()
begin
    UseInterface(Create());
end;

The compiler has to create an implicit temp variable to hold the result of Create as it is passed into UseInterface, to make sure that the interface has a lifetime >= the lifetime of the UseInterface call. That implicit temp variable will be disposed at the end of the procedure that owns it, in this case at the end of the Test() procedure.

It's possible that your pointer assignment case may fall into the same bucket as passing intermediate interface values as function parameters, since the compiler can't "see" where the value is going.

I recall there have been a few bugs in this area over the years. Long ago (D3? D4?), the compiler didn't reference count the intermediate value at all. It worked most of the time, but got into trouble in parameter alias situations. Once that was addressed there was a follow up regarding const params, I believe. There was always a desire to move disposal of the intermediate value interface up to as soon as possible after the statement in which it was needed, but I don't think that ever got implemented in the Win32 optimizer because the compiler just wasn't set up for handling disposal at statement or block granularity.

You can not guarantee that compiler will not decide to create a temporal invisible variable.

And even if you do, the turned off optimization (or even stack frames?) may mess up your perfectly checked code.

And even if you manage to review your code under all possible combinations of project options - compiling your code under something like Lazarus or even new Delphi version will bring hell back.

A best bet would be to use "internal variables can not outlive routine" rule. We usually do not know, if compiler would create some internal variables or not, but we do know, that any such variables (if created) would be finalized when routine exists.

Therefore, if you have code like this:

// 1. Some code which may (or may not) create invisible variables
// 2. Some code which requires release of reference-counted data

E.g.:

Lib := LoadLibrary(Lib, 'xyz');
try
  // Create interface
  P := GetProcAddress(Lib, 'xyz');
  I := P;
  // Work with interface
finally
  // Something that requires all interfaces to be released
  FreeLibrary(Lib); // <- May be not OK
end;

Then you should just wrap "Work with interface" block into subroutine:

procedure Work(const Lib: HModule);
begin
  // Create interface
  P := GetProcAddress(Lib, 'xyz');
  I := P;
  // Work with interface
end; // <- Releases hidden variables (if any exist)

Lib := LoadLibrary(Lib, 'xyz');
try
  Work(Lib);
finally
  // Something that requires all interfaces to be released
  FreeLibrary(Lib); // <- OK!
end;

It is a simple, but effective rule.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!