Search code examples
exceptiondelphidwscript

How to get Delphi side exception class from generic EDelphi exception in script


Calling a Delphi side function from a script that raises an exception raises a generic EDelphi exception on the script side. This hides the exception class of the actual Delphi side exception. I tried to investigate the implementation of the EDelphi Ijaddajadda interface but am lacking skills to see a way to extract the Delphi side class or at least the class name. The truth has to be out there...

Example, script side

Try
 MyDelphiSideFunction; // May raise EOutOfmemory
Except on E:Exception do
 PrintLn(E.ClassName);
End;

This always prints "EDelphi", no matter the actual Delphi side exception class (EOutOfmemory in this example).

How to get the "real" class or class name from E?


Solution

  • You cannot directly get the exception class type as the DWScript exception handler doesn't store that information anywhere and the Delphi side type wouldn't make sense inside the script anyway. You can however easily get the class name from the EDelphi script side exception object via the EDelphi.ExceptionClass property. Like this:

    Script

    try
    
      var x := 1/0;
    
    except
      on E: EDelphi do
        WriteLn('Error: ' + E.Message + ' (' + E.ExceptionClass + ')');
    
      on E: Exception do
        WriteLn('Error: ' + E.Message);
    else
      WriteLn('Unknown exception type');
    end;
    

    Output

    Error: Floating point division by zero (EZeroDivide)
    

    You can see the DWScript source code that transforms a Delphi side exception to a script side EDelphi exception here: https://github.com/EricGrange/DWScript/blob/2d61c8a95fb4a2aab328f3cc84bb9f243c927286/Source/dwsExprs.pas#L2244

    How I found the answer

    I simply searched the source on Github (the search on the main Bitbucket repo sucks) for "EDelphi": https://github.com/search?q=repo%3AEricGrange%2FDWScript%20EDelphi&type=code

    This gave me this piece of code:

    // A Delphi exception. Transform it to a EDelphi-dws exception
    exceptObj:=CreateEDelphiObj(mainException.ClassName, mainException.Message);
    

    I then looked at the CreateEDelphiObj method:

    function TdwsProgramExecution.CreateEDelphiObj(const ClassName : String;
                                       const Message : String) : IScriptObj;
    begin
       Result := IScriptObj(IUnknown(
          ProgramInfo.Vars[SYS_EDELPHI].Method[SYS_TOBJECT_CREATE].Call([ClassName, Message]).Value));
       (Result.ExternalObject as TdwsExceptionContext).ReplaceTop(LastScriptError); // temporary constructor expression
    end;
    

    This looks like a call to a script side constructor, passing the ClassName and Message strings as parameters. That's fine, but what happens to the ClassName value?

    Again, looking at the search result I noticed the Delphi side implementation of the EDelphi constructor:

    procedure TDelphiExceptionCreateMethod.Execute(info : TProgramInfo; var ExternalObject: TObject);
    begin
       inherited;
       Info.ValueAsVariant[SYS_EDELPHI_EXCEPTIONCLASS_FIELD]:=Info.ValueAsVariant['Cls']
    end;
    

    So the constructor calls the inherited constructor and then stores the ClassName value in the FExceptionClass (the value of the SYS_EDELPHI_EXCEPTIONCLASS_FIELD constant) field.

    At this point I could guess that the property would be called ExceptionClass so I used my DWScriptStudio IDE/debugger to write the example and verify the result. DWScriptStudio