Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного? / 9 сообщений из 9, страница 1 из 1
29.12.2017, 21:39
    #39578084
nicholaos
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Доброго времени суток.

Есть минимальный пример для воспроизведения, в нем процедура вызывает другую процедуру по переданному адресу, которая меняет var-параметр:

Код
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
interface

type

  TSomeProc = procedure(const NewValue: integer; var Value: integer);

...

implementation

procedure CallProc(Proc: TSomeProc; var Value: integer);
begin
  Proc(1, Value);
end;

procedure ExternalProc(const NewValue: integer; var Value: integer);
begin
  Value := NewValue;
end;

procedure TForm1.Button1Click(Sender: TObject);

  procedure InnerProc(const NewValue: integer; var Value: integer);
  begin
    Value := NewValue;
  end;

var
  Value: integer;
begin
  Value := -1;

  //Вариант 1
  //CallProc(@ExternalProc, Value);
  //Вариант 2
  CallProc(@InnerProc, Value);

  ShowMessage(IntToStr(Value));
end;




При компиляции 32-х битным компилятором (Delphi 2007 и Delphi XE8) без ошибок выполняется как вариант 1, так и вариант 2.
При компиляции 64-х битным компилятором (Delphi XE8) без ошибок выполняется только вариант 1.
Вариант 2 выдает ошибку:

Ошибка---------------------------
Debugger Exception Notification
---------------------------
Project Bug64.exe raised exception class $C0000005 with message 'c0000005 ACCESS_VIOLATION'.
---------------------------
Break Continue Help
---------------------------


Есть предположение, что проблема в том, что InnerProc вложена в метод класса , т.е. всегда имеет неявный аргумент Self.
Однако, тогда не понятно, почему при компиляции в 32 бита ошибки нет.

Возможно, что при компиляции в 32 бита "везет" на расположение аргументов (в регистре или на стеке), а в 64 - "не везет". Пробовал добавлять ко всем процедурам stdcall - тот же результат.

Подскажите, пожалуйста, что происходит на самом деле.

Без использования адреса
Насколько я понимаю, вызов с использованием адреса ("CallProc(@...") приводит к "обману" проверки типов компилятора, а вызов без @ приведет к ошибкам компиляции, т.е. дельфи старается не дать программисту отстрелить себе ногу:
dcc Error[dcc32 Error] uBug64.pas(52): E2094 Local procedure/function 'InnerProc' assigned to procedure variable
и
[dcc64 Error] uBug64.pas(52): E2094 Local procedure/function 'InnerProc' assigned to procedure variable



P.S. Проблема не выдумана, такой прием активно используется в одном стороннем компоненте.
...
Рейтинг: 0 / 0
29.12.2017, 22:15
    #39578096
Кар-Кар
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Всё так, имеет Self - это стек, регистр rbp.
Чтобы можно было SuperValue достать в Button1Click и вообще контекст не терять, как-то так выйдет:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
type
  TSomeProc = procedure(const Stack:UInt64;const NewValue: integer; var Value: integer);

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure CallProc(const Stack: UInt64;Proc: TSomeProc; var Value: integer);
begin
  Proc(Stack, 1, Value);
end;

procedure ExternalProc(const NewValue: integer; var Value: integer);
begin
  Value := NewValue;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
SuperValue : Integer;

  procedure InnerProc(const NewValue: integer; var Value: integer);
  begin
    Value := NewValue + SuperValue;
  end;

  function GetStack:UInt64;
  asm
    //.NOFRAME не работает в локальных оказывается
    mov       rax,rcx
  end;

var
  Value: integer;
begin
  Value := -1;
  SuperValue := 1000;

  //CallProc(@ExternalProc, Value);
  CallProc(GetStack, @InnerProc, Value);

  ShowMessage(IntToStr(Value));
end;
...
Рейтинг: 0 / 0
29.12.2017, 23:12
    #39578107
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
...
Рейтинг: 0 / 0
29.12.2017, 23:13
    #39578108
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Нельзя так делать...
На х32 вам просто везет..
...
Рейтинг: 0 / 0
29.12.2017, 23:14
    #39578109
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Вместо этого используйте замыкания...
...
Рейтинг: 0 / 0
30.12.2017, 00:11
    #39578125
kealon(Ruslan)
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
nicholaos,

волшебное везение 32-х битного

CallProc(@InnerProc, Value);
не используйте этот знак если нет чёткого понимания зачем это нужно
...
Рейтинг: 0 / 0
30.12.2017, 02:28
    #39578157
GunSmoker
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Используя @, ты получаешь нетипизированный указатель, что отключает проверки компилятора. Убери @ - и компилятор сам скажет, что так делать нельзя .
...
Рейтинг: 0 / 0
30.12.2017, 03:36
    #39578161
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
type
  TSomeProc = reference to procedure(const NewValue: Integer; var Value: Integer);

procedure CallProc(Proc: TSomeProc; var Value: integer);
begin
  Proc(1, Value);
end;

procedure TForm1.Test;
var
  Value: Integer;
begin
  Value := -1;
  CallProc (
    procedure(const NewValue: Integer; var Value: Integer)
    begin
      Value := NewValue;
    end,
    Value
  );
end;
...
Рейтинг: 0 / 0
30.12.2017, 15:23
    #39578249
nicholaos
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного?
Использование замыканий, судя по всему, и проще, и правильней.
Поменял объявление типов для процедур-коллбэков на "reference to procedure" - это позволило выловить все ошибки по время компиляции. Исправил все ошибки - все заработало.

Всем спасибо за ответы!
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / Локальные процедуры. Баг 64-х битного компилятора или волшебное везение 32-х битного? / 9 сообщений из 9, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]