[Pas2js] Exception handling in callbacks/event handlers

Michael Van Canneyt michael at freepascal.org
Sun May 17 11:08:05 CEST 2020


Hello,

We've implemented a small change in the event handling mechanism, with
possibly far reaching consequences.

Previously, when an exception happened in a event handler of some browser
class: an onclick or onchange, this exception would go unnoticed in pascal
code. You would see it only in the console of the browser.

Even if you set the showUncaughtExceptions options of the rtl object, like
so:
     rtl.showUncaughtExceptions=true;
     rtl.run();
then the exceptions would still not be shown.

This behaviour was similar to exceptions raised in threads in a native
application: the exception was never shown in the main thread.

We have now introduced the 'safecall' modifier for an event handler:
When you use this modifier, the rtl will change the small wrapper it
generates for callbacks, so it catches any exceptions, and routes the
exception through the rtl exception handler.

The 'safecall' modifier must only be specified on the event handler
definition, the actual event handler does not need this flag, so your code
does not need any changes.

That being the case, I have changed the definition of the basic event handler type in
the web.pas unit:

TJSRawEventHandler = reference to Procedure(Event: TJSEvent); safecall;

That means that now, when you click a button and an exception is raised, the
exception is handled like an exception in the 'main' program section.

Note that any javascript errors in an event handler will also be caught and handled this way.

At the same time the SysUtils unit now has - in addition to the existing
'ShowException' routine - several new hooks:

Var
   // Handler to show an exception (used when showexception is called)
   OnShowException : TShowExceptionHandler = nil;

// Set handlers for uncaught exceptions. These will call HookUncaughtExceptions
// They return the old exception handler, if there was any.
Function SetOnUnCaughtExceptionHandler(aValue : TUncaughtPascalExceptionHandler) : TUncaughtPascalExceptionHandler;
Function SetOnUnCaughtExceptionHandler(aValue : TUncaughtJSExceptionHandler) : TUncaughtJSExceptionHandler;

// Hook the rtl handler for uncaught exceptions. If any exception handlers were set, they will be called.
// If none were set, the exceptions will be displayed using ShowException.
Procedure HookUncaughtExceptions;

The 'HookUnCaughtException' will tap into the rtl exception handler routine:
when the rtl catches an exception, the hook will try to display it, or you
can route the exception to a routine of your choice.

Note that there are 2 routines: 
One for pascal exceptions, one for Javascript exceptions. 
This is because a TJSObject is not a TObject.

Note that as long as you don't set
    rtl.showUncaughtExceptions=true;
Nothing changes: exceptions will still get caught by the rtl, but will be re-raised at
once, which is fully backwards compatible.

To make it easier for you, the Lazarus IDE's File-New 'Web Browser Application' wizard 
has been extended with an option to set this flag for you when it generates 
a new project.

A demonstration program has been committed to the demos (errorhandler), so you can 
test this new feature.

Enjoy !

Michael.


More information about the Pas2js mailing list