-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Currently, the Flutter Embedder API in AOT mode requires the embedder to specify 4 buffers (for the VM and isolate heap and snapshots) to the FlutterEngineInitialize or FlutterEngineRun calls. The Embedder API takes an entirely hands off approach towards where these buffers come from. Existing embedders usually put these into Flutter Dart application specific dynamic libraries and resolve them at runtime (dlopen and dlsym) before providing them to the engine. The embedders use gen_snapshot on the host to create these buffers from their Dart application sources (via an intermediate step to create kernel files). Detailed instructions on creating, packaging and providing these buffers to the Flutter Engine are detailed on this page in the wiki.
More recently, gen_snapshot has gained the ability to package the 4 buffers into an ELF file (without the need for a native toolchain). The ELF binary contains the exact same buffers. These buffers can be loaded by dlopen or dlsym on platforms that support ELF as an executable format. On platforms where ELF is not supported (Mach-O on macOS/iOS and COFF on Windows), the Flutter Engine provides a cross platform ELF loader for the resolution of these symbols.
Today, the same 4 buffers are necessary for AOT mode operation irrespective of whether they come from memory mapped blobs, symbols resolved via system utilities, or symbols resolved via the cross platform portable ELF loader. However, there is a possibility that the specifying just these 4 blobs will be insufficient to support dart:ffi completely in the future. There is strong guidance from the Dart team to move to an API where the embedder provides a dynamic library (ELF or otherwise) instead of just 4 buffers.
To be clear, the current API works fine and existing embedders can continue using the API to support AOT mode operation. But, an API that works with dynamic libraries instead of the 4 buffers makes a lot of sense from a packaging and forward compatibility perspective. This issue describes such a non-breaking extension to the Embedder API.
To make it so that existing embedders continue to work, have a clear (and optional) transition path to FFI readiness, and have an easy to use API for portable loading of ELF (if necessary), the following API is proposed. The existing API will change but this would be the recommendation for AOT moving forward.
//------------------------------------------------------------------------------
/// Specifies one of various locations the engine can look for AOT data sources.
///
typedef struct {
// Typed union of either a path to a dylib, a file descriptor pointing to the
// dynamic library, or, an existing mmapped buffer of the dynamic library. As
// a compatibility affordance, the embedder could give the 4 buffers directly
// too. A library handle could be provided too maybe.
} FlutterEngineAOTDataSource;
//------------------------------------------------------------------------------
/// An opaque object that describes the AOT data that can be used to launch
/// Flutter Engine instance in AOT mode.
///
typedef struct _FlutterEngineAOTData* FlutterEngineAOTData;
//------------------------------------------------------------------------------
/// @brief Creates the necessary data structures to launch a Flutter Dart
/// application in AOT mode. The data may only be collected after
/// the FlutterEngine instance launched using this data is shut
/// down.
///
/// @param[in] source The source of the AOT data.
/// @param[out] data_out The AOT on success. Unchanged on failure.
///
/// @return Returns if the AOT data could be successfully resolved.
///
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineCreateAOTData(
const FlutterEngineAOTDataSource& source,
FlutterEngineAOTData& data_out);
//------------------------------------------------------------------------------
/// @brief Collects the AOT data.
///
/// @warning The embedder must ensure that this call is made only after all
/// FlutterEngine instance launched using this data have been
/// shutdown.
///
/// @param[in] data The data to collect.
///
/// @return If the AOT data was successfully collected.
///
FLUTTER_EXPORT
FlutterEngineResult FlutterEngineCollectAOTData(FlutterEngineAOTData data);
Once the embedder successfully obtains an instance of FlutterEngineAOTData, it may pass it to a new field introduced at the end of the FlutterProjectArgs struct to launch an engine instance. For backwards compatibility, the 4 buffers can still be specified in the FlutterProjectArgs struct. However, specifying two sources will terminate engine launch.
The implementation within the engine will do the following:
- Attempt to resolve symbols from the dynamic library using system APIs (
dlopen/dlsym). - If the executable is ELF, use the portable ELF loader to resolve symbols.
Since the FlutterEngineAOTData is itself an opaque object, that should allow the Embedder API some latitude in the future to tinker with the kinds of buffers to be resolved from the dynamic library and control how specific features are enabled.