У меня есть некоторые неожиданные нарушения доступа для кода Delphi, которые, по моему мнению, верны, но, кажется, неправильно скомпилированы. Я могу свести его кНеверный код при объединении анонимных и вложенных процедур
procedure Run(Proc: TProc);
begin
Proc;
end;
procedure Test;
begin
Run(
procedure
var
S: PChar;
procedure Nested;
begin
Run(
procedure
begin
end);
S := 'Hello, world!';
end;
begin
Run(
procedure
begin
S := 'Hello';
end);
Nested;
ShowMessage(S);
end);
end;
Что происходит для меня является то, что S := 'Hello, world!'
хранит в неправильном месте. Из-за этого возникает либо нарушение прав доступа, либо ShowMessage(S)
показывает «Привет» (а иногда возникает нарушение прав доступа при освобождении объектов, используемых для реализации анонимных процедур).
Я использую Delphi XE, все обновления установлены.
Как я могу узнать, где это вызовет проблемы? Я знаю, как переписать мой код, чтобы избежать анонимных процедур, но мне трудно понять, в каких ситуациях они приводят к неправильному коду, поэтому я не знаю, где их избежать.
Мне было бы интересно узнать, исправлено ли это в более поздних версиях Delphi, но не более чем интересно, обновление на данный момент не является вариантом.
В QC, в последнем отчете я могу найти похожие #91876, но это разрешено в Delphi XE.
Update:
На основе замечаний AlexSC, в с небольшим изменением:
...
procedure Nested;
begin
Run(
procedure
begin
S := S;
end);
S := 'Hello, world!';
end;
...
работает.
Сформированный машинный код для
S := 'Hello, world!';
в программе неудовлетворительную является
ScratchForm.pas.44: S := 'Hello, world!';
004BD971 B89CD94B00 mov eax,$004bd99c
004BD976 894524 mov [ebp+$24],eax
тогда правильная версия
ScratchForm.pas.45: S := 'Hello, world!';
004BD981 B8B0D94B00 mov eax,$004bd9b0
004BD986 8B5508 mov edx,[ebp+$08]
004BD989 8B52FC mov edx,[edx-$04]
004BD98C 89420C mov [edx+$0c],eax
сгенерированный код в неисправной программе не видит что S
перенесен в класс, созданный компилятором, [ebp+$24]
- как к внешним локальным переменным вложенных методов доступно доступ к, к которым обращаются локальные переменные.
В моих тестах я получил это предупреждение «[Предупреждение DCC] Unit1.pas (45): W1036« $ frame »переменной« возможно, не был инициализирован ». Поскольку я не объявлял какую-либо переменную $ frame, я предполагаю, что она была сгенерирована компилятором при объявлении интерфейсов, реализующих анонимные методы. Предупреждение подсказывает, что не все было сделано правильно компилятором, так что это кажется ошибкой. Изменение кода, чтобы объявление переменной S в качестве строки заставляло проблему проявлять себя раньше. Отладка предполагает, что переменная S не была должным образом обработана сгенерированным кодом. – AlexSC
@AlexSC «Возможно, не было инициализировано» обнаружение, как известно, плохо, существует множество ложных срабатываний, которые не указывают на какую-либо реальную проблему и не влияют на сгенерированный код, поэтому это предупреждение, которое должно быть безопасным для игнорирования. Я также могу получить это предупреждение (включая переменную, генерируемую компилятором '$ frame') в более простом коде, который работает корректно. – hvd
Скомпилирует и работает нормально в XE2 –