| This package provides the data classes for the usage recording feature in the |
| Dart SDK. |
| |
| During compilation, usages of declarations annotated with `@RecordUse()` in |
| reachable code are recorded, and information about these usages is made available |
| to post-compile steps (such as link hooks). Usages in unreachable code are not |
| recorded. |
| |
| - If placed on a statically resolved function or member (such as a top-level |
| function, static method, non-redirecting factory constructor, getter, |
| setter, operator, extension method, or extension type method), all calls |
| to or tear-offs of that member in reachable code will be recorded, along |
| with arguments passed to the call as far as they can be evaluated as |
| constant expressions at compile time. Generative constructors and |
| redirecting factory constructors cannot be annotated directly. |
| - If placed on a class, any constant instance of the class (including |
| instances created via `const` redirecting factory constructors), any |
| non-const generative constructor invocation, and any generative |
| constructor tear-off in reachable code will be recorded. Calls to |
| non-const factory constructors are not recorded directly by annotating the |
| class; rather, any generative constructor invocation within the factory |
| body will be recorded. |
| |
| Only usages in executable code are recorded. Usages appearing within metadata |
| (annotations) are ignored. |
| |
| > [!NOTE] |
| > The `@RecordUse()` annotation is only allowed on declarations within a package's |
| > `lib/` directory. |
| |
| ## Example |
| |
| <!-- file://./example/api/usage.dart#usage --> |
| ```dart |
| void main() { |
| PirateTranslator.speak('Hello'); |
| print(const PirateShip('Black Pearl', 50)); |
| } |
| |
| abstract class PirateTranslator { |
| @RecordUse() |
| static String speak(String english) => 'Ahoy $english'; |
| } |
| |
| @RecordUse() |
| final class PirateShip { |
| final String name; |
| final int cannons; |
| |
| const PirateShip(this.name, this.cannons); |
| } |
| ``` |
| This code will generate a data file that contains both the field values of |
| the `PirateShip` instances, as well as the arguments for the `speak` |
| method annotated with `@RecordUse()`. |
| |
| This information can then be accessed in a link hook as follows: |
| <!-- file://./example/api/usage_link.dart#link --> |
| ```dart |
| void main(List<String> arguments) { |
| link(arguments, (input, output) async { |
| final uses = input.recordedUses; |
| if (uses == null) return; |
| |
| final calls = uses.calls[methodId] ?? []; |
| for (final call in calls) { |
| switch (call) { |
| case CallWithArguments( |
| positionalArguments: [StringConstant(value: final english), ...], |
| ): |
| // Shrink a translations file based on all the different translation |
| // keys. |
| print('Translating to pirate: $english'); |
| case _: |
| print('Cannot determine which translations are used.'); |
| } |
| } |
| |
| final ships = uses.instances[classId] ?? []; |
| for (final ship in ships) { |
| switch (ship) { |
| case InstanceConstantReference( |
| instanceConstant: InstanceConstant( |
| fields: {'name': StringConstant(value: final name)}, |
| ), |
| ): |
| // Include the 3d model for this ship in the application but not |
| // bundle the other ships. |
| print('Pirate ship found: $name'); |
| case _: |
| print('Cannot determine which ships are used.'); |
| } |
| } |
| }); |
| } |
| ``` |
| |
| ## Contributing |
| Contributions are welcome! Please open an issue or submit a pull request. |