Description
Currently we can mark functions (and other objects) as @pragma('vm:entry-point')
.
Having this annotation implies currently a number of things:
- The function serves as a root for our AOT tree shaker (i.e. it's code and what it calls cannot be tree-shaken)
- The function may be looked up by name (i.e. cannot be obfuscated)
- The function needs to have it's
Function
object preserved for AOT runtime (i.e. cannot tree-shake function object) - The function's parameter types etc need to be preserved so arguments can be type-checked before invoking the function (i.e. cannot tree-shake function metadata)
There's use cases where we'd only need 2/3/4 but not 1 and as such would allow us to sometimes tree shake chunks of apps away that happen to be unused.
Case for @pragma('vm:weak-entry-point')
Flutter provides an API to map a function to a "unique" integer. When the app later on gets started (e.g. due to an OS event that starts the app) the app can look up based on that unique integer the dart function and call it. More specifically an app can
- call
dart:ui
sPluginUtilities.getCallbackHandle(function)
- Internally this will use Dart VM runtime-reflection on the
function
provided to obtain: library/class/function names of the tear-offfunction
- seeDartRuntimeHooks::GetCallbackHandle()
- (The function has therefore to be a global/static function annotated via
@pragma('vm:entry-point')
) - Assign a number to the function (identified by library/class/function name) and write a json file to-disc that maps the integer to the library/class/function names
DartCallbackCache::GetCallbackHandle()
- Internally this will use Dart VM runtime-reflection on the
- later on call PluginUtilities.getCallbackFromHandle
- which will end up loading file on disc, looking up the integer, getting library/class/function names, and get the corresponding dart function
Now the interesting thing to note here is that if we have e.g.
void register() {
if (<always-false>) {
// Dead code, call will be removed by AOT compiler.
PluginUtilities.getCallbackHandle(myCallback);
}
}
void foo() {
final function = PluginUtilities.getCallbackFromhandle(CallbackHandle.fromRawHandle(123));
// `function` can never be `myCallback` (since we tree-shaken `PluginUtilities.getCallbackHandle(myCallback)`).
}
@pragma('vm:entry-point')
void myCallback() {
<lots of things we do here>
}
then we (as application developer) know that myCallback
will never be invoked.
To support this use case, we could add a @pramga('vm:weak-entry-point')
that will have the same implications as a normal @pragma('vm:entry-point')
with the difference that we allow our AOT tree shaker to tree shake it, but if it survived we have to guarantee 2/3/4 above.
/cc @alexmarkov @mraleph