DWScript: Getting from IScriptObj to IInfo or TProgramInfo
Asked Answered
S

1

7

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?

Sedgewick answered 16/12, 2014 at 14:59 Comment(5)
I don't have much expirience with DWScript but are you sure you are calling your script method the right way? Based on a quick look at DWS Wiki (code.google.com/p/dwscript/wiki/FirstSteps#Functions) the corect call would be "Info.Method['DoSomething'].Call([]);"Duwalt
The two expressions are equivalent; The Method[] property returns an IInfo interface reference.Sedgewick
Can you show the declaration of TProgramInfo?Ist
Also whats not working? Do you get an error? or just nothing happens?Ist
TProgramInfo is a DWScript class. This isn't a generic Delphi question. It is specific to the use of DWScript. The problem is that DWScript, apart from the most basic use, is almost completely undocumented. The source is very poorly commented so I've basically had to reverse engineer it to understand what's going on and obviously I've failed at that. I know how to get from A to B but what I need is a way to get from B to A (the "what happens here" comment in the source example above).Sedgewick
S
1

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);
Sedgewick answered 21/12, 2014 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.