Martin Kustermann 4455e224e9 Fix FFI-based tonic layer to use proper C++ static_cast<>()s (flutter/engine#47644)
Flutter makes use of C++ multiple inheritence in it's C++ classes that
have corresponding Dart objects attached. An example is e.g.
```
  class Canvas : public RefCountedDartWrappable<Canvas>, DisplayListOpFlags { }

  template <typename T>
  class RefCountedDartWrappable : public fml::RefCountedThreadSafe<T>,
                                  public tonic::DartWrappable { }
```
When a C++ class has multiple base classes, the C++ compiler has the
liberty to decide the order in which they get layed out in memory. (It
doesn't necessarily follow declaration order, in fact it has a
preference for having classes with
vtables before classes without vtables.)

Two casts to consider (with multiple inheritance in mind):

* upcasts: Those are done by C++ automatically but can modify the actual
address (i.e. pointer value)

* downcasts: Those have to be done with `static_cast<>()`, which can
also modify the address (i.e. pointer value) - using
`reinterpret_cast<>()` is incorrect (as it doesn't modify the address)

Now the Dart objects that refer to C++ objects have a native field in
them. The value of that field is a `tonic::DartWrappable*`. That means
whenever we convert between the C++ object pointer (e.g.
`flutter::Canvas*`) and the value of the native field
(`tonic::Wrappable*`) the actual address / pointer may need
modification.

There were bugs in the C FFI based tonic layer: Pointer values coming
from Dart (via C FFI) are `dart::Wrappable*` and not pointers to
subtypes.

=> For methods we type the first parameter in tonic trampolines as
    `dart::Wrappable*` and `static_cast<Class*>()` that value

=> Similarly for arguments, the parameter has to be typed as
    `dart::Wrappable*` and `static_cast<Class*>()` that value

How did it ever work? Well it happens to be that the C++ compiler
decided to layout `tonic::DartWrappable` before
`fml::RefCountedThreadSafe`, making the `tonic::DartWrappable*` and the
`flutter::Canvas*` (and other classes) have the same pointer value.

=> This worked due to implementation choice of C++ compiler.
=> This breaks immediately if one decided to add another base class
(with virtual methods) to `RefCountedDartWrappable`
2023-11-06 11:07:55 +01:00
..