// 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 '../api.dart'; import '../executor.dart'; import 'exception_impls.dart'; import 'introspection_impls.dart'; import 'response_impls.dart'; abstract class TypeBuilderBase implements TypePhaseIntrospector, Builder { /// All the collected diagnostics for this builder. final List _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> _enumValueAugmentations; /// All the extends clauses to be added, indexed by the identifier for the /// augmented type declaration. final Map _extendsTypeAugmentations; /// All the interfaces to be added, indexed by the identifier for the /// augmented type declaration. final Map> _interfaceAugmentations; /// All the top level declarations to add to the current library. final List _libraryAugmentations; /// All the mixins to be added, indexed by the identifier for the /// augmented type declaration. final Map> _mixinAugmentations; /// The names of any new types added in [_libraryAugmentations]. final List _newTypeNames = []; /// All the declarations to be added to types, indexed by the identifier for /// the augmented type. final Map> _typeAugmentations; TypePhaseIntrospector get introspector; /// Creates and returns a [MacroExecutionResult] out of the [_augmentations] /// created by this builder. MacroExecutionResult get result => MacroExecutionResultImpl( diagnostics: _diagnostics, exception: _exception, enumValueAugmentations: _enumValueAugmentations, extendsTypeAugmentations: _extendsTypeAugmentations, interfaceAugmentations: _interfaceAugmentations, libraryAugmentations: _libraryAugmentations, mixinAugmentations: _mixinAugmentations, newTypeNames: _newTypeNames, typeAugmentations: _typeAugmentations, ); TypeBuilderBase() : _diagnostics = [], _enumValueAugmentations = {}, _extendsTypeAugmentations = {}, _interfaceAugmentations = {}, _libraryAugmentations = [], _mixinAugmentations = {}, _typeAugmentations = {}; TypeBuilderBase.nested({ Map>? parentEnumValueAugmentations, Map? parentExtendsTypeAugmentations, Map>? parentInterfaceAugmentations, List? parentLibraryAugmentations, Map>? parentMixinAugmentations, Map>? parentTypeAugmentations, List? parentDiagnostics, }) : _diagnostics = parentDiagnostics ?? [], _enumValueAugmentations = parentEnumValueAugmentations ?? {}, _extendsTypeAugmentations = parentExtendsTypeAugmentations ?? {}, _interfaceAugmentations = parentInterfaceAugmentations ?? {}, _libraryAugmentations = parentLibraryAugmentations ?? [], _mixinAugmentations = parentMixinAugmentations ?? {}, _typeAugmentations = parentTypeAugmentations ?? {}; @override void report(Diagnostic diagnostic) => _diagnostics.add(diagnostic); void failWithException(MacroExceptionImpl exception) { if (_exception != null) throw StateError('Already set exception'); _exception = exception; } @override Future 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 ExtendsTypeBuilderImpl on TypeBuilderImpl implements ExtendsTypeBuilder { /// The type that we are going to be adding an extends clause. IdentifierImpl get originalType; /// Sets the `extends` clause to [superclass]. /// /// The type must not already have an `extends` clause. @override void extendsType(NamedTypeAnnotationCode superclass) { if (_extendsTypeAugmentations.containsKey(originalType)) { throw ArgumentError.value( originalType.name, null, 'A type cannot extend multiple types'); } _extendsTypeAugmentations[originalType] = superclass; } } 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 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 mixins) { (_mixinAugmentations[originalType] ??= []).addAll(mixins); } } class ClassTypeBuilderImpl extends TypeBuilderImpl with ExtendsTypeBuilderImpl, 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(); DeclarationBuilderBase.nested({ required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentTypeAugmentations, required super.parentMixinAugmentations, }) : super.nested(); @override Future typeDeclarationOf(IdentifierImpl identifier) => introspector.typeDeclarationOf(identifier); @override Future> constructorsOf(TypeDeclaration type) => introspector.constructorsOf(type); @override Future> valuesOf( covariant EnumDeclaration enuum) => introspector.valuesOf(enuum); @override Future> fieldsOf(TypeDeclaration type) => introspector.fieldsOf(type); @override Future> methodsOf(TypeDeclaration type) => introspector.methodsOf(type); @override Future resolve(TypeAnnotationCode code) => introspector.resolve(code); @override Future> 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); DefinitionBuilderBase.nested( this.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentTypeAugmentations, required super.parentMixinAugmentations, }) : super.nested(); @override Future declarationOf(Identifier identifier) => introspector.declarationOf(identifier); @override Future inferType(OmittedTypeAnnotationImpl omittedType) => introspector.inferType(omittedType); @override Future> topLevelDeclarationsOf(Library library) => introspector.topLevelDeclarationsOf(library); @override Future 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); TypeDefinitionBuilderImpl.nested( this.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentTypeAugmentations, required super.parentMixinAugmentations, }) : super.nested(); @override Future buildConstructor( Identifier identifier) async { ConstructorDeclarationImpl constructor = (await introspector .constructorsOf(declaration)) .firstWhere((constructor) => constructor.identifier == identifier) as ConstructorDeclarationImpl; return ConstructorDefinitionBuilderImpl.nested(constructor, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations); } @override Future buildField(Identifier identifier) async { FieldDeclaration field = (await introspector.fieldsOf(declaration)) .firstWhere((field) => field.identifier == identifier); return VariableDefinitionBuilderImpl.nested(field, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations); } @override Future buildMethod(Identifier identifier) async { MethodDeclarationImpl method = (await introspector.methodsOf(declaration)) .firstWhere((method) => method.identifier == identifier) as MethodDeclarationImpl; return FunctionDefinitionBuilderImpl.nested(method, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations); } } class EnumDefinitionBuilderImpl extends TypeDefinitionBuilderImpl implements EnumDefinitionBuilder { @override EnumDeclaration get declaration => super.declaration as EnumDeclaration; EnumDefinitionBuilderImpl( EnumDeclaration super.declaration, super.introspector); EnumDefinitionBuilderImpl.nested( EnumDeclaration super.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @override Future buildEnumValue( Identifier identifier) async { EnumValueDeclarationImpl entry = (await introspector.valuesOf(declaration)) .firstWhere((entry) => entry.identifier == identifier) as EnumValueDeclarationImpl; return EnumValueDefinitionBuilderImpl.nested( entry, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentLibraryAugmentations: _libraryAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, ); } } class EnumValueDefinitionBuilderImpl extends DefinitionBuilderBase implements EnumValueDefinitionBuilder { final EnumValueDeclarationImpl declaration; EnumValueDefinitionBuilderImpl(this.declaration, super.introspector); EnumValueDefinitionBuilderImpl.nested( this.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @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); FunctionDefinitionBuilderImpl.nested( this.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @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); ConstructorDefinitionBuilderImpl.nested( this.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @override void augment( {FunctionBodyCode? body, List? initializers, CommentCode? docComments}) { 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); VariableDefinitionBuilderImpl.nested( this.declaration, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @override void augment( {DeclarationCode? getter, DeclarationCode? setter, ExpressionCode? initializer, CommentCode? initializerDocComments}) { List 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); LibraryDefinitionBuilderImpl.nested( this.library, super.introspector, { required super.parentDiagnostics, required super.parentEnumValueAugmentations, required super.parentExtendsTypeAugmentations, required super.parentInterfaceAugmentations, required super.parentLibraryAugmentations, required super.parentMixinAugmentations, required super.parentTypeAugmentations, }) : super.nested(); @override Future buildFunction(Identifier identifier) async { FunctionDeclarationImpl function = (await introspector .topLevelDeclarationsOf(library)) .firstWhere((declaration) => declaration.identifier == identifier) as FunctionDeclarationImpl; return FunctionDefinitionBuilderImpl.nested( function, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations, ); } @override Future buildType(Identifier identifier) async { TypeDeclaration type = (await introspector.topLevelDeclarationsOf(library)) .firstWhere((declaration) => declaration.identifier == identifier) as TypeDeclaration; return TypeDefinitionBuilderImpl.nested(type, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations); } @override Future buildVariable(Identifier identifier) async { VariableDeclarationImpl variable = (await introspector .topLevelDeclarationsOf(library)) .firstWhere((declaration) => declaration.identifier == identifier) as VariableDeclarationImpl; return VariableDefinitionBuilderImpl.nested(variable, introspector, parentDiagnostics: _diagnostics, parentEnumValueAugmentations: _enumValueAugmentations, parentExtendsTypeAugmentations: _extendsTypeAugmentations, parentInterfaceAugmentations: _interfaceAugmentations, parentMixinAugmentations: _mixinAugmentations, parentTypeAugmentations: _typeAugmentations, parentLibraryAugmentations: _libraryAugmentations); } } /// Builds all the possible augmentations for a variable. List _buildVariableAugmentations( VariableDeclaration declaration, {DeclarationCode? getter, DeclarationCode? setter, ExpressionCode? initializer, CommentCode? initializerDocComments}) { if (initializerDocComments != null && initializer == null) { throw ArgumentError( 'initializerDocComments cannot be provided if an initializer is not ' 'provided.'); } List augmentations = []; if (getter != null) { augmentations.add(DeclarationCode.fromParts([ if (declaration is FieldDeclaration) ' ', 'augment ', if (declaration is FieldDeclaration && declaration.hasStatic) 'static ', getter, ])); } if (setter != null) { augmentations.add(DeclarationCode.fromParts([ if (declaration is FieldDeclaration) ' ', 'augment ', if (declaration is FieldDeclaration && declaration.hasStatic) 'static ', setter, ])); } if (initializer != null) { augmentations.add(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? initializers, CommentCode? docComments}) { assert(initializers == null || declaration is ConstructorDeclaration); return DeclarationCode.fromParts([ if (docComments != null) ...[docComments, '\n'], if (declaration is MethodDeclaration) ' ', 'augment ', if (declaration is ConstructorDeclaration) ...[ if (declaration.isConst) 'const ', if (declaration.isFactory) 'factory ', 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) ...[ '\n : ', initializers.first, for (Code initializer in initializers.skip(1)) ...[ ',\n ', initializer, ], ], if (body == null) ';' else ...[ ' ', body, ] ]); }