Event<T> passes wrong parameter to handler when T is ...

Issue #327 resolved
Jens Mertelmeyer created an issue

Consider the following snippet:

program Project1;

uses System.SysUtils, Spring;

type
    {$M+}
    THandlerProc<T> = reference to procedure(const eventArgs: T);
    {$M-}

    TData = record
        bool1, bool2, bool3: Boolean;
        class operator Equal(const a, b: TData): Boolean;
    end;

var
    sent, received: TData;

    class operator TData.Equal(const a, b: TData): Boolean;
    begin
        Result := (a.bool1 = b.bool1) and (a.bool2 = b.bool2) and (a.bool3 = b.bool3);
    end;

    procedure handleEvent(const eventArgs: TData);
    begin
        received := eventArgs;
    end;

    procedure p();
    var
        myEvent: Spring.Event<THandlerProc<TData>>;
    begin
        myEvent.Add(handleEvent);

        sent.bool1 := False;
        sent.bool2 := True;
        sent.bool3 := False;
        myEvent.Invoke(sent);

        Assert( sent = received );
    end;

begin
    p();
end.

You would expect the assertion to succeed. handleEvent(..) should be called with a record of (False, True, False). However, the passed record has all fields set to false and the assertion fails.

What really confuses me is that the assertion does not fail when TData has four or just two booleans. I have absolutely no clue why this is the case as I am unable to understand and debug the assembler code.

Comments (11)

  1. Stefan Glienke repo owner
    • changed status to open

    Looks to be a bug in System.Rtti as this also happens when doing TRttiMethod.Invoke - I will investigate.

  2. Jens Mertelmeyer reporter

    In case it helps, my Delphi version was “10 Seattle Subscription Update 1”.
    Build was Win32. Win64 appears to work fine.

  3. Stefan Glienke repo owner

    Seems to be records of size 3 are being passed wrong. I am gonna prepare a repro and report it to QP later. You probably have to put a dummy field to make the record size 4 for being passed properly as I can’t do anything about it.

  4. Jens Mertelmeyer reporter

    Yes, that’s what I did. Thanks for taking the time to investigate. Does this affect all of Event<T> with T being TAction<X> and SizeOf(X) = 3? Not sure if it makes sense to make run-time checks and raise an exception…

  5. Stefan Glienke repo owner

    It potentially affects all places that use Rtti Invoke passing such a parameter. And I will not put any mitigation code for RTL bugs. When I got no other things to do I could look if I can patch the defect code similar to other runtime patches that come with Spring4D but it’s probably more of a waste of time that could better be spent elsewhere.

  6. Stefan Glienke repo owner
    • changed milestone to 2.0

    After a more thorough look I found out the issue is in Spring.Events where 3 byte types are treated as passed via register which they surprisingly are not but pushed on the stack. I will fix this.

  7. Log in to comment