Pragma Annotations understood by dart2js

Pragmas for general use

PragmaMeaning
dart2js:noInlineNever inline a function or method
dart2js:never-inlineAlias for dart2js:noInline
dart2js:tryInlineInline a function or method when possible
dart2js:prefer-inlineAlias for dart2js:tryInline
dart2js:disable-inliningDisable inlining within a method
dart2js:noElisionDisables an optimization whereby unused fields or unused parameters are removed

Unsafe pragmas for general use

These pragmas are available for use in third-party code but are potentially unsafe. The use of these pragmas is discouraged unless the developer fully understands potential repercussions.

PragmaMeaning
dart2js:as:checkCheck as casts
dart2js:as:trustTrust as casts
dart2js:downcast:checkCheck downcasts
dart2js:downcast:trustTrust downcasts
dart2js:index-bounds:checkTBD
dart2js:index-bounds:trustTBD
dart2js:late:checkCheck late fields are used correctly
dart2js:late:trustTrust late fields are used correctly
dart2js:parameter:checkTBD
dart2js:parameter:trustTBD
dart2js:types:checkTBD
dart2js:types:trustTBD

Pragmas for internal use

These pragmas can cause unsound behavior if used incorrectly and therefore are only allowed within the core SDK libraries.

PragmaMeaning
dart2js:assumeDynamicTBD
dart2js:disableFinalTBD
dart2js:noSideEffectsRequires dart2js:noInline to work properly
dart2js:noThrowsRequires dart2js:noInline to work properly

Detailed descriptions

Annotations related to function inlining

Function (method) inlining is a compiler optimization where a call to a function is replaced with the body of the function. To perform function inlining, the compiler needs to determine that the call site calls exactly one function, the target. This is trivial for top-level methods, static methods and constructors. For calls to instance methods, the compiler does an analysis of the possible types of the receiver and uses that to reduce the set of potential targets. If there is a single target, it can potentially be inlined.

Not all functions can be inlined. For example, a recursive function cannot be expanded by inlining indefinitely. dart2js will not inline functions complex control flow, such as methods with exception handling (try-catch-finally) or many return or throw exit points.

We say a function is a viable inlining candidate when it is the single target and it is possible to perform the inlining.

One benefit of inlining is that the execution cost of performing the call is avoided, which can be a substantial part of the total cost of the call when the body of the callee is simple. Copying instructions from the callee into the caller can create more opportunities for optimization, for example, it becomes possible to recognize and remove repeated operations.

The compiler automatically makes a decision whether or not to inline a function or method based on heuristics. One heuristic is to inline if the the inlined code is likely to be smaller that the call, as this results in a smaller and faster program. Another heuristic is to inline even if the code is likely to be slightly larger when the call is in a loop, as loops here is a chance that some of the code can be hoisted out of the loop.

The annotations described below allow the developer to override the default decisions. They should be used sparingly since it is likely that over time manual overrides will become increasingly out of date and mismatched with the evolving capabilities of the compiler.

Requesting a function be inlined

@pragma('dart2js:tryInline')
@pragma('dart2js:prefer-inline) // Alias for the above annotation.

This annotation may be placed on a function or method.

The compiler will inline the annotated function wherever it is a viable inlining candidate.

Requesting a function never be inlined

@pragma('dart2js:noInline')
@pragma('dart2js:never-inline) // Alias for the above annotation.

This annotation may be placed on a function or method to prevent the function from being inlined.

Disabling inlining

@pragma('dart2js:disable-inlining')

This annotation may be placed on a function or method.

Function inlining is disabled at call sites within the annotated function. Inlining is disabled even when the call site has a viable inlining candidate that is annotated with @pragma('dart2js:tryInline').

Annotations related to run-time checks

The Dart language and runtime libraries mandate checks in various places. Checks result in some kind of Error exception being thrown. If a program has a high degree of test coverage, the developer might have some confidence that the checks will never fail. If this is the case, the checks can be disabled via command line options or annotations. Annotations override the command line settings.

Trusting (i.e. disabling) checks can lead to a smaller and faster program. The cost is highly confusing unspecified behavior in place of the Errors that would otherwise have been thrown. The unspecified behavior is not necessarily consistent between runs and includes the program execution reaching statements that are ‘impossible’ to reach and variables being assigned values of an ‘impossible’ type.

Casts

@pragma('dart2js:as:check')
@pragma('dart2js:as:trust')

These annotations may be placed on a function or method to control whether as casts in the body of the function are checked.

One use of dart2js:as:trust is to construct an unsafeCast method.

@pragma('dart2js:tryInline')
@pragma('dart2js:as:trust')
T unsafeCast<T>(Object? o) => o as T;

The tryInline pragma ensures that the function is inlined, removing the cost of the call and passing the type parameter T, and the as:trust pragma removes the code that does the check.

Downcasts

@pragma('dart2js:downcast:check')
@pragma('dart2js:downcast:trust')

These annotations may be placed on a function or method to control whether implicit downcasts in the body of the function are checked.

This is similar to the dart2js:as:check and dart2js:as:trust pragmas except it applies to implicit downcasts. Implicit downcasts are as checks that are inserted to cast from dynamic.

The unsafeCast method described above could also be written by trusting implicit downcasts.

@pragma('dart2js:tryInline')
@pragma('dart2js:downcast:trust')
T unsafeCast<T>(dynamic o) => o; // implicit downcast `as T`.

Trusting implicit downcasts is part of the -O3 and -O4 optimization level command line options. dart2js:downcast:check can be used to enable checking of implicit downcasts in a method when it would otherwise be trusted due to the command line options.

Late checks

Late checks - checking whether a late variable has been initialized - occur on all late variables. The checks on late instance variables (i.e. late fields) can be controlled via the following annotations.

@pragma('dart2js:late:check')
@pragma('dart2js:late:trust')

These annotations may be placed on the declaration of a late field, class, or library. When placed on a class, the annotation applies to all late fields of the class. When placed on a library, the annotation applies to all late fields of all classes in the library. dart2js:late annotations are scoped: when there are multiple annotations, the one nearest the late field wins.

In the future this annotation might be extended to apply to late local variables, static variables, and top-level variables.