Search code examples
delphidwscript

DWScript: Getting from IScriptObj to IInfo or TProgramInfo


Given a IScriptObj reference how does one get to a corresponding IInfo or TProgramInfo?


I have a script object that wraps a Delphi object.

In order to manage the life time of the script object the Delphi object stores a reference to the script object. The Script object is declared with a TdwsUnit component. It's pretty standard and goes something like this:

Delphi

type
  TDelphiObject = class
  private
    FScriptObject: IScriptObj;
  public
    procedure DoSomething;
    property ScriptObject: IScriptObj read FScriptObject write FScriptObject;
  end;

Script

type
  TScriptObject = class
  protected
    procedure DoSomething; virtual;
  public
    constructor Create;
  end;

The instantiation of the Delphi object and setup of the Delphi/script links happens in the Delphi implementation of the script object constructor. Also pretty standard:

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;

Ideally I would have saved the IInfo reference rather that the IScriptObj since IInfo does everything I need later on, but from experience it seems the IInfo object is only valid for the duration of the method call.

Anyway, the problem occurs later on when TDelphiObject.DoSomething is called on the Delphi side. TDelphiObject.DoSomething is meant to call the corresponding virtual method on the script object:

Delphi

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;

I have tried a lot of different techniques to get a usable IInfo or TProgramInfo from the stored IScriptObj but every thing has failed. So what is the correct way of doing this?


Solution

  • The problem turned out to be that I assumed I needed an IInfo interface to encapsulate the object instance but apparently DWScript doesn't work that way. What I need is to create a temporary reference/pointer to the instance and then create an IInfo on that instead.

    Here's how that is done:

    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;
    

    What this does is enable object oriented call backs from Delphi to the script. Without this it is only possible to call global script functions from Delphi.

    FWIW, the following two lines from the above:

    ProgramInfo.Execution.DataContext_Create(Data, 0, DataContext);
    Info := TInfoClassObj.Create(ProgramInfo, FScriptObject.ClassSym, DataContext);
    

    can be replaced with a call to CreateInfoOnSymbol (declared in dwsInfo):

    CreateInfoOnSymbol(Info, ProgramInfo, FScriptObject.ClassSym, Data, 0);