I’ve been working with Eric Grange on adding a new feature to the DWS compiler recently: external routines. The goal of this feature is to allow DWS code to call into native routines like Delphi code can call into routines in a DLL by writing a function signature and marking it external, without having to use a TdwsUnit component and create a bunch of heavy-overhead binding code.
This is accomplished by using a simple JIT that takes a DWS function object as its input, and outputs a little stub of machine code that does the same thing as the binding routine you would have to write otherwise.
The eventual goal is to be able to call out to DLLs as well as internal native routines with this. That will require supporting multiple calling conventions, and since handling the function call is the whole point of the JIT, that will probably mean creating multiple JITs. But at the moment, I’m still working on the first one, to support the register calling convention.
It’s nowhere near “production-ready” yet, but I’ve got some progress checked in. So far, it’s able to create stubs for procedures with up to 3 parameters of type Integer or String, with no return value. You create a header in your DWS script and mark it with the external; directive (no name, just the keyword) and then, after compiling the script but before running it, call the new RegisterExternalFunction method on the compiler to link the external; routine with the procedure it should execute.
My basic methodology has gone like this:
- Open a Console project in Delphi
- Create a procedure that looks like a DWS external eval handler
- Create a procedure with the right signature for the eval handler to call into
- Write the code for the eval handler
- Build, put a breakpoint in the eval handler, and run
- Examine the generated ASM code in the CPU view
- Figure out what it’s doing
- Set up my JIT to do the same thing
The only really tricky part so far has been setting up the implicit try/finally block to clean up strings, mostly because there’s a lot of “magic” going on in there and I don’t know what it all does. I’m sure
Barry Kelly the current compiler guy for the Delphi team could explain it in detail, but for the moment I’m just content with doing the same thing the Delphi compiler is doing and making sure it works the same way.
Next on the list will be the other two basic types in DWS, boolean and float. After that, I’m not sure. Probably return values, and sets because those are implemented under the hood as integers.
Anyway, like I said this is still a work in progress, but the progress I have so far is checked in, so have a look if anyone’s interested. As always, feedback is welcome.