From 98f645f7c40c95366dbf46f3d175593c7eb04d6f Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Thu, 10 Oct 2024 20:43:51 +0000 Subject: [PATCH] add specific macro implementations and delegate to them --- pkgs/_macro_client/lib/macro_client.dart | 16 +- pkgs/_macro_client/lib/src/execute_macro.dart | 97 ++++++ pkgs/_test_macros/lib/declare_x_macro.dart | 5 +- pkgs/_test_macros/lib/json_codable.dart | 18 +- pkgs/_test_macros/lib/query_class.dart | 9 +- pkgs/macro/lib/macro.dart | 290 +++++++++++++++++- 6 files changed, 403 insertions(+), 32 deletions(-) create mode 100644 pkgs/_macro_client/lib/src/execute_macro.dart diff --git a/pkgs/_macro_client/lib/macro_client.dart b/pkgs/_macro_client/lib/macro_client.dart index d54b9f6c..67a03050 100644 --- a/pkgs/_macro_client/lib/macro_client.dart +++ b/pkgs/_macro_client/lib/macro_client.dart @@ -9,6 +9,8 @@ import 'package:dart_model/dart_model.dart'; import 'package:macro/macro.dart'; import 'package:macro_service/macro_service.dart'; +import 'src/execute_macro.dart'; + /// Local macro client which runs macros as directed by requests from a remote /// macro host. /// @@ -109,9 +111,17 @@ class MacroClient { error: 'No macro for annotation: ' '${hostRequest.macroAnnotation.asString}'))); } else { - await Scope.macro.runAsync(() async => _sendResponse( - Response.augmentResponse( - await macro.augment(_host, hostRequest.asAugmentRequest), + final augmentRequest = hostRequest.asAugmentRequest; + await Scope.macro + .runAsync(() async => _sendResponse(Response.augmentResponse( + await switch (augmentRequest.phase) { + 1 => executeTypesMacro(macro, _host, augmentRequest), + 2 => executeDeclarationsMacro(macro, _host, augmentRequest), + 3 => executeDefinitionMacro(macro, _host, augmentRequest), + _ => throw StateError( + 'Unexpected phase ${augmentRequest.phase}, ' + 'expected 1, 2, or 3.') + }, requestId: hostRequest.id))); } default: diff --git a/pkgs/_macro_client/lib/src/execute_macro.dart b/pkgs/_macro_client/lib/src/execute_macro.dart new file mode 100644 index 00000000..5a0d15ed --- /dev/null +++ b/pkgs/_macro_client/lib/src/execute_macro.dart @@ -0,0 +1,97 @@ +// Copyright (c) 2022, 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:dart_model/dart_model.dart'; +import 'package:macro/macro.dart'; +import 'package:macro_service/macro_service.dart'; + +/// Runs [macro] in the types phase and returns a [AugmentResponse]. +Future executeTypesMacro( + Macro macro, Host host, AugmentRequest request) async { + final target = request.target; + final queryResult = await host.query(Query(target: target)); + final properties = + queryResult.uris[target.uri]!.scopes[target.name]!.properties; + switch ((properties, macro)) { + case (Properties(isClass: true), ClassTypesMacro macro): + return await macro.buildTypesForClass(host, request); + case (_, LibraryTypesMacro()): + case (_, ConstructorTypesMacro()): + case (_, MethodTypesMacro()): + case (_, FunctionTypesMacro()): + case (_, FieldTypesMacro()): + case (_, VariableTypesMacro()): + case (_, EnumTypesMacro()): + case (_, ExtensionTypesMacro()): + case (_, ExtensionTypeTypesMacro()): + case (_, MixinTypesMacro()): + case (_, EnumValueTypesMacro()): + case (_, TypeAliasTypesMacro()): + throw UnimplementedError('Unimplemented macro target'); + default: + throw UnsupportedError('Unsupported macro type or invalid target:\n' + 'macro: $macro\ntarget: $target'); + } +} + +/// Runs [macro] in the declaration phase and returns a [AugmentResponse]. +Future executeDeclarationsMacro( + Macro macro, Host host, AugmentRequest request) async { + final target = request.target; + final queryResult = await host.query(Query(target: target)); + final properties = + queryResult.uris[target.uri]!.scopes[target.name]!.properties; + + switch ((properties, macro)) { + case (Properties(isClass: true), ClassDeclarationsMacro macro): + return await macro.buildDeclarationsForClass(host, request); + case (_, LibraryDeclarationsMacro()): + case (_, EnumDeclarationsMacro()): + case (_, ExtensionDeclarationsMacro()): + case (_, ExtensionTypeDeclarationsMacro()): + case (_, MixinDeclarationsMacro()): + case (_, EnumValueDeclarationsMacro()): + case (_, ConstructorDeclarationsMacro()): + case (_, MethodDeclarationsMacro()): + case (_, FieldDeclarationsMacro()): + case (_, FunctionDeclarationsMacro()): + case (_, VariableDeclarationsMacro()): + case (_, TypeAliasDeclarationsMacro()): + throw UnimplementedError('Unimplemented macro target'); + default: + throw UnsupportedError('Unsupported macro type or invalid target:\n' + 'macro: $macro\ntarget: $target'); + } +} + +/// Runs [macro] in the definition phase and returns a [AugmentResponse]. +Future executeDefinitionMacro( + Macro macro, Host host, AugmentRequest request) async { + final target = request.target; + final queryResult = await host.query(Query(target: target)); + final properties = + queryResult.uris[target.uri]!.scopes[target.name]!.properties; + + switch ((properties, macro)) { + case (Properties(isClass: true), ClassDefinitionMacro macro): + return await macro.buildDefinitionForClass(host, request); + case (_, LibraryDefinitionMacro()): + case (_, EnumDefinitionMacro()): + case (_, ExtensionDefinitionMacro()): + case (_, ExtensionTypeDefinitionMacro()): + case (_, MixinDefinitionMacro()): + case (_, EnumValueDefinitionMacro()): + case (_, ConstructorDefinitionMacro()): + case (_, MethodDefinitionMacro()): + case (_, FieldDefinitionMacro()): + case (_, FunctionDefinitionMacro()): + case (_, VariableDefinitionMacro()): + throw UnimplementedError('Unimplemented macro target'); + default: + throw UnsupportedError('Unsupported macro type or invalid target:\n' + 'macro: $macro\ntarget: $target'); + } +} diff --git a/pkgs/_test_macros/lib/declare_x_macro.dart b/pkgs/_test_macros/lib/declare_x_macro.dart index ece158b3..c51d0573 100644 --- a/pkgs/_test_macros/lib/declare_x_macro.dart +++ b/pkgs/_test_macros/lib/declare_x_macro.dart @@ -13,7 +13,7 @@ class DeclareX { const DeclareX(); } -class DeclareXImplementation implements Macro { +class DeclareXImplementation implements ClassDeclarationsMacro { // TODO(davidmorgan): this should be injected by the bootstrap script. @override MacroDescription get description => MacroDescription( @@ -22,7 +22,8 @@ class DeclareXImplementation implements Macro { runsInPhases: [2]); @override - Future augment(Host host, AugmentRequest request) async { + Future buildDeclarationsForClass( + Host host, AugmentRequest request) async { // TODO(davidmorgan): make the host only run in the phases requested so // that this is not needed. if (request.phase != 2) return AugmentResponse(augmentations: []); diff --git a/pkgs/_test_macros/lib/json_codable.dart b/pkgs/_test_macros/lib/json_codable.dart index 3cffb313..b0357727 100644 --- a/pkgs/_test_macros/lib/json_codable.dart +++ b/pkgs/_test_macros/lib/json_codable.dart @@ -18,7 +18,8 @@ final _jsonMapTypeForLiteral = '<{{dart:core#String}}, {{dart:core#Object}}?>'; final _jsonMapType = '{{dart:core#Map}}$_jsonMapTypeForLiteral'; final _mapEntryType = '{{dart:core#MapEntry}}'; -class JsonCodableImplementation implements Macro { +class JsonCodableImplementation + implements ClassDeclarationsMacro, ClassDefinitionMacro { @override MacroDescription get description => MacroDescription( annotation: QualifiedName( @@ -26,15 +27,8 @@ class JsonCodableImplementation implements Macro { runsInPhases: [2, 3]); @override - Future augment(Host host, AugmentRequest request) async { - return switch (request.phase) { - 2 => phase2(host, request), - 3 => phase3(host, request), - _ => AugmentResponse(augmentations: []), - }; - } - - Future phase2(Host host, AugmentRequest request) async { + Future buildDeclarationsForClass( + Host host, AugmentRequest request) async { final target = request.target; return AugmentResponse(augmentations: [ Augmentation(code: expandTemplate(''' @@ -45,7 +39,9 @@ class JsonCodableImplementation implements Macro { ]); } - Future phase3(Host host, AugmentRequest request) async { + @override + Future buildDefinitionForClass( + Host host, AugmentRequest request) async { final target = request.target; final model = await host.query(Query(target: target)); final clazz = model.uris[target.uri]!.scopes[target.name]!; diff --git a/pkgs/_test_macros/lib/query_class.dart b/pkgs/_test_macros/lib/query_class.dart index 6f652b2a..13a841fa 100644 --- a/pkgs/_test_macros/lib/query_class.dart +++ b/pkgs/_test_macros/lib/query_class.dart @@ -16,7 +16,7 @@ class QueryClass { const QueryClass(); } -class QueryClassImplementation implements Macro { +class QueryClassImplementation implements ClassDefinitionMacro { // TODO(davidmorgan): this should be injected by the bootstrap script. @override MacroDescription get description => MacroDescription( @@ -25,11 +25,8 @@ class QueryClassImplementation implements Macro { runsInPhases: [3]); @override - Future augment(Host host, AugmentRequest request) async { - // TODO(davidmorgan): make the host only run in the phases requested so - // that this is not needed. - if (request.phase != 3) return AugmentResponse(augmentations: []); - + Future buildDefinitionForClass( + Host host, AugmentRequest request) async { final model = await host.query(Query( target: request.target, )); diff --git a/pkgs/macro/lib/macro.dart b/pkgs/macro/lib/macro.dart index f4bcfbc6..1d8f9b87 100644 --- a/pkgs/macro/lib/macro.dart +++ b/pkgs/macro/lib/macro.dart @@ -2,11 +2,18 @@ // 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:dart_model/dart_model.dart'; import 'package:macro_service/macro_service.dart'; -/// A macro: transforms Dart code into Dart code augmentations to apply to the -/// code. +/// The macro host. +abstract interface class Host { + Future query(Query query); + // TODO(davidmorgan): introspection methods go here. +} + +/// The marker interface for all types of macros. abstract interface class Macro { /// Description of the macro. /// @@ -15,15 +22,278 @@ abstract interface class Macro { /// than relying on the user-written macro code to return it. /// MacroDescription get description; +} - /// Generate augmentatations and diagnostics for [request]. - /// - /// The code can be introspected by sending queries to [host]. - Future augment(Host host, AugmentRequest request); +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to contribute new type declarations to the library. +abstract interface class LibraryTypesMacro implements Macro { + FutureOr buildTypesForLibrary( + Host host, AugmentRequest request); } -/// The macro host. -abstract interface class Host { - Future query(Query query); - // TODO(davidmorgan): introspection methods go here. +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to contribute new non-type declarations to the library. +abstract interface class LibraryDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForLibrary( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to provide definitions for declarations in the library. +abstract interface class LibraryDefinitionMacro implements Macro { + FutureOr buildDefinitionForLibrary( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to contribute new type +/// declarations to the program. +abstract interface class FunctionTypesMacro implements Macro { + FutureOr buildTypesForFunction( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to contribute new non-type +/// declarations to the program. +abstract interface class FunctionDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForFunction( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to augment the function +/// definition. +abstract interface class FunctionDefinitionMacro implements Macro { + FutureOr buildDefinitionForFunction( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level variable or +/// instance field, and want to contribute new type declarations to the +/// program. +abstract interface class VariableTypesMacro implements Macro { + FutureOr buildTypesForVariable( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level variable or +/// instance field and want to contribute new non-type declarations to the +/// program. +abstract interface class VariableDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForVariable( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any top level variable +/// or instance field, and want to augment the variable definition. +abstract interface class VariableDefinitionMacro implements Macro { + FutureOr buildDefinitionForVariable( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// contribute new type declarations to the program. +abstract interface class ClassTypesMacro implements Macro { + FutureOr buildTypesForClass( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// contribute new non-type declarations to the program. +abstract interface class ClassDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForClass( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// augment the definitions of the members of that class. +abstract interface class ClassDefinitionMacro implements Macro { + FutureOr buildDefinitionForClass( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new type declarations to the program. +abstract interface class EnumTypesMacro implements Macro { + FutureOr buildTypesForEnum( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new non-type declarations to the program. +abstract interface class EnumDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForEnum( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// augment the definitions of members or values of that enum. +abstract interface class EnumDefinitionMacro implements Macro { + FutureOr buildDefinitionForEnum( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new type declarations to the program. +abstract interface class EnumValueTypesMacro implements Macro { + FutureOr buildTypesForEnumValue( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new non-type declarations to the program. +abstract interface class EnumValueDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForEnumValue( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// augment the definitions of members or values of that enum. +abstract interface class EnumValueDefinitionMacro implements Macro { + FutureOr buildDefinitionForEnumValue( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// contribute new type declarations to the program. +abstract interface class FieldTypesMacro implements Macro { + FutureOr buildTypesForField( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// contribute new type declarations to the program. +abstract interface class FieldDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForField( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// augment the field definition. +abstract interface class FieldDefinitionMacro implements Macro { + FutureOr buildDefinitionForField( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// contribute new type declarations to the program. +abstract interface class MethodTypesMacro implements Macro { + FutureOr buildTypesForMethod( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// contribute new non-type declarations to the program. +abstract interface class MethodDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForMethod( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// augment the function definition. +abstract interface class MethodDefinitionMacro implements Macro { + FutureOr buildDefinitionForMethod( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any constructor, and want +/// to contribute new type declarations to the program. +abstract interface class ConstructorTypesMacro implements Macro { + FutureOr buildTypesForConstructor( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any constructors, and +/// want to contribute new non-type declarations to the program. +abstract interface class ConstructorDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForConstructor( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any constructor, and want +/// to augment the function definition. +abstract interface class ConstructorDefinitionMacro implements Macro { + FutureOr buildDefinitionForConstructor( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to contribute new type declarations to the program. +abstract interface class MixinTypesMacro implements Macro { + FutureOr buildTypesForMixin( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to contribute new non-type declarations to the program. +abstract interface class MixinDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForMixin( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to augment the definitions of the members of that mixin. +abstract interface class MixinDefinitionMacro implements Macro { + FutureOr buildDefinitionForMixin( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to contribute new type declarations to the program. +abstract interface class ExtensionTypesMacro implements Macro { + FutureOr buildTypesForExtension( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to contribute new non-type declarations to the program. +abstract interface class ExtensionDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForExtension( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to augment the definitions of the members of that extension. +abstract interface class ExtensionDefinitionMacro implements Macro { + FutureOr buildDefinitionForExtension( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to contribute new type declarations to the program. +abstract interface class ExtensionTypeTypesMacro implements Macro { + FutureOr buildTypesForExtensionType( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to contribute new non-type declarations to the +/// program. +abstract interface class ExtensionTypeDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForExtensionType( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to augment the definitions of the members of that +/// extension. +abstract interface class ExtensionTypeDefinitionMacro implements Macro { + FutureOr buildDefinitionForExtensionType( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any type alias +/// declaration, and want to contribute new type declarations to the program. +abstract interface class TypeAliasTypesMacro implements Macro { + FutureOr buildTypesForTypeAlias( + Host host, AugmentRequest request); +} + +/// The interface for [Macro]s that can be applied to any type alias +/// declaration, and want to contribute new non-type declarations to the +/// program. +abstract interface class TypeAliasDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForTypeAlias( + Host host, AugmentRequest request); }