DWScript:从IScriptObj到IInfo或TProgramInfo

Spe*_*eak 7 delphi dwscript

给出一个IScriptObj参考如何获得相应的IInfoTProgramInfo


我有一个包装Delphi对象的脚本对象.

为了管理脚本对象的生命周期,Delphi对象存储对脚本对象的引用.Script对象是使用TdwsUnit组件声明的.这是非常标准的,如下所示:

德尔福

type
  TDelphiObject = class
  private
    FScriptObject: IScriptObj;
  public
    procedure DoSomething;
    property ScriptObject: IScriptObj read FScriptObject write FScriptObject;
  end;
Run Code Online (Sandbox Code Playgroud)

脚本

type
  TScriptObject = class
  protected
    procedure DoSomething; virtual;
  public
    constructor Create;
  end;
Run Code Online (Sandbox Code Playgroud)

Delphi对象的实例化和Delphi /脚本链接的设置发生在脚本对象构造函数的Delphi实现中.也非常标准:

德尔福

// Implements TScriptObject.Create
procedure TMyForm.dwsUnitClassesTScriptObjectConstructorsCreateEval(Info: TProgramInfo; var ExtObject: TObject);
var
  DelphiObject: TDelphiObject;
  DelphiObjectInfo: IInfo;
begin
  // Create the Delphi-side object
  DelphiObject := TDelphiObject.Create;

  // Get the script object "self" value
  DelphiObjectInfo := Info.Vars['self'];

  // Store the ScriptObject reference
  DelphiObject.ScriptObject := DelphiObjectInfo.ScriptObj;

  // Return the instance reference to the script
  ExtObject := DelphiObject;
end;
Run Code Online (Sandbox Code Playgroud)

理想情况下,我会保存IInfo引用,而不是IScriptObj以后IInfo我需要的所有东西,但从经验来看,似乎该IInfo对象仅在方法调用的持续时间内有效.

无论如何,问题发生TDelphiObject.DoSomething在Delphi方面调用之后. TDelphiObject.DoSomething意味着在脚本对象上调用相应的虚方法:

德尔福

procedure TDelphiObject.DoSomething;
var
  Info: IInfo;
  DoSomethingInfo: IInfo;
begin
  // I have a IScriptObj but I need a IInfo...
  Info := { what happens here? };

  // Call the virtual DoSomething method
  DoSomethingInfo := Info.Method['DoSomething'];
  DoSomethingInfo.Call([]);
end;
Run Code Online (Sandbox Code Playgroud)

我已经尝试了很多不同的技术来获得可用IInfoTProgramInfo存储IScriptObj但是每件事都失败了.那么这样做的正确方法是什么?

Spe*_*eak 1

问题是我假设我需要一个IInfo接口来封装对象实例,但显然 DWScript 不能那样工作。我需要的是创建一个指向该实例的临时引用/指针,然后IInfo在其上创建一个。

这是如何完成的:

procedure TDelphiObject.DoSomething;
var
  ProgramExecution: TdwsProgramExecution;
  ProgramInfo: TProgramInfo;
  Data: TData;
  DataContext: IDataContext;
  Info: IInfo;
  DoSomethingInfo: IInfo;
begin
  (*
  ** Create an IInfo that lets me access the object represented by the IScriptObj pointer.
  *)

  // FProgramExecution is the IdwsProgramExecution reference that is returned by
  // TdwsMainProgram.CreateNewExecution and BeginNewExecution. I have stored this
  // elsewhere.
  ProgramExecution := TdwsProgramExecution(FProgramExecution);
  ProgramInfo := ProgramExecution.AcquireProgramInfo(nil);
  try
    // Create a temporary reference object
    SetLength(Data, 1);
    Data[0] := FScriptObject;
    ProgramInfo.Execution.DataContext_Create(Data, 0, DataContext);
    // Wrap the reference
    Info := TInfoClassObj.Create(ProgramInfo, FScriptObject.ClassSym, DataContext);


    // Call the virtual DoSomething method
    DoSomethingInfo := Info.Method['DoSomething'];
    DoSomethingInfo.Call([]);

  finally
    ProgramExecution.ReleaseProgramInfo(ProgramInfo);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

它的作用是启用从 Delphi 到脚本的面向对象回调。如果没有这个,就只能从 Delphi 调用全局脚本函数。

FWIW,上面的以下两行:

ProgramInfo.Execution.DataContext_Create(Data, 0, DataContext);
Info := TInfoClassObj.Create(ProgramInfo, FScriptObject.ClassSym, DataContext);
Run Code Online (Sandbox Code Playgroud)

可以替换为调用CreateInfoOnSymbol(在 dwsInfo 中声明):

CreateInfoOnSymbol(Info, ProgramInfo, FScriptObject.ClassSym, Data, 0);
Run Code Online (Sandbox Code Playgroud)