record_use library

Dart API to access @RecordUse() recorded usages in link hooks.

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.

The main entrypoint for recorded usages is Recordings.

  • 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. These can be found in Recordings.calls. Generative constructors and redirecting factory constructors cannot be annotated directly.
  • If placed on a final class or enum:
    • For a final 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. These can be found in Recordings.instances. 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.
    • For an enum: any constant enum element in reachable code will be recorded in Recordings.instances.
    • The @RecordUse() annotation cannot be placed directly on an extension type to record instances.

Only usages in executable code are recorded. Usages appearing within metadata (annotations) are ignored.

The @RecordUse() annotation is only allowed on declarations within a package's lib/ directory.

Example

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:

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.');
      }
    }
  });
}

Classes

BoolConstant
A constant boolean value.
CallReference
A reference to a call to some Definition.
CallTearoff
A reference to a tear-off use of the Definition. This means that we can't record the arguments possibly passed to the method somewhere else.
CallWithArguments
A reference to a call to some Definition with positionalArguments and namedArguments.
Class
A Dart class.
Constant
A constant value that can be recorded and serialized.
Constructor
A Dart constructor.
ConstructorTearoffReference
A reference to a tear-off of a generative constructor on definition.
Definition
A unique identifier for a code element, such as a Class or Method within a Dart program.
DefinitionWithInstances
A Definition for which instances or constant values can be recorded in Recordings.instances.
DefinitionWithMembers
A Definition that can contain other Members.
DefinitionWithStaticCalls
A Definition that can be recorded as a static call in Recordings.calls.
DoubleConstant
A constant double value.
Enum
A Dart enum.
EnumConstant
A constant enum value.
Extension
A Dart extension.
ExtensionType
A Dart extension type.
Getter
A Dart getter.
InstanceConstant
A constant instance of a class with its fields.
InstanceConstantReference
A reference to a constant instance of a class or enum element.
InstanceCreationReference
Recorded for generative constructor invocations (non-const).
InstanceReference
A reference to an instance usage of a DefinitionWithInstances (final class or enum).
IntConstant
A constant integer value.
Library
A Dart library.
ListConstant
A constant list of Constant values.
LoadingUnit
A loading unit in which a usage of a Definition was recorded.
MapConstant
A constant map from Constant keys to Constant values.
MaybeConstant
A value recorded during compilation.
Member
A member definition.
Method
A Dart method.
Mixin
A Dart mixin.
NonConstant
A value that is not a constant.
NullConstant
The null constant value.
Operator
A Dart operator.
RecordConstant
A constant record value.
Recordings
Holds all information recorded during compilation.
ScopeWithMembers
A Library or DefinitionWithMembers which can contain Members.
SetConstant
A constant set of Constant values.
Setter
A Dart setter.
StringConstant
A constant string value.
SymbolConstant
A constant symbol value.
UnsupportedConstant
A constant value in Dart but not supported in package:record_use.