Skip to content

x64: Application exit abnormally when exception raised in event #287

Description

@ccy

Try run this script in Win64 build that raise exception in event handler:

procedure OnChange(Sender: TObject);
begin
   Abort;
end;

function Execute: string;
var s: TStringList;
begin
  s := TStringList.Create;
  try
    s.OnChange := @OnChange;
    s.Add('test');
  finally
    s.Free;
  end;
end;

The above code works in Win32 build that raise proper EAbort exception.

Activity

  1. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    @pult Any idea what could be wrong? Above code crashes for me as well with the improved x64.inc from #149.

  2. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    Or would could this be related to #107's fix @velter? Before #107 the code would not run at all, but does the fix include exception support?

  3. evgeny-k commented on Jan 12, 2026

    @evgeny-k
    Contributor

    pay attention, that testcase is a bit incorrect.

    event is declared as of object (i.e. Method Pointers):

      TStringList = class(TStrings)
    ..
        property OnChange: TNotifyEvent read FOnChange write FOnChange;
    
    TNotifyEvent = procedure(Sender: TObject) of object;
    

    but in testcase, it is used as usual method: procedure OnChange(Sender: TObject);.

    check more about differences at https://docwiki.embarcadero.com/RADStudio/Sydney/en/Procedural_Types_%28Delphi%29

  4. ccy commented on Jan 12, 2026

    @ccy
    ContributorAuthor

    @evgeny-k , you are right about TNotifyEvent. However, the sample script works on Win32 build.

    Would you suggest a way to assign write TNotifyEvent in the script and assign to TStringList.OnChange?

  5. evgeny-k commented on Jan 12, 2026

    @evgeny-k
    Contributor

    in Delphi, this code works as expected:

    
    type
      myclass = class
      public
        class procedure OnChange(Sender: TObject);
      end;
    
    class procedure myclass.OnChange(Sender: TObject);
    begin
      //ShowMessage('Abort was called');
      Abort;
    end;
    
    function Execute: string;
    var
      s: TStringList;
    begin
      s := TStringList.Create;
      try
        s.OnChange := myclass.OnChange;
        s.Add('test');
        Result := s.Text;
      finally
        s.Free;
      end;
    end;
    
    procedure TForm15.Button1Click(Sender: TObject);
    begin
      ShowMessage(execute);
    end;
    

    so I would start "play" with it.

  6. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    @evgeny-k The problem appears to be the x64 code of MyAllMethodsHandler: it is not set up to allow exception handling.

    This is what happens with the script above:

    • MyAllMethodsHandler is called which then calls MyAllMethodsHandler3.
    • MyAllMethodsHandler3 catches the EAbort and reraises it at line 12720 of UPSRuntime (raise TObject(FStack);).
    • Control then simply returns to MyAllMethodsHandler, which is wrong.
      • It crashes at next //Restore stack, ADD RSP, 40h, much like x64.inc did before @pult's changes were added.
    • Instead control should have returned to the try .. except handling of the TPSExternalProcRec(u).ProcPtr call at line 8432 of UPSRuntime

    Updating MyAllMethodsHandler to be like x64.inc makes the script work in my test, regardless of your of object obversation.

    Do you agree it needs this fix? According to Git you are the author of the x64 code of MyAllMethodsHandler.

  7. ccy commented on Jan 12, 2026

    @ccy
    ContributorAuthor

    @martijnlaan , what is your fix for MyAllMethodsHandler ?

  8. evgeny-k commented on Jan 12, 2026

    @evgeny-k
    Contributor

    @martijnlaan

    Updating MyAllMethodsHandler to be like x64.inc makes the script work in my test, regardless of your of object obversation.

    Do you agree it needs this fix? According to Git you are the author of the x64 code of MyAllMethodsHandler.

    if you can fix that code, it would be great.

  9. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    pay attention, that testcase is a bit incorrect.

    event is declared as of object (i.e. Method Pointers):

    but in testcase, it is used as usual method: procedure OnChange(Sender: TObject);.

    Don't think I agree, it's in fact the opposite: the events must be of object, because ROPS (ab)uses Self/Method.Data to store the info it needs to run the event, as setup by GetMethodInfoRec. So the testcase is correct.

    Am in the progress of finishing up the MyAllMethodsHandler fix, and also found another problem: in a 64-bit build SupportsRegister should return True for (U)Int64, but it doesn't, which breaks any (U)Int64 parameters used by events.

  10. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    if you can fix that code, it would be great.

    Ok, thanks, please review my 2 commits: a118e3a and cd31a61

  11. martijnlaan commented on Jan 12, 2026

    @martijnlaan
    Collaborator

    By the way, I also checked @pult's fork but was unable to find if it had a solution for this problem. I did notice a comment saying '{$include x64.inc} // can implement "MyAllMethodsHandler"' but was unsure what that means.

  12. added a commit that references this issue on Jan 13, 2026
  13. 23 remaining items

  14. evgeny-k commented on Jan 15, 2026

    @evgeny-k
    Contributor

    I saw this in FPC code , so we also can use {$ifdef FPC_HAS_TYPE_EXTENDED} in PS.

    {$ifdef FPC_HAS_TYPE_EXTENDED}
    Function FloatToStrF(Value: Extended; format: TFloatFormat; Precision, Digits: Integer): String;
    Function FloatToStrF(Value: Extended; format: TFloatFormat; Precision, Digits: Integer; Const FormatSettings: TFormatSettings): String;
    {$endif FPC_HAS_TYPE_EXTENDED}
    ...
    {$ifdef FPC_HAS_TYPE_EXTENDED}
    Function FloatToStr(Value: Extended): String;
    Function FloatToStr(Value: Extended; Const FormatSettings: TFormatSettings): String;
    {$endif FPC_HAS_TYPE_EXTENDED}
    
  15. martijnlaan commented on Jan 15, 2026

    @martijnlaan
    Collaborator

    TExtended80Rec is used when a script containing a literal such as 1.0 is compiled with a 64-bit compiler but executed by a 32-bit runner. Without TExtended80Rec, the 64-bit compiler would write an 8-byte value to the bytecode, because Extended is 8 bytes in 64-bit code. The 32-bit runner, however, would try to read a 10-byte value, because Extended is 10 bytes in 32-bit code.

    To avoid this mismatch, the 64-bit compiler uses TExtended80Rec, so it can write 10 bytes, in the correct binary format.

    When using a 32-bit compiler with a 64-bit runner, the fix is simpler: the 32-bit compiler just needs to write an 8-byte Double instead of a 10-byte Extended.

    Once you understand the above, it should be clear that FPC_HAS_TYPE_EXTENDED is unrelated and does not solve this problem.

  16. evgeny-k commented on Jan 15, 2026

    @evgeny-k
    Contributor

    I want to compile PascalScriptFCL.lpk
    suggest any workaround how I can do this.
    I have FPC 3.2.2 x86_64-win64-win32/win64

    Image
  17. added a commit that references this issue on Jan 16, 2026
  18. martijnlaan commented on Jan 16, 2026

    @martijnlaan
    Collaborator

    I would do like IFDEF CPU64 IFDEF DELPHI DEFINE USETEXTENDED80REC and then use that in the code. Can add it later.

  19. added a commit that references this issue on Mar 19, 2026
  20. added a commit that references this issue on Apr 28, 2026
  21. added a commit that references this issue on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions