DWScript: проблема обновления до текущей версии разработки

В эти выходные я обновил свою кодовую базу из DWScript SVN. Я использовал Preview 2.7, и теперь я использую последнюю версию транка.

Я перекомпилирую свое приложение, и теперь OnAfterInitUnitTable больше не запускается. На самом деле TdwsUnit.InitUnitTable вообще не вызывается. Кстати: TDWSunit создается во время выполнения с помощью кода, а затем с помощью ExposeRTTI выставляются два класса. При необходимости выставить один экземпляр каждого класса.

Каковы предварительные условия для срабатывания OnAfterInitUnitTable?

Любая помощь приветствуется.

РЕДАКТИРОВАТЬ: Пример кода для воспроизведения:

program ExposeTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
    SysUtils, Classes, TypInfo,
    dwsRTTIExposer, dwsExprs, dwsComp;

type
    TScriptApplication = class(TPersistent)

    end;

    TTestClass = class(TThread)
    private
        FScript                  : IdwsProgram;
        FDelphiWebScript         : TDelphiWebScript;
        FUnit                    : TdwsUnit;
        FScriptApplication       : TScriptApplication;
        FSuccess                 : Boolean;
        procedure ExposeInstancesAfterInitTable(Sender: TObject);
    public
        constructor Create;
        destructor Destroy; override;
        procedure Execute; override;
    end;

var
    Test : TTestClass;


{ TTestClass }

constructor TTestClass.Create;
begin
    inherited Create(TRUE);
    FScriptApplication              := TScriptApplication.Create;
    FDelphiWebScript                := TDelphiWebScript.Create(nil);
    FUnit                           := TdwsUnit.Create(nil);
    FUnit.UnitName                  := 'Test';
    FUnit.Script                    := FDelphiWebScript;
    FUnit.ExposeRTTI(TypeInfo(TScriptApplication), [eoNoFreeOnCleanup]);
    FUnit.OnAfterInitUnitTable      := ExposeInstancesAfterInitTable;
end;

destructor TTestClass.Destroy;
begin
    FreeAndNil(FScriptApplication);
    FreeAndNil(FUnit);
    FreeAndNil(FDelphiWebScript);
    inherited;
end;

procedure TTestClass.Execute;
begin
    WriteLn('Test 1');
    FSuccess     := FALSE;
    FScript      := FDelphiWebScript.Compile('Unit Test; var I: Integer; I := 0;');
    if FSuccess then
        WriteLn('   Success')
    else
        WriteLn('   Failure');
    WriteLn('Test 2');
    FSuccess     := FALSE;
    FScript      := FDelphiWebScript.Compile('var I: Integer; I := 0;');
    if FSuccess then
        WriteLn('   Success')
    else
        WriteLn('   Failure');
    WriteLn('Test Done');
end;

procedure TTestClass.ExposeInstancesAfterInitTable(Sender: TObject);
begin
    FUnit.ExposeInstanceToUnit('Application', 'TScriptApplication', FScriptApplication);
    WriteLn('OnAfterInitUnitTable called');
    FSuccess     := TRUE;
end;

begin
    Test := TTestClass.Create;
    Test.Start;
    Sleep(1000);
    WriteLn('Hit enter to quit');
    ReadLn;
    Test.Free;
end.

EDIt2: Другая версия, чтобы показать новую проблему, используя предложение Эрика Грейнджа в ответе 1 ниже;

program ExposeTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
    SysUtils, Classes, TypInfo,
    dwsRTTIExposer, dwsFunctions, dwsExprs, dwsComp;

type
    TScriptApplication = class(TPersistent)
    published
        procedure Demo;
    end;

    TTestClass = class(TThread)
    private
        FScript                  : IdwsProgram;
        FDelphiWebScript         : TDelphiWebScript;
        FUnit                    : TdwsUnit;
        FScriptApplication       : TScriptApplication;
        FSuccess                 : Boolean;
        procedure ExposeInstancesAfterInitTable(Sender: TObject);
        function NeedUnitHandler(const UnitName   : UnicodeString;
                                 var   UnitSource : UnicodeString): IdwsUnit;
    public
        constructor Create;
        destructor Destroy; override;
        procedure Execute; override;
    end;

var
    Test : TTestClass;


{ TTestClass }

constructor TTestClass.Create;
begin
    inherited Create(TRUE);
    FScriptApplication              := TScriptApplication.Create;
    FDelphiWebScript                := TDelphiWebScript.Create(nil);
    FDelphiWebScript.OnNeedUnit     := NeedUnitHandler;
    FUnit                           := TdwsUnit.Create(nil);
    FUnit.UnitName                  := 'Test';
    FUnit.Script                    := FDelphiWebScript;
    FUnit.ExposeRTTI(TypeInfo(TScriptApplication), [eoNoFreeOnCleanup]);
    FUnit.OnAfterInitUnitTable      := ExposeInstancesAfterInitTable;
end;

destructor TTestClass.Destroy;
begin
    FreeAndNil(FScriptApplication);
    FreeAndNil(FUnit);
    FreeAndNil(FDelphiWebScript);
    inherited;
end;

procedure TTestClass.Execute;
begin
    WriteLn('Test 1');
    FSuccess     := FALSE;
    FScript      := FDelphiWebScript.Compile('Unit Test; var I: Integer; I := 0;');
    WriteLn(FScript.Msgs.AsInfo);
    if FSuccess then
        WriteLn('   Success')
    else
        WriteLn('   Failure');
    WriteLn('Test 2');
    FSuccess     := FALSE;
    FScript      := FDelphiWebScript.Compile('uses Other;');
    WriteLn(FScript.Msgs.AsInfo);
    if FSuccess then
        WriteLn('   Success')
    else
        WriteLn('   Failure');
    WriteLn('Test Done');
end;

procedure TTestClass.ExposeInstancesAfterInitTable(Sender: TObject);
begin
    FUnit.ExposeInstanceToUnit('Application', 'TScriptApplication', FScriptApplication);
    WriteLn('OnAfterInitUnitTable called');
    FSuccess     := TRUE;
end;

function TTestClass.NeedUnitHandler(
    const UnitName   : UnicodeString;
    var   UnitSource : UnicodeString): IdwsUnit;
begin
    Result := nil;
    if SameText(UnitName, 'Other') then
    UnitSource := 'unit Other;' + #13#10 +
                  'procedure Func;' + #13#10 +
                  'begin' + #13#10 +
                  '  Application.Demo;' + #13#10 +
                  'end;' + #13#10
    else
        UnitSource := '';
end;

{ TScriptApplication }

procedure TScriptApplication.Demo;
begin

end;

begin
    Test := TTestClass.Create;
    Test.Start;
    Sleep(1000);
    WriteLn('Hit enter to quit');
    ReadLn;
    Test.Free;
end.

person fpiette    schedule 27.05.2013    source источник
comment
Он по-прежнему используется для тестовых модулей, которые можно найти в UdwsUnitTests и URTTIExposeTests. Не могли бы вы получить более подробную информацию о воспроизведении проблемы?   -  person Eric Grange    schedule 28.05.2013
comment
Пытаясь написать простой пример, я обнаружил, что это происходит, когда вы компилируете скрипт, содержащий «единицу» в первой строке. Например, компиляция модуля Test; переменная I: целое; начало I := 0; конец; делает событие OnAfterInitTable не вызываемым. Удалить модульный тест; с линии и вдруг вызывается событие.   -  person fpiette    schedule 28.05.2013
comment
Хммм... тест ExplicitUses в UdwsUnitTests делает что-то очень близкое к этому, и вызывается его OnAfterInitUnitTable. У вас установлено StaticSymbols в true? Если это так, событие будет вызвано только один раз. Если False, он будет вызываться при каждой компиляции.   -  person Eric Grange    schedule 28.05.2013
comment
Изменение значения StaticSymbols ничего не меняет.   -  person fpiette    schedule 29.05.2013
comment
Отредактировал вопрос, чтобы добавить пример кода для воспроизведения проблемы.   -  person fpiette    schedule 29.05.2013
comment
Спасибо, я неправильно понял вашу проблему. Публикация ответа.   -  person Eric Grange    schedule 31.05.2013


Ответы (1)


При обнаружении "модуля" в качестве основной программы компилятор в настоящее время предполагает, что это просто компиляция для целей IDE, т.е. для проверки синтаксических ошибок, создания карты символов, предоставления предложений и т. д., и, как следствие, полученная программа не будет полностью инициализирована.

Итак, если вы хотите скомпилировать модуль и создать исполняемую программу, у вас может быть основная программа, которая будет выглядеть примерно так:

uses Test;

Это скомпилирует программу, состоящую из вашего модуля, для которого могут быть созданы исполнения и где функции могут быть вызваны через exec.Info, классы могут быть созданы и т. д.

Edit2: для второго тестового примера это работает, если "использует Test"; добавлен. Для полной кросс-компиляции с Delphi вам также потребуются разделы интерфейса/реализации (при нацеливании только на скрипт они не нужны)

unit Other;

interface

uses Test;

procedure Func;

implementation

procedure Func;
begin
  Application.Demo;
end;

и если RTTI генерируется для методов с директивой $RTTI, по крайней мере, с

{$RTTI EXPLICIT METHODS([vcPublished])}
TScriptApplication = class(TPersistent)
published
    procedure Demo;
end;

в противном случае вы получите ошибку о том, что «Демо» не найдено.

person Eric Grange    schedule 31.05.2013
comment
Не уверен, что понимаю. Вы имеете в виду, что я должен скомпилировать скрипт, в котором одна строка использует Test; который запросит фактический скрипт в модульном тесте. Модульный тест может быть полноценным исходным файлом, начинающимся с модульного теста; и имея полный сценарий? В DWScript Preview 2.7 такого не было, и это раздражает. Должен ли я использовать программу ключевого слова вместо единицы? - person fpiette; 31.05.2013
comment
Если ваш модуль на самом деле является программой, и вы не собираетесь использовать его где-либо еще, вы можете просто пропустить оператор модуля. Тогда я не знал, что единицы можно использовать как программы :( - person Eric Grange; 31.05.2013
comment
Этот способ работает не намного лучше. Вызывается ExposeInstancesAfterInitTable, но открытый объект неизвестен компилятору. Я отредактировал исходный вопрос, добавив второй пример кода, чтобы показать эту новую проблему, которая вызывает ошибку компиляции, говорящую о том, что имя открытого объекта неизвестно (в демо-версии имя объекта — Приложение). - person fpiette; 03.06.2013
comment
Забыл сказать, что конечной целью является создание скрипта со 100% совместимым синтаксисом Delphi, чтобы его можно было скомпилировать в Delphi и скомпоновать в приложении. - person fpiette; 03.06.2013
comment
Обновленный ответ с компилируемой Delphi версией другого модуля - person Eric Grange; 03.06.2013
comment
Мой код компилировался с помощью Delphi XE4. Может быть, вы пробовали старый? Кстати, что ты изменил? Вы воспроизвели проблему? - person fpiette; 04.06.2013
comment
Я имею в виду код другого модуля (в TTestClass.NeedUnitHandler), у которого не было интерфейса/реализации, может ли XE4 его скомпилировать? Или я неправильно понял вашу потребность в компиляции этого модуля в DWS и Delphi? Остальной код Delphi я не менял. Я компилирую в XE. - person Eric Grange; 05.06.2013
comment
Я сделал очень небольшой пример кода, чтобы показать ошибку DWScript, а не для того, чтобы показать, как достичь моей конечной цели, которая здесь не имеет значения. Вы не ответили на вопрос: воспроизводите ли вы проблему? - person fpiette; 06.06.2013
comment
Нет, я не воспроизводю это, как я разместил в Edit2 своего ответа, как только я исправлю ваш блок Other, добавив use Test и добавив явную директиву $RTTI в программу Delphi, это сработало. - person Eric Grange; 07.06.2013
comment
Явная директива $RTTI не требуется, если предком является TPersitent или он наследуется от TPersistent. - person fpiette; 11.06.2013