- DelphiTools - https://www.delphitools.info -

TdwsRttiConnector: read anything, write anything

…or to be more accurate, many things the Delphi RTTI can reach, DWScript can reach.

Raw RTTI connectivity for DWS

The new TdwsRttiConnector components exposes a new RttiVariant type to DWScript, which can be used to dynamically read and write fields/properties, or call methods of any RTTI-exposed Delphi type. You can use it to update or “bind” component properties, with the full power of DWS to draw upon in case of need. For instance:

For instance:

var f := ConnectForm('Form1');
f.Label1.Caption := 'Hello ' + f.Edit1.Text;

with ConnectForm() being a function that allows connecting to any TForm registered in the main TApplication, its implementation looks like

var
   c : TComponent;
begin
   c:=Application.FindComponent(Info.ParamAsString[0]);
   if not (c is TForm) then
      Info.ResultAsVariant:=Null
   else Info.ResultAsVariant:=TdwsRTTIVariant.FromObject(c);
end;

So essentially, to expose any instance as an RttiVariant, you just have to pass the result  TdwsRTTIVariant.FromObject() to a script, be it via as function result, a TdwsUnit-based instance or variable, directly via IInfo, etc. choose your poison.

This offers a very lightweight approach to exposing instances to script, the drawbacks being of course that everything is resolved at runtime (no compile-time checks), and that everything goes through RTTI, which limits performance vs “heavier” forms of class and instance exposure in DWScript, and of course, breaks sandboxing.

Cooked RTTI Connectivity

To get some compile-time checks and leverage strong typing, you can however use the new DWS Connector qualifiers, which look like generics, f.i. if you were to go for a strict version of the above sample:

var f := RttiVariant<Forms.TForm> := ConnectForm('Form1');
var lbl := RttiVariant<StdCtrls.TLabel> := f.Label1;
var edt := RttiVariant<StdCtrls.TEdit> := f.Edit1;
lbl.Caption := 'Hello ' + edt.Text;

In that updated version, the script will be type-checked at compile-time, the only dynamic checks remaining being the binding one (to connect ‘Form1’ by name), but you could choose to remove it by exposing the instance directly to the script. All the rest is type-checked, and if for instance a user mistyped and extra ‘s’:

lbl.Captions := 'Hello ' + edt.Text;

then he would get a compile error about TLabel not having a Captions property. In the unqualified version, that would be a runtime error upon executing the offending line.

This being scripts, it still means you’ll have to type-check at runtime, but you can compile all the scripts present in the application to check for errors, the actual forms and components do not have to be up and running!