Changes, fixes and debugger

These are exciting days and Quartex Pascal is being polished, refactored and optimized on a daily basis. For the past week or more my focus has been on the RTL, while Kjell is busy with the IDE and making our toolchain more platform independent.

I could probably list a wide range of tickets we have closed, but I want to focus on the RTL changes since that is at the heart of things. To sum up the most important changes in the RTL this week:

  1. Align: TAlign now works brilliantly
  2. Bugs with regards to async constructors are gone, the source for the strange inconsistensies was simply that people (myself included) had tried to achieve too much inside callback mechanisms, causing synchronization and timing issues. It is very tempting to adopt some of the flamboyance of JavaScript when working with QTX, but the framework defines rules that are there for a reason.
  3. Moved execution of the constructor callback to AFTER the widget has been injected into the DOM, but before ObjectReady() is invoked. This is actually a massive change, since it radically changes what you can do in the constructor callback. It also affecs nested constructors and when they finish (read: when widgets are available in the DOM and can be accessed).
  4. By getting rid of the issues above, Javascript JIT kicks in and is able to optimize the code far better than before. I was also able to remove bloat and fine-tune things now that ready-state is no longer a factor.
  5. TQTXSyncWidget will be depricated since there is no longer any difference between TQTXWidget and TQTXSyncWidget.
  6. Sourcemaps and debugging!

As you might remember from previous articles and posts, the difference between Delphi or FPC’s component constructor and our’s, is that we have a second, optional parameter. An anonymous procedure (a.k.a callback) that allows you to daisy-chain constructors and together – and thus initialize properties immediately, as a part of the constructor itself, rather than afterwards.

The benefit of this technique is that you can “recursively nest” work together, forcing JavaScript to create the entire UI in a single swoop. This technique is something that JSVM (JavaScript virtual machine) excels at and agressively JIT optimize -which is ultimately why QTX code is blistering fast. Even for large and complex UI designs containing hundreds, if not thousands of widgets.

The downside has been that you have been limited to two things:

  1. Create child widgets or objects
  2. Initialize properties

If you tried to do anything else, like accessing a widget further up the chain (which might not be ready yet due to the reverse nature of daisy chained constructors) you would run into access violations.

To make this more clear, imagine you daisy-chain three constructors together to create three widgets as quickly as possible:

  1. First widget
    • Second widget
      • Third widget

In the above sequence, the third widget would in fact be available first, since that is at the end of the recursive sequence. This means that if you tried to access the first or second widget from the third, you would cause an access violation. A widget is by nature only ready to be accessed after it’s callback had finished.

Making it easier

Since this clearly has been counter intuitive we decided to move where we invoke the callback. We have moved it to a spot after the widget is ready to be used, but before ObjectReady() is invoked. This means that child elements will already be in the DOM when the callback executes, and that elements further up in a nested sequence will be available.

Code such as this will then be perfectly valid:

var fHeader:= TQTXHeader.Create(self, procedure (Header: TQTXHeader)
begin
?Header.ButtonLeft.Caption := "Back";
?Header.ButtonRight.Caption := "Forward";
end);
In the new RTL timing is more natural and intuitive

The above code failed in previous versions of the RTL, because there was no guarantee that TQTXHeader had in fact created it’s child buttons. The ButtonLeft and ButtonRight child objects would have been nil.

Since we know the callback and it’s limitations has been confusing and counter intuitive, especially the limitations — we decided it was worth the effort to change it. Most people will immediately assume that they can access widgets and objects there, so it makes sense to meet that expectation as best we can.

We do still suggest you override the InitializeObject() and FinalizeObject() methods rather than the constructor, but these methods are just time-savers since you dont have to type the entire constructor syntax and callback type.

Consequences

The consequences of this is surprisingly little and only affects the RTL and component writers. In 99% of the cases I have encountered so far, the only consequence is that creation-order is flipped. Like outlined above, the last constructor would finish first in the old RTL, but now they finish first-to-last (in the order you have nested them).

The only UI change is (so far) with regards to zIndex. I am sure there will be edge cases, but updating code written especially for the older RTL is very simple. We are talking one-liners or, at most, re-arranging the sequence you create child elements. You can still use WhenReady() to line up work that needs to happen when ObjectReady() is invoked, just like before. So the change is abysmall.

Since Align: TAlign is now supported out of the box for all TQTXWidget’s, you dont need to override Resize() to manually deal with layout. So all in all writing your own complex widgets is now easier than ever.

Sourcemaps and debugging

We still have some way to go before Chrome’s debugger protocol is fully integrated with the IDE, but we are making excellent headway. The compiler now emits the source-maps, and the IDE clones out the pascal code (which is needed by the browser debugger if you wish to use Chrome’s tools directly) into it’s own debug folder.

Once we get more tickets done the goal is to talk directly with Chrome (or Edge) so you can set breakpoints directly in the pascal editor, inspect variables and parameters, and basically work with the code like you do in Delphi or Lazarus. The protocol is already wrapped and ready, so this will be awesome!

Support for Linux, MacOS and ARM

We have already gotten the compiler to spin on Linux, MacOS and Windows from the same codebase, and likewise for ARM Linux (suitable for Raspberry PI, ODroid, Rock Pi 5 and similar SBC [single board computers]) so that is exciting!

I want to add Risc-V to the list of platforms, but we have to research what that entails or how well Freepascal supports the platform. From what we understand so far, support is fairly complete and it should be possible to compile for Linux on Risc-V once our codebase is 100% platform independent.

The goal ofcourse is to make both the IDE and compiler 100% platform independent (the compiler already running on said targets and chipsets), so that it compiles both under Delphi and Freepascal. This will require a substancial refactor of our codebase, but it’s something we want to do. This will have significant benefits for us both in terms of product demographic and adoption, but also for schools and educational institutions where affordable ARM devices is used for IoT. Being able to work on such devices is definitively a great bonus!

Stay tuned, QTX is coming!

Published by Jon Lennart Aasenden

Lead developer for Quartex Pascal

3 thoughts on “Changes, fixes and debugger

Leave a Reply