| // Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:_fe_analyzer_shared/src/macros/executor/serialization_extensions.dart'; |
| import 'package:dart_internal/extract_type_arguments.dart'; |
| import 'package:meta/meta.dart'; |
| |
| import 'api.dart'; |
| // ignore: unused_import |
| import 'bootstrap.dart'; // For doc comments only. |
| import 'executor/introspection_impls.dart'; |
| import 'executor/serialization.dart'; |
| |
| part 'executor/arguments.dart'; |
| |
| /// The interface used by Dart language implementations, in order to load |
| /// and execute macros, as well as produce library augmentations from those |
| /// macro applications. |
| /// |
| /// This class more clearly defines the role of a Dart language implementation |
| /// during macro discovery and expansion, and unifies how augmentation libraries |
| /// are produced. |
| abstract class MacroExecutor { |
| /// Creates an instance of the macro [name] from [library] in the executor, |
| /// and returns an identifier for that instance. |
| /// |
| /// Throws an exception if an instance is not created. |
| /// |
| /// Instances may be re-used throughout a single build, but should be |
| /// re-created on subsequent builds (even incremental ones). |
| Future<MacroInstanceIdentifier> instantiateMacro( |
| Uri library, String name, String constructor, Arguments arguments); |
| |
| /// Runs the type phase for [macro] on a given [declaration]. |
| /// |
| /// Throws an exception if there is an error executing the macro. |
| Future<MacroExecutionResult> executeTypesPhase(MacroInstanceIdentifier macro, |
| covariant Declaration declaration, IdentifierResolver identifierResolver); |
| |
| /// Runs the declarations phase for [macro] on a given [declaration]. |
| /// |
| /// Throws an exception if there is an error executing the macro. |
| Future<MacroExecutionResult> executeDeclarationsPhase( |
| MacroInstanceIdentifier macro, |
| covariant Declaration declaration, |
| IdentifierResolver identifierResolver, |
| TypeDeclarationResolver typeDeclarationResolver, |
| TypeResolver typeResolver, |
| TypeIntrospector typeIntrospector); |
| |
| /// Runs the definitions phase for [macro] on a given [declaration]. |
| /// |
| /// Throws an exception if there is an error executing the macro. |
| Future<MacroExecutionResult> executeDefinitionsPhase( |
| MacroInstanceIdentifier macro, |
| covariant Declaration declaration, |
| IdentifierResolver identifierResolver, |
| TypeDeclarationResolver typeDeclarationResolver, |
| TypeResolver typeResolver, |
| TypeIntrospector typeIntrospector, |
| TypeInferrer typeInferrer, |
| LibraryDeclarationsResolver libraryDeclarationsResolver); |
| |
| /// Combines multiple [MacroExecutionResult]s into a single library |
| /// augmentation file, and returns a [String] representing that file. |
| /// |
| /// The [resolveDeclaration] argument should return the [TypeDeclaration] for |
| /// an [Identifier] pointing at a named type in the library being augmented |
| /// (note this could be a type that was added in the "types" phase). |
| /// |
| /// The [resolveIdentifier] argument should return the import uri to be used |
| /// for that identifier. |
| /// |
| /// The [inferOmittedType] argument is used to get the inferred type for a |
| /// given [OmittedTypeAnnotation]. |
| /// |
| /// If [omittedTypes] is provided, [inferOmittedType] is allowed to return |
| /// `null` for types that have not yet been inferred. In this case a fresh |
| /// name will be used for the omitted type in the generated library code and |
| /// the omitted type will be mapped to the fresh name in [omittedTypes]. |
| /// |
| /// The generated library files content must be deterministic, including the |
| /// generation of fresh names for import prefixes and omitted types. |
| String buildAugmentationLibrary( |
| Iterable<MacroExecutionResult> macroResults, |
| TypeDeclaration Function(Identifier) resolveDeclaration, |
| ResolvedIdentifier Function(Identifier) resolveIdentifier, |
| TypeAnnotation? Function(OmittedTypeAnnotation) inferOmittedType, |
| {Map<OmittedTypeAnnotation, String>? omittedTypes}); |
| |
| /// Tell the executor to shut down and clean up any resources it may have |
| /// allocated. |
| Future<void> close(); |
| } |
| |
| /// A resolved [Identifier], this is used when creating augmentation libraries |
| /// to qualify identifiers where needed. |
| class ResolvedIdentifier implements Identifier { |
| /// The import URI for the library that defines the member that is referenced |
| /// by this identifier. |
| /// |
| /// If this identifier is an instance member or a built-in type, like |
| /// `void`, [uri] is `null`. |
| final Uri? uri; |
| |
| /// Type of identifier this is (instance, static, top level). |
| final IdentifierKind kind; |
| |
| /// The unqualified name of this identifier. |
| @override |
| final String name; |
| |
| /// If this is a static member, then the name of the fully qualified scope |
| /// surrounding this member. Should not contain a trailing `.`. |
| /// |
| /// Typically this would just be the name of a type. |
| final String? staticScope; |
| |
| ResolvedIdentifier({ |
| required this.kind, |
| required this.name, |
| required this.staticScope, |
| required this.uri, |
| }); |
| } |
| |
| /// The types of identifiers. |
| enum IdentifierKind { |
| instanceMember, |
| local, // Parameters, local variables, etc. |
| staticInstanceMember, |
| topLevelMember, |
| } |
| |
| /// An opaque identifier for an instance of a macro class, retrieved by |
| /// [MacroExecutor.instantiateMacro]. |
| /// |
| /// Used to execute or reload this macro in the future. |
| abstract class MacroInstanceIdentifier implements Serializable { |
| /// Whether or not this instance should run in [phase] on [declarationKind]. |
| /// |
| /// Attempting to execute a macro in a phase it doesn't support, or on a |
| /// declaration kind it doesn't support is an error. |
| bool shouldExecute(DeclarationKind declarationKind, Phase phase); |
| |
| /// Whether or not this macro supports [declarationKind] in any phase. |
| bool supportsDeclarationKind(DeclarationKind declarationKind); |
| } |
| |
| /// A summary of the results of running a macro in a given phase. |
| /// |
| /// All modifications are expressed in terms of library augmentation |
| /// declarations. |
| abstract class MacroExecutionResult implements Serializable { |
| /// Any augmentations to enum values that should be applied to an enum as a |
| /// result of executing a macro, indexed by the identifier of the enum. |
| Map<Identifier, Iterable<DeclarationCode>> get enumValueAugmentations; |
| |
| /// Any augmentations that should be applied to the library as a result of |
| /// executing a macro. |
| Iterable<DeclarationCode> get libraryAugmentations; |
| |
| /// The names of any new types declared in [augmentations]. |
| Iterable<String> get newTypeNames; |
| |
| /// Any augmentations that should be applied to a class as a result of |
| /// executing a macro, indexed by the identifier of the class. |
| Map<Identifier, Iterable<DeclarationCode>> get typeAugmentations; |
| } |
| |
| /// Each of the possible types of declarations a macro can be applied to |
| enum DeclarationKind { |
| classType, |
| constructor, |
| enumType, |
| enumValue, |
| field, |
| function, |
| method, |
| mixinType, |
| variable, |
| } |
| |
| /// Each of the different macro execution phases. |
| enum Phase { |
| /// Only new types are added in this phase. |
| types, |
| |
| /// New non-type declarations are added in this phase. |
| declarations, |
| |
| /// This phase allows augmenting existing declarations. |
| definitions, |
| } |