blob: 2d6fd52cdb2e529dd8e337ad47e714efb20344a0 [file] [log] [blame] [view]
# `vm:entry-point` pragma
The annotation `@pragma("vm:entry-point", ...)` **must** be placed on a class or
member to indicate that it may be resolved, allocated or invoked directly from
native or VM code _in AOT mode_.
To reduce the differences between JIT and AOT mode, entry point annotations are
also checked in JIT mode except for uses of the `dart:mirrors` library and
debugging uses via the VM service.
## Background
Dart VM precompiler (AOT compiler) performs whole-program optimizations such as
tree shaking and type flow analysis (TFA) in order to decrease size of the
resulting compiled apps and improve their performance. Such optimizations
assume that compiler can see the whole Dart program, and is able to discover
and analyse all Dart functions and members which can be potentially executed at
run time. While the Dart code is fully available for precompiler, native code
of the embedder and native methods are out of reach of the compiler. Such
native code can call back to Dart via native Dart API.
In order to guide precompiler, programmer **must** explicitly list entry points
(roots) - Dart classes and members which are accessed from native code. Note
that listing entry points is not optional: as long as program defines native
methods which call into Dart, the entry points are required for the correctness
of compilation.
In addition, when obfuscation is enabled, the precompiler needs to know which
symbols need to be preserved to ensure they can be resolved from native code.
## Syntax
The allowed uses of the annotation are as follows.
### Classes
Any one of the following forms may be attached to a class:
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
class C { ... }
```
If the second parameter is missing, `null` or `true`, the class will be
available for allocation directly from native or VM code.
Note that `@pragma("vm:entry-point")` may be added to abstract classes -- in
this case, their name will survive obfuscation, but they won't have any
allocation stubs.
### Getters
Any one of the following forms may be attached to getters:
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "get")
void get foo { ... }
```
The `"get"` annotation allows retrieval of the getter value via
`Dart_GetField`. `Dart_Invoke` can only be used with getters that return a
closure value, in which case it is the same as retrieving the closure via
`Dart_GetField` and then invoking the closure using `Dart_InvokeClosure`, so
the "get" annotation is also needed for such uses.
If the second parameter is missing, `null` or `true`, it behaves the same
as the `"get"` parameter.
Getters cannot be closurized.
### Setters
Any one of the following forms may be attached to setters:
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "set")
void set foo(int value) { ... }
```
The `"set"` annotation allows setting the value via `Dart_SetField`.
If the second parameter is missing, `null` or `true`, it behaves the same
as the `"set"` parameter.
Setters cannot be closurized.
### Constructors
Any one of the following forms may be attached to constructors:
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "call")
C(this.foo) { ... }
```
If the annotation is `"call"`, then the procedure is available for invocation
(access via `Dart_Invoke`).
If the second parameter is missing, `null` or `true`, it behaves the same
as the `"call"` parameter.
If the constructor is _generative_, the enclosing class must also be annotated
for allocation from native or VM code.
Constructors cannot be closurized.
### Other Procedures
Any one of the following forms may be attached to other types of procedures:
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "get")
@pragma("vm:entry-point", "call")
void foo(int value) { ... }
```
If the annotation is `"get"`, then the procedure is only available for
closurization (access via `Dart_GetField`).
If the annotation is `"call"`, then the procedure is only available for
invocation (access via `Dart_Invoke`).
If the second parameter is missing, `null` or `true`, the procedure is available
for both closurization and invocation.
### Fields
Any one of the following forms may be attached to a non-static field. The first
three forms may be attached to static fields.
```dart
@pragma("vm:entry-point")
@pragma("vm:entry-point", null)
@pragma("vm:entry-point", true/false)
@pragma("vm:entry-point", !const bool.fromEnvironment("dart.vm.product"))
@pragma("vm:entry-point", "get"/"set")
int foo;
```
If the second parameter is missing, `null` or `true`, the field is marked for
native access and for non-static fields the corresponding getter and setter in
the interface of the enclosing class are marked for native invocation. If the
`"get"` or `"set"` parameter is used, only the getter or setter is marked. For
static fields, the implicit getter is always marked if the field is marked
for native access.
A field containing a closure may only be invoked using `Dart_Invoke` if the
getter is marked, in which case it is the same as retrieving the closure from
the field using `Dart_GetField` and then invoking the closure using
`Dart_InvokeClosure`.