…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!
The compile-time check is a very nice feature.
The official Delphi compiler should have such “qualifiers”…
DWS rocks!
Hi,
Is it possible to expose the public functions in DWS ? In the ShouldExpose function I allowed the public items to pass through but at the time of script compilation I’m getting “Name ‘Create’ already exists” exception. If we have have the ability to use public class functions in the script, we can use DWS to create Rails/ASP.NET MVC like web framework.
Thanks,
Guru Kathiresan
This is cool – I used it in DSharp to make it possible to create powerful expressions similar to (and imho even better than) LiveBindings.
Exposing a class to DWS anyway is still very broken. Redefined properties are not handled correctly (Property ‘XYZ’ already exists) and also exposing methods is not handled correctly (I think it cannot handle overloads?). So just changing the ShouldExpose would not do it because it actually does not only check the methods in the class itself but also those of the ancestors. Better way would be to also make it possible to auto expose the ancestor classes also. But that might end up in creating a huge dependency because parameter types also would have to be exposed. So worst case would be you end up with half the RTL and VCL in your script 😉
@Guru Kathiresan
‘public’ in itself shouldn’t pose problems, so it may be something else, can you open a ticket with more details at http://code.google.com/p/dwscript/issues/list?
@Stefan Glienke
I encourage you to post the cases on the issue tracker, I’m using it in very limited and well-controlled cases (on classes designed for it, that don’t break sandboxing), so less controlled cases aren’t well tested.
Overloads aren’t supported yet, that’ll be for 2.3, alongside generics and a few other things.
And yes, the daisy chaining is an issue, VCL/RTL are quite tightly coupled, and some classes pretty much bring everything with them, that’s also why the typed connectors were introduced: they give access to everything RTTI, but only pull and check what is actually used.
Another issue is that of memory management, when a method returns an object, there is no way to know how/if the returned object should or shouldn’t be freed.
@Eric
Yes, I will do. Although I went the other way using that RttiVariant in my DSharp integration for DWS.