It introduces a new layer to the compiler in order to write transformations of Vala code into other Vala code, such that it's easier for the codegen layer to generate C code, and it's easier for us developers to write and maintain such transformations.
The current Vala pipeline is as follows:
You already got it. There are a couple of flaws in this pipeline:
- The semantic analyzer not only checks the code, but also does transformations for simplifying the code that will reach the codegen.
- The flow analyzer does not analyze the original code, rather the transformed code. This leads to loss of information, thus bugs that are fixable only by making the flow analyzer more intelligent or with hacks (see this bug for example)
- At the codegen level, we do complex transformations which are hard to maintain.
The wip/transform branch introduces a pluggable visitor for transforming Vala code into other Vala code, and it sits in between the flow analyzer and the codegen:
So this is what we gain and lose with the new approach:
- Probably this change will hit the compiler performance, but I'm planning to reduce the number of temporary variables used, thus save time at the C compilation stage. Though we always head for correctness before optimization.
- The semantic analyzer and flow analyzer no longer have the issues explained above.
- The C codegen has been extremely simplified, because now the ugly transformations are made simpler in the transformer.
- The flow analyzer needs some more code in order to handle cases that were first simplified, like conditional expressions.
Also the transformer has several cool helpers that let us write transformed Vala code using strings, like a meta language, and that's very convenient.
This new architecture naturally opens for a new feature: Vala plugins. It is possible to specify directories of plugins with --plugindir . A plugin is a file starting with "valaplugin" that is dynamically loadable (a .so, .dll, or whatelse).
There is no system directory for plugins, that's intentional:
- The feature will be experimental.
- The API of libvala is unstable, therefore having system wide plugins would lead to even more breakage for users.
The feature is still useful on the application level. You can create your own plugins to automatically generate code that otherwise requires much boilerplate. Think for example of some rpc protocol, serialization or orm.
Now let's have some fun with a sample plugin for registering unit tests with the glib test framework.
This is how your code would look like now:
We want it to be like this:
So let's write our beloved glib test plugin for Vala:
What we do here is:
- Hook on every method definition
- Check if they have a [Test (name = ...)] attribute
- Then find the return statement in the main() method (context.entry_point) and cache it
- Create a dummy statement placed right before the return statement
- Replace that dummy statement with Test.add_func(...).
Compile the plugin with:
$valasrc/compiler/valac -C $valasrc/vapi/libvala-0.24.vapi valaplugintest.vala gcc -o valaplugintest.so -shared `pkg-config glib-2.0 gobject-2.0 --cflags --libs` -fPIC -I../vala -I../gee valaplugintest.c
Finally compile newtest.vala with:
$valadoc/compiler/valac --plugindir . test.vala
The current wip/transform branch is usable and very promising. Many complex projects already build and run flawlessly.
* While in the picture I put the gtkmodule as transformer, this is not true yet. The gtkmodule is still in the codegen, but won't take much to port it as a transformer.
Hope the next wip/transform branch will be about a call for testing before the merge :-)