Главная » Статьи » FreePascal

Ошибка в LazDE

Озаботился подготовкой справочной информации по модулям реализации дерева парсинга аналитических выражений. Для подготовки файлов в формате CHM использовал редактор документации LazDE, который идет в установочном пакете Lazarus'а.

В ходе работы обнаружил, что про добавлении ссылок в раздел See Also, они не сохраняются в секции <seealso> XML файла описания. Соответственно, это приводит к тому, эти ссылки отсутствуют и в файле справочной информации.

Изучение исходных текстов LazDE привело к следующему месту в файл $(LazarusDir)\doceditor\freditor.pp:

420    Function  TElementEditor.CurrentXML : String;
...
432    Var
433      I : Integer;
434      S,L,LT : String;
435
436    begin
...
443      S:='';
444      for I:=0 to lbxSeeAlso.Items.Count-1 do
445        begin
446        LT:=Trim(lbxSeeAlso.Items[i]);
447        if (LT<>'') then
448          begin
449          SplitLinkText(LT,L,LT);  //<<<<<<<<<<<<<<<<<<<<<<
450          If (LT<>'') then
451            S:=S+'<link id="'+L+'">'+LT+'</link>'
452          else
453            S:=S+'<link id="'+L+'"/>';
454          end;
455        end;
456      Result:=Result+GetNodeString('seealso',S);

Процедура SplitLinkText, отмеченная в строке с номером 449, расположена в этом же файле и имеет следующее объявление:

Procedure SplitLinkText(LT : String; out lblShortDescr,T : String);

Последние два параметра объявлены с типом out (output parameters), особенности применения которых описываются  в Reference guide for Free Pascal, version 3.0.2 следующим образом:

The purpose of an out parameter is to pass values back to the calling routine: the variable is passed by reference. The initial value of the parameter on function entry is discarded, and should not be used.

Что же происходит в нашем случае? Это становится ясно из ассемблерного файла, который получается при компиляции модуля с опцией -al. Рассмотрим это при компиляции для x86_64-win64.

И так отмеченный ранее вызов SplitLinkText:

//Win64 ==========================================================
.globl    FREDITOR$_$TELEMENTEDITOR_$__$$_CURRENTXML$$ANSISTRING
FREDITOR$_$TELEMENTEDITOR_$__$$_CURRENTXML$$ANSISTRING:
.......
# Var L located at rbp-40, size=OS_64
# Var LT located at rbp-48, size=OS_64

# [449] SplitLinkText(LT,L,LT);
    leaq    -48(%rbp),%rcx         <<<<<< LT (third parameter - address)
    call    fpc_ansistr_decr_ref   <<<<<< декремент счетчика референций
    leaq    -48(%rbp),%rsi         <<<<<< временное сохранение адреса LT
    leaq    -40(%rbp),%rcx         <<<<<< L (second parameter - address)
    call    fpc_ansistr_decr_ref   <<<<<< декремент счетчика референций
    leaq    -40(%rbp),%rdx         <<<<<< сохранение адреса L
    movq    -48(%rbp),%rcx         <<<<<< сохранение адреса LT (first parameter - address)
    movq    %rsi,%r8               <<<<<< перенос адреса LT
    call    FREDITOR_$$_SPLITLINKTEXT$ANSISTRING$ANSISTRING$ANSISTRING
.Ll244:

То есть для всех трех параметров передаются адреса соответствующих переменных. При этом для третьего и первого параметра передается один и тот же адрес переменной LT. Кроме того для двух последних параметров, которые описаны с типом out, выполняется декремент счетчика референций (ссылок), и, если этот счетчик становится равным нулю, то содержимое соответствующей строки теряется!

Посмотрим теперь на пролог процедуры SplitLinkText:

//Win64 ==========================================================
#Procedure SplitLinkText(LT : String; out lblShortDescr,T : String);
.globl    FREDITOR_$$_SPLITLINKTEXT$ANSISTRING$ANSISTRING$ANSISTRING
....
# Var LT located at rbp-8, size=OS_64
# Var lblShortDescr located at rbp-16, size=OS_64
# Var T located at rbp-24, size=OS_64
    movq    %rcx,-8(%rbp)
    movq    %rdx,-16(%rbp)
    movq    %r8,-24(%rbp)
    movq    -8(%rbp),%rcx            <<<<<< адрес параметра LT
    call    fpc_ansistr_incr_ref       <<<<<< инкремент счетчика референций
    movq    -16(%rbp),%rax          <<<<<< адрес параметра lblShortDescr
    movq    $0,(%rax)                   <<<<<< обнуление lblShortDescr
    movq    -24(%rbp),%rax          <<<<<< адрес параметра T
    movq    $0,(%rax)                  <<<<<< обнуление T

Здесь видно, что в прологе данной процедуры выполняется обнуление содержимого строк второго и третьего параметров, имеющих тип out. Но ранее мы видели, что при рассматриваемом нами вызове процедуры для третьего параметра передается тот же адрес что и для первого параметра. Таким образом содержимое исходной строки теряется на этапе пролога процедуры SplitLinkText.

Исправляется данная ошибка просто:

420    Function  TElementEditor.CurrentXML : String;
...
432    Var
433      I : Integer;
434      S,L,LT, T : String;   <<<<<< объявляем  дополнительную переменную T
435
436    begin
...
443      S:='';
444      for I:=0 to lbxSeeAlso.Items.Count-1 do
445        begin
446        LT:=Trim(lbxSeeAlso.Items[i]);
447        if (LT<>'') then
448          begin
449          SplitLinkText(LT,L,T);  <<<<<< изменяем третий параметр
450          If (LT<>'') then
451            S:=S+'<link id="'+L+'">'+T+'</link>'   <<<<<< меняем LT на T
452          else
453            S:=S+'<link id="'+L+'"/>';
454          end;
455        end;
456      Result:=Result+GetNodeString('seealso',S);

Все! Редактор успешно работает!

Категория: FreePascal | Добавил: zoleg5763 (24.11.2018)
Просмотров: 213 | Комментарии: 1 | Теги: редактор документации LazDE, FreePascal | Рейтинг: 0.0/0
Всего комментариев: 1
avatar
0
1 zoleg5763 • 12:07, 05.04.2019
Подготовленный мной патч применен, fixed in Revision r60823
avatar