Skip to content

Commit

Permalink
add specific macro implementations and delegate to them
Browse files Browse the repository at this point in the history
  • Loading branch information
jakemac53 committed Oct 10, 2024
1 parent 87ce3dc commit 98f645f
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 32 deletions.
16 changes: 13 additions & 3 deletions pkgs/_macro_client/lib/macro_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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:
Expand Down
97 changes: 97 additions & 0 deletions pkgs/_macro_client/lib/src/execute_macro.dart
Original file line number Diff line number Diff line change
@@ -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<AugmentResponse> 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<AugmentResponse> 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<AugmentResponse> 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');
}
}
5 changes: 3 additions & 2 deletions pkgs/_test_macros/lib/declare_x_macro.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -22,7 +22,8 @@ class DeclareXImplementation implements Macro {
runsInPhases: [2]);

@override
Future<AugmentResponse> augment(Host host, AugmentRequest request) async {
Future<AugmentResponse> 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: []);
Expand Down
18 changes: 7 additions & 11 deletions pkgs/_test_macros/lib/json_codable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,17 @@ 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(
uri: 'package:_test_macros/json_codable.dart', name: 'JsonCodable'),
runsInPhases: [2, 3]);

@override
Future<AugmentResponse> augment(Host host, AugmentRequest request) async {
return switch (request.phase) {
2 => phase2(host, request),
3 => phase3(host, request),
_ => AugmentResponse(augmentations: []),
};
}

Future<AugmentResponse> phase2(Host host, AugmentRequest request) async {
Future<AugmentResponse> buildDeclarationsForClass(
Host host, AugmentRequest request) async {
final target = request.target;
return AugmentResponse(augmentations: [
Augmentation(code: expandTemplate('''
Expand All @@ -45,7 +39,9 @@ class JsonCodableImplementation implements Macro {
]);
}

Future<AugmentResponse> phase3(Host host, AugmentRequest request) async {
@override
Future<AugmentResponse> 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]!;
Expand Down
9 changes: 3 additions & 6 deletions pkgs/_test_macros/lib/query_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -25,11 +25,8 @@ class QueryClassImplementation implements Macro {
runsInPhases: [3]);

@override
Future<AugmentResponse> 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<AugmentResponse> buildDefinitionForClass(
Host host, AugmentRequest request) async {
final model = await host.query(Query(
target: request.target,
));
Expand Down
Loading

0 comments on commit 98f645f

Please sign in to comment.