MR Moqui and QuickJS-Oxide

This turned out in sort of an interesting way, so I figured I’d post about it for anyone starting to look into MR Moqui.

One of the biggest architectural changes, in a way more than moving from Java to Rust, is moving from Groovy and FreeMarker to JavaScript. In MR Moqui pretty much everything that isn’t written in Rust runs in the JavaScript engine, calling into framework Rust code. This includes XML Actions (instead of Groovy), XML Screen rendering (instead of FreeMarker), and expressions in XML Screens (instead of Groovy). In MR Moqui this also includes the built-in test harness which runs internally for service-level tests and such, and serverless pointing to a separate instance to run client tests.

The new JavaScript stuff is kind of fun. The templates are called Fancy templates, and they are a lot like FreeMarker, but the engine running them is a combination of Rust and JavaScript. The syntax is very similar, with JavaScript expressions and such. It is called “Fancy” because I told the machine to make it like a ES6 Template Literal but fancy, and then went on to describe the features in FreeMarker that we care about and asking it to mine our templates for what we actually use. When it came time to choose an extension I wasn’t paying attention and it chose ‘fancy’. When I noticed, probably a good day later, I figured it wasn’t the worst name in the world so I left it. Fancy templates are basically FreeMarker reimagined for JavaScript.

Similarly, the test execution engine borrows many ideas from Spock, and our Spock tests in Moqui Framework, Mantle USL, etc were the initial requirements and what was actually ported. So, the ‘moqui test’ and ‘moqui test-client’ testing harnesses and server-side and client-side variants of Spock in JavaScript with a whole bunch of features for browser control and all sorts of stuff so that we don’t have any Node JS dependencies… it all runs in our own little internal JavaScript engine.

So, that little engine is pretty important. I started out with rquickjs, which is a Rust wrapper around QuickJS which is the classic highly-optimized JIT-less engine written in C. This did pretty well, but the memory model was a mess, tons of overhead and we couldn’t share compiled scripts.

Long story short, I moved mr-moqui to use a fork of QuickJS-Oxide which is a pure Rust port of QuickJS with few dependencies. While there is room for optimization in this engine, and I’ve already done some (or had Claude Opus do it), the main improvements it needed were binding interfaces… and this was a problem with rquickjs and QuickJS-C, but initially QuickJS-Oxide didn’t really have exotic object embedding support so that needed to be added anyway (to expose the Moqui API to JavaScript scripts).

So, those are the bulk of the changes in the engine: there are a lot of particular requirements for running expressions and scripts in a framework engine like Moqui. There are services that call services, and while that is in the same thread unless async it is in a separate context and needs it’s own script environment. So, those need to be cheap and nestable. We also need the ability to globally cache compiled scripts, and that is a big deal for something like Moqui with typically hundreds of frequently run scripts active in the system. The compiler is impressively fast, but still, it’s a heck of a lot of work vs running the script so we need a good shared cache for memory optimization (reuse) and performance (reuse). This also required engine changes to add memory management hooks and such.

The end result is, in a way, sort of a neat new thing in the world. For our purposes it is generally faster than QuickJS written in C for the JavaScript itself, and bindings and memory management are far far more efficient (ie parity on larger scripts, much much faster for expressions and smaller scripts, some like 1/5th-1/10th the execution time).

What this enables in Rust applications is sort of cool, and for MR Moqui it also gives us a code base that is reasonably sized in addition to the framework that we can optimize the heck out of for our particular needs. With the desktop and mobile builds of MR Moqui, it is turning into something sort of like the Electron framework (with much fancier database stuff, and weird ways of writing screens).

Anyway, as far the build goes in mr-moqui: you still only need to clone the mr-moqui repo, no need to manually clone the moqui/quickjs-oxide repo unless you want a local copy (then put it next to the mr-moqui directory and it should pick it up locally in the build). Cargo will automatically download the specific commit ID from git that is currently pinned for the framework, so no additional steps are needed with this fork.

2 Likes