Description
Current implementation (and documenation) of PluginUtilities.getCallbackHandle
does not respect the concept of tree-shaking and consequently causes problems in release builds whenever it is used, especially when it is used as an implementation detail.
PluginUtilities.getCallbackHandle
expects that any static function in a Dart application can be uniquely identified by a tuple (libraryUri, className, functionName)
, meaning that it expects to be able to convert any static function tear-off into such tuple and then expects to be able to look up the same tear-off by doing reflective access through Dart VM C API.
While this does work reliably in debug (JIT) mode, this is not guaranteed to work in release (AOT) mode. Release mode requires that any reflectively accessed program element (library, method, class, function) is annotated with @pragma('vm:entry-point')
. Program elements not annotated as entry points might be shaken from the resulting binary or at least will be evicted from lookup dictionaries, making reflective access to them impossible.
This introduces discrepancy between release/profile and debug modes, which leads to obscure crashes like this one:
Ultimately this means at the very least we need to ensure that any function passed to PluginUtilities.getCallbackHandle
is annotated with @pragma('vm:entry-point')
. We need to update documentation to make this requirement absolutely clear. Note that this needs to be applied transitively: e.g. if state restoration code is using PluginUtilities.getCallbackHandle
under the hood we need to document that some specific function needs to be annotated.
Dart VM has a flag which enforces the presence of entry-point
annotations on any reflectively accessed element (--verify_entry_points
). This flag was not turned on by default because it was breaking Flutter applications - I think we need to turn it on because the applications which hit those asserts are broken anyway (/cc @mkustermann)
Long term Flutter needs to redesign the implementation of PluginUtilities.getCallbackHandle
to avoid this problem. One possible direction is described in #94571 by @ds84182.
A potential Dart language level solution requires some way to enforce const-ness of the parameter value. I have submitted language level proposal here: dart-lang/language#2776