blob: b1d60302a15e303567e859fa130ed259c5485912 [file] [log] [blame]
// 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 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart';
import '../api.dart';
import '../executor.dart';
import 'exception_impls.dart';
import 'response_impls.dart';
abstract class TypeBuilderBase implements TypePhaseIntrospector, Builder {
/// All the collected diagnostics for this builder.
final List<Diagnostic> _diagnostics = [];
/// If execution was stopped by an exception, the exception.
MacroExceptionImpl? _exception;
/// All the enum values to be added, indexed by the identifier for the
/// augmented enum declaration.
final Map<IdentifierImpl, List<DeclarationCode>> _enumValueAugmentations;
/// All the interfaces to be added, indexed by the identifier for the
/// augmented type declaration.
final Map<IdentifierImpl, List<TypeAnnotationCode>> _interfaceAugmentations;
/// All the top level declarations to add to the current library.
final List<DeclarationCode> _libraryAugmentations;
/// All the mixins to be added, indexed by the identifier for the
/// augmented type declaration.
final Map<IdentifierImpl, List<TypeAnnotationCode>> _mixinAugmentations;
/// The names of any new types added in [_libraryAugmentations].
final List<String> _newTypeNames = [];
/// All the declarations to be added to types, indexed by the identifier for
/// the augmented type.
final Map<IdentifierImpl, List<DeclarationCode>> _typeAugmentations;
TypePhaseIntrospector get introspector;
/// Creates and returns a [MacroExecutionResult] out of the [_augmentations]
/// created by this builder.
MacroExecutionResult get result => new MacroExecutionResultImpl(
diagnostics: _diagnostics,
exception: _exception,
enumValueAugmentations: _enumValueAugmentations,
interfaceAugmentations: _interfaceAugmentations,
libraryAugmentations: _libraryAugmentations,
mixinAugmentations: _mixinAugmentations,
newTypeNames: _newTypeNames,
typeAugmentations: _typeAugmentations,
);
TypeBuilderBase({
Map<IdentifierImpl, List<DeclarationCode>>? parentEnumValueAugmentations,
Map<IdentifierImpl, List<TypeAnnotationCode>>? parentInterfaceAugmentations,
List<DeclarationCode>? parentLibraryAugmentations,
Map<IdentifierImpl, List<TypeAnnotationCode>>? parentMixinAugmentations,
Map<IdentifierImpl, List<DeclarationCode>>? parentTypeAugmentations,
}) : _enumValueAugmentations = parentEnumValueAugmentations ?? {},
_interfaceAugmentations = parentInterfaceAugmentations ?? {},
_libraryAugmentations = parentLibraryAugmentations ?? [],
_mixinAugmentations = parentMixinAugmentations ?? {},
_typeAugmentations = parentTypeAugmentations ?? {};
@override
void report(Diagnostic diagnostic) => _diagnostics.add(diagnostic);
void failWithException(MacroExceptionImpl exception) {
if (_exception != null) throw new StateError('Already set exception');
_exception = exception;
}
@override
Future<Identifier> resolveIdentifier(Uri library, String identifier) =>
// ignore: deprecated_member_use_from_same_package
introspector.resolveIdentifier(library, identifier);
}
class TypeBuilderImpl extends TypeBuilderBase implements TypeBuilder {
@override
final TypePhaseIntrospector introspector;
TypeBuilderImpl(this.introspector);
@override
void declareType(String name, DeclarationCode typeDeclaration) {
_newTypeNames.add(name);
_libraryAugmentations.add(typeDeclaration);
}
}
mixin InterfaceTypesBuilderImpl on TypeBuilderImpl
implements InterfaceTypesBuilder {
/// The type that we are going to be adding interfaces to.
IdentifierImpl get originalType;
/// Appends [interfaces] to the list of interfaces for this type.
@override
void appendInterfaces(Iterable<TypeAnnotationCode> interfaces) {
_interfaceAugmentations
.putIfAbsent(originalType, () => [])
.addAll(interfaces);
}
}
mixin MixinTypesBuilderImpl on TypeBuilderImpl implements MixinTypesBuilder {
/// The type that we are going to be adding mixins to.
IdentifierImpl get originalType;
/// Appends [mixins] to the list of mixins for this type.
@override
void appendMixins(Iterable<TypeAnnotationCode> mixins) {
(_mixinAugmentations[originalType] ??= []).addAll(mixins);
}
}
class ClassTypeBuilderImpl extends TypeBuilderImpl
with InterfaceTypesBuilderImpl, MixinTypesBuilderImpl
implements ClassTypeBuilder {
@override
final IdentifierImpl originalType;
ClassTypeBuilderImpl(this.originalType, super.introspector);
}
class EnumTypeBuilderImpl extends TypeBuilderImpl
with InterfaceTypesBuilderImpl, MixinTypesBuilderImpl
implements EnumTypeBuilder {
@override
final IdentifierImpl originalType;
EnumTypeBuilderImpl(this.originalType, super.introspector);
}
class MixinTypeBuilderImpl extends TypeBuilderImpl
with InterfaceTypesBuilderImpl
implements MixinTypeBuilder {
@override
final IdentifierImpl originalType;
MixinTypeBuilderImpl(this.originalType, super.introspector);
}
/// Base class for all [DeclarationBuilder]s.
abstract class DeclarationBuilderBase extends TypeBuilderBase
implements DeclarationPhaseIntrospector {
@override
DeclarationPhaseIntrospector get introspector;
DeclarationBuilderBase({
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentTypeAugmentations,
super.parentMixinAugmentations,
});
@override
Future<TypeDeclaration> typeDeclarationOf(IdentifierImpl identifier) =>
introspector.typeDeclarationOf(identifier);
@override
Future<List<ConstructorDeclaration>> constructorsOf(TypeDeclaration type) =>
introspector.constructorsOf(type);
@override
Future<List<EnumValueDeclaration>> valuesOf(
covariant EnumDeclaration enuum) =>
introspector.valuesOf(enuum);
@override
Future<List<FieldDeclaration>> fieldsOf(TypeDeclaration type) =>
introspector.fieldsOf(type);
@override
Future<List<MethodDeclaration>> methodsOf(TypeDeclaration type) =>
introspector.methodsOf(type);
@override
Future<StaticType> resolve(TypeAnnotationCode code) =>
introspector.resolve(code);
@override
Future<List<TypeDeclaration>> typesOf(Library library) =>
introspector.typesOf(library);
}
class DeclarationBuilderImpl extends DeclarationBuilderBase
implements DeclarationBuilder {
@override
final DeclarationPhaseIntrospector introspector;
DeclarationBuilderImpl(this.introspector);
@override
void declareInLibrary(DeclarationCode declaration) {
_libraryAugmentations.add(declaration);
}
}
class MemberDeclarationBuilderImpl extends DeclarationBuilderImpl
implements MemberDeclarationBuilder {
final IdentifierImpl definingType;
MemberDeclarationBuilderImpl(
this.definingType,
super.introspector,
);
@override
void declareInType(DeclarationCode declaration) {
_typeAugmentations.update(definingType, (value) => value..add(declaration),
ifAbsent: () => [declaration]);
}
}
class EnumDeclarationBuilderImpl extends MemberDeclarationBuilderImpl
implements EnumDeclarationBuilder {
EnumDeclarationBuilderImpl(
super.definingType,
super.introspector,
);
@override
void declareEnumValue(DeclarationCode declaration) {
_enumValueAugmentations.update(
definingType, (value) => value..add(declaration),
ifAbsent: () => [declaration]);
}
}
/// Base class for all [DefinitionBuilder]s.
class DefinitionBuilderBase extends DeclarationBuilderBase
implements DefinitionPhaseIntrospector {
@override
final DefinitionPhaseIntrospector introspector;
DefinitionBuilderBase(
this.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentTypeAugmentations,
super.parentMixinAugmentations,
});
@override
Future<Declaration> declarationOf(Identifier identifier) =>
introspector.declarationOf(identifier);
@override
Future<TypeAnnotation> inferType(OmittedTypeAnnotationImpl omittedType) =>
introspector.inferType(omittedType);
@override
Future<List<Declaration>> topLevelDeclarationsOf(Library library) =>
introspector.topLevelDeclarationsOf(library);
@override
Future<TypeDeclaration> typeDeclarationOf(Identifier identifier) =>
introspector.typeDeclarationOf(identifier);
}
class TypeDefinitionBuilderImpl extends DefinitionBuilderBase
implements TypeDefinitionBuilder {
/// The declaration this is a builder for.
final TypeDeclaration declaration;
TypeDefinitionBuilderImpl(
this.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentTypeAugmentations,
super.parentMixinAugmentations,
});
@override
Future<ConstructorDefinitionBuilder> buildConstructor(
Identifier identifier) async {
ConstructorDeclarationImpl constructor = (await introspector
.constructorsOf(declaration))
.firstWhere((constructor) => constructor.identifier == identifier)
as ConstructorDeclarationImpl;
return new ConstructorDefinitionBuilderImpl(constructor, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<VariableDefinitionBuilder> buildField(Identifier identifier) async {
FieldDeclaration field = (await introspector.fieldsOf(declaration))
.firstWhere((field) => field.identifier == identifier);
return new VariableDefinitionBuilderImpl(field, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<FunctionDefinitionBuilder> buildMethod(Identifier identifier) async {
MethodDeclarationImpl method = (await introspector.methodsOf(declaration))
.firstWhere((method) => method.identifier == identifier)
as MethodDeclarationImpl;
return new FunctionDefinitionBuilderImpl(method, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
}
class EnumDefinitionBuilderImpl extends TypeDefinitionBuilderImpl
implements EnumDefinitionBuilder {
@override
EnumDeclaration get declaration => super.declaration as EnumDeclaration;
EnumDefinitionBuilderImpl(
EnumDeclaration super.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentTypeAugmentations,
super.parentMixinAugmentations,
});
@override
Future<EnumValueDefinitionBuilder> buildEnumValue(
Identifier identifier) async {
EnumValueDeclarationImpl entry = (await introspector.valuesOf(declaration))
.firstWhere((entry) => entry.identifier == identifier)
as EnumValueDeclarationImpl;
return new EnumValueDefinitionBuilderImpl(
entry,
introspector,
parentEnumValueAugmentations: _enumValueAugmentations,
parentInterfaceAugmentations: _interfaceAugmentations,
parentLibraryAugmentations: _libraryAugmentations,
parentMixinAugmentations: _mixinAugmentations,
parentTypeAugmentations: _typeAugmentations,
);
}
}
class EnumValueDefinitionBuilderImpl extends DefinitionBuilderBase
implements EnumValueDefinitionBuilder {
final EnumValueDeclarationImpl declaration;
EnumValueDefinitionBuilderImpl(
this.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentMixinAugmentations,
super.parentTypeAugmentations,
});
@override
void augment(DeclarationCode entry) {
_enumValueAugmentations.update(
declaration.definingEnum, (value) => value..add(entry),
ifAbsent: () => [entry]);
}
}
/// Implementation of [FunctionDefinitionBuilder].
class FunctionDefinitionBuilderImpl extends DefinitionBuilderBase
implements FunctionDefinitionBuilder {
final FunctionDeclarationImpl declaration;
FunctionDefinitionBuilderImpl(
this.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentMixinAugmentations,
super.parentTypeAugmentations,
});
@override
void augment(FunctionBodyCode body, {CommentCode? docComments}) {
DeclarationCode augmentation =
_buildFunctionAugmentation(body, declaration, docComments: docComments);
if (declaration is MemberDeclaration) {
_typeAugmentations.update(
(declaration as MethodDeclarationImpl).definingType,
(value) => value..add(augmentation),
ifAbsent: () => [augmentation]);
} else {
_libraryAugmentations.add(augmentation);
}
}
}
class ConstructorDefinitionBuilderImpl extends DefinitionBuilderBase
implements ConstructorDefinitionBuilder {
final ConstructorDeclarationImpl declaration;
ConstructorDefinitionBuilderImpl(
this.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentMixinAugmentations,
super.parentTypeAugmentations,
});
@override
void augment(
{FunctionBodyCode? body,
List<Code>? initializers,
CommentCode? docComments}) {
if (body != null && declaration.hasBody) {
// TODO: https://github.com/dart-lang/language/issues/3555
throw new UnsupportedError(
'Augmenting existing constructor bodies is not allowed.');
}
body ??= new FunctionBodyCode.fromString('{}');
DeclarationCode augmentation = _buildFunctionAugmentation(body, declaration,
initializers: initializers, docComments: docComments);
_typeAugmentations.update(
declaration.definingType, (value) => value..add(augmentation),
ifAbsent: () => [augmentation]);
}
}
class VariableDefinitionBuilderImpl extends DefinitionBuilderBase
implements VariableDefinitionBuilder {
final VariableDeclaration declaration;
VariableDefinitionBuilderImpl(
this.declaration,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentMixinAugmentations,
super.parentTypeAugmentations,
});
@override
void augment(
{DeclarationCode? getter,
DeclarationCode? setter,
ExpressionCode? initializer,
CommentCode? initializerDocComments}) {
List<DeclarationCode> augmentations = _buildVariableAugmentations(
declaration,
getter: getter,
setter: setter,
initializer: initializer,
initializerDocComments: initializerDocComments);
if (declaration is MemberDeclaration) {
_typeAugmentations.update(
(declaration as FieldDeclarationImpl).definingType,
(value) => value..addAll(augmentations),
ifAbsent: () => augmentations);
} else {
_libraryAugmentations.addAll(augmentations);
}
}
}
class LibraryDefinitionBuilderImpl extends DefinitionBuilderBase
implements LibraryDefinitionBuilder {
final Library library;
LibraryDefinitionBuilderImpl(
this.library,
super.introspector, {
super.parentEnumValueAugmentations,
super.parentInterfaceAugmentations,
super.parentLibraryAugmentations,
super.parentMixinAugmentations,
super.parentTypeAugmentations,
});
@override
Future<FunctionDefinitionBuilder> buildFunction(Identifier identifier) async {
FunctionDeclarationImpl function = (await introspector
.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as FunctionDeclarationImpl;
return new FunctionDefinitionBuilderImpl(function, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<TypeDefinitionBuilder> buildType(Identifier identifier) async {
TypeDeclaration type = (await introspector.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as TypeDeclaration;
return new TypeDefinitionBuilderImpl(type, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
@override
Future<VariableDefinitionBuilder> buildVariable(Identifier identifier) async {
VariableDeclarationImpl variable = (await introspector
.topLevelDeclarationsOf(library))
.firstWhere((declaration) => declaration.identifier == identifier)
as VariableDeclarationImpl;
return new VariableDefinitionBuilderImpl(variable, introspector,
parentTypeAugmentations: _typeAugmentations,
parentLibraryAugmentations: _libraryAugmentations);
}
}
/// Builds all the possible augmentations for a variable.
List<DeclarationCode> _buildVariableAugmentations(
VariableDeclaration declaration,
{DeclarationCode? getter,
DeclarationCode? setter,
ExpressionCode? initializer,
CommentCode? initializerDocComments}) {
if (initializerDocComments != null && initializer == null) {
throw new ArgumentError(
'initializerDocComments cannot be provided if an initializer is not '
'provided.');
}
List<DeclarationCode> augmentations = [];
if (getter != null) {
augmentations.add(new DeclarationCode.fromParts([
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
getter,
]));
}
if (setter != null) {
augmentations.add(new DeclarationCode.fromParts([
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
setter,
]));
}
if (initializer != null) {
augmentations.add(new DeclarationCode.fromParts([
if (initializerDocComments != null) initializerDocComments,
if (declaration is FieldDeclaration) ' ',
'augment ',
if (declaration is FieldDeclaration && declaration.hasStatic) 'static ',
if (declaration.hasFinal) 'final ',
declaration.type.code,
' ',
declaration.identifier.name,
' = ',
initializer,
';',
]));
}
return augmentations;
}
/// Builds the code to augment a function, method, or constructor with a new
/// body.
///
/// The [initializers] parameter can only be used if [declaration] is a
/// constructor.
DeclarationCode _buildFunctionAugmentation(
FunctionBodyCode body, FunctionDeclaration declaration,
{List<Code>? initializers, CommentCode? docComments}) {
assert(initializers == null || declaration is ConstructorDeclaration);
return new DeclarationCode.fromParts([
if (docComments != null) docComments,
if (declaration is MethodDeclaration) ' ',
'augment ',
if (declaration is ConstructorDeclaration) ...[
declaration.definingType.name,
if (declaration.identifier.name.isNotEmpty) '.',
] else ...[
if (declaration is MethodDeclaration && declaration.hasStatic) 'static ',
declaration.returnType.code,
' ',
if (declaration.isOperator) 'operator ',
],
if (declaration.isGetter) 'get ',
if (declaration.isSetter) 'set ',
declaration.identifier.name,
if (!declaration.isGetter) ...[
if (declaration.typeParameters.isNotEmpty) ...[
'<',
for (TypeParameterDeclaration typeParam
in declaration.typeParameters) ...[
typeParam.identifier.name,
if (typeParam.bound != null) ...[' extends ', typeParam.bound!.code],
if (typeParam != declaration.typeParameters.last) ', ',
],
'>',
],
'(',
for (FormalParameterDeclaration positionalRequired in declaration
.positionalParameters
.takeWhile((p) => p.isRequired)) ...[
positionalRequired.code,
', ',
],
if (declaration.positionalParameters.any((p) => !p.isRequired)) ...[
'[',
for (FormalParameterDeclaration positionalOptional in declaration
.positionalParameters
.where((p) => !p.isRequired)) ...[
positionalOptional.code,
', ',
],
']',
],
if (declaration.namedParameters.isNotEmpty) ...[
'{',
for (FormalParameterDeclaration named
in declaration.namedParameters) ...[
named.code,
', ',
],
'}',
],
')',
],
' ',
if (initializers != null && initializers.isNotEmpty) ...[
' : ',
initializers.first,
for (Code initializer in initializers.skip(1)) ...[
',\n',
initializer,
],
],
body,
]);
}