Exception handling broken?

It seems that pas2js does not handle "Exception" as the base class of all Exceptions. This way, the regular Delphi style error handling like this will fail in case the actual exception is not of class ERESTRequestError:

Except
   On E:ERESTRequestError do
    Begin
     // Handle specific exception class
    End;
   On E:Exception do
    Begin
     // Handle all other exception classes
    End
End

The "On E:Exception" branch will never be reached for errors thrown e.g. by javascript, like e.g. a "Type Error". And even worse: Although there is an Application error handler installed like this

Application.OnError := MyErrorHandler;

also MyErrorHandler will not catch the error. Instead, the rtl will take care of the exception and handle it in the way defined by Application.ErrorType.

I managed to help myself out of this by catching the error in a generic "Else" branch. The pitfall here is that in the "Else" branch there is no access to the exception object, and to make it even worse, pas2js does not offer acces to the Delphi global ExceptObject. So, where to get the Exception object from? I helped myself with this:

Var
 JSE : Exception;
...
...

Except
   On E:ERESTRequestError do
    Begin
     // Handle specific exception class
    End
   Else
    // Catch all other exceptions
    Begin
     // Instantiate (not raise) a dummy Delphi exception object
     JSE := Exception.Create('');
     ASM
       // Attach the javascript error object in $e to the exception 
       // object as well as the error message
      JSE.FJSError = $e;
      JSE.fMessage = $e.message;
     END
     // You now have a "regular" Delphi exception in JSE
     // Handle the JSE exception ...
    End
  End
End

While this works for me for my further evaluation of the Web Core product, I would like to know if I can savely go with this approach or if I did anything "evil" here that may kick back some time later.

We'll check this with the pas2js team

Here is the answer for you from Michael Van Canneyt from the pas2js team:

It is correct that not all exceptions descend from Exception.

All browser exceptions normally descend from TJSError ("Error" in Javascript).
But even this does not have to be the case, you can raise any object as an exception in Javascript - and a string is also an object.

But the same is true in Delphi: You can perfectly raise an exception with TObject. That all exceptions are descendents of Exception is a convention.
(simply one that all programmers adhere to)

See the following demo application:

uses sysutils;

{$APPTYPE Console}

type
   TMyError = Class(TObject)
     FErrorCode : Integer;
     constructor Create(aErrorCode : Integer);
     property ErrorCode : Integer Read FErrorCode;
   end;

constructor TMyError.Create(aErrorCode : Integer); begin
   FErrorCode:=aErrorCode;
end;

begin
   try
     raise TMyError.Create(aErrorCode)
   except
      on E : Exception do
        Writeln('Caught exception : ',E.MEssage);
   end;
   Readln;
end.

When run, the exception is not caught:

$ ./raiseobj.exe
Exception TMyError in module raiseobj.exe at 0001F5BA.

Because TMyError is not a descendent of Exception...

The situation in Pas2JS is not different, there are simply a lot of exceptions that are not pascal descendents of "Exception".

Now, the good news is that

On JE : TJSError do
   begin

   end;

works as one would expect, i.e. JavaScript exceptions will be caught and the exception object is available.

Thank you very much for this comprehensive explanation and presenting a solution! It comes absolutely clear when you dive further into the innards of pas2js and the speifics of javascript. It's just that TMS in their product presentations are somehow pretending that using Web Core is nearly identical to writing Delphi code. At least that was my assumption when I started the product evaluation. But, it is not! There are a lot of differences and many pitfalls. The problem with the different sets of sources and unidentical interfaces is also not really helpfull...(see my other post some minutes ago).

Unfortunately, not working here:

Try
 Raise TJSError.New('Trallala');
Except
   On JE : TJSError do
    Begin
     // Not going here
    End;
   On E:Exception do
    Begin
     // Not going here
    End;
   Else
    Begin
      // Only going here
    End;

And btw TJSError is missing in unit JS of Component Library Source, but it is in unit JS of Core source...

Please do not mix JavaScript exceptions with Pascal exceptions. These are two different things.
Sticking to Pascal exceptions, this all works as expected:

type
  MyException = class(Exception);


var
  i: integer;
begin
  try
    i := random(10);
    if i > 5 then
      raise Exception.Create('Pascal Exception')
    else
      raise MyException.Create('Pascal MyException');
  except
    on e: MyException do
    begin
       console.log('got MyException',i);
    end;
    on e: Exception do
    begin
       console.log('got Exception',i);
    end;
  end;
end;

The topic was how to catch JS errors, as they are not descending from Exception. Michael Van Canneyt provided a solution On JE : TJSError do. Did you read that? I tested this solution and it failed.

I will ask follow-up from Michael.

Hello, this is Michael.

This was a known bug: the detection of an external class failed in
Except
On E is X do

statements. This has been solved in trunk some time ago, and will be
included in pas2js 1.4.32. (Scheduled to be released this week)

If memory serves well, as a workaround you should be able to do the following:

On E : TJSObject do
begin
if E is TJSError then
begin
end;
end;

Hello Michael,

thank you for tying to help!

I tried:

On E : TJSObject do
 begin
  if E is TJSError then

and pas2js gives me the error "Types are not related: TJSObject and class TJSError"

To help myself I currently do this:

Var
 JSE : Exception;
...
Except
   On E:ERESTRequestError do
    Begin
     // Handle specific exception class
    End
   Else
    // Catch all other exceptions
    Begin
     // Instantiate (not raise) a dummy Delphi exception object
     JSE := Exception.Create('');
     ASM
       // Attach the javascript error object in $e to the exception 
       // object as well as the error message
      JSE.FJSError = $e;
      JSE.fMessage = $e.message;
     END
     // You now have a "regular" Delphi exception in JSE
     // Handle the JSE exception ...
    End
  End
End

I can live with that until TMS releases the fix, but I just wanted to know if I'm doing anything "evil" here that potentially kicks back later.

your solution is OK.

FYI: 1.4.32 has just been released, so you can expect the fix soon.