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?
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);