Skip to content

Commit

Permalink
Allow union types to have additional fields, support required fields.
Browse files Browse the repository at this point in the history
Uses this feature to add an `id` field to request types, and
`requestId` to response types. These will be used to support multiple
ongoing requests and remove hacks with global variables for cfe/analyzer
internals.
  • Loading branch information
jakemac53 committed Aug 14, 2024
1 parent f8aea9b commit 6f762a6
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 98 deletions.
2 changes: 1 addition & 1 deletion pkgs/_analyzer_macros/lib/query_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ analyzer.LinkedElementFactory get _elementFactory =>
class AnalyzerQueryService implements QueryService {
@override
Future<QueryResponse> handle(QueryRequest request) async {
return QueryResponse(model: _evaluateClassQuery(request.query.target));
return QueryResponse(model: _evaluateClassQuery(request.query!.target!));
}

Model _evaluateClassQuery(QualifiedName target) {
Expand Down
2 changes: 1 addition & 1 deletion pkgs/_cfe_macros/lib/query_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class CfeQueryService implements QueryService {
@override
Future<QueryResponse> handle(QueryRequest request) async {
return QueryResponse(
model: await _evaluateClassQuery(request.query.target));
model: await _evaluateClassQuery(request.query!.target!));
}

Future<Model> _evaluateClassQuery(QualifiedName target) async {
Expand Down
11 changes: 7 additions & 4 deletions pkgs/_macro_client/lib/macro_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ class MacroClient {
// Tell the host which macros are in this bundle.
for (final macro in macros) {
_sendRequest(MacroRequest.macroStartedRequest(
MacroStartedRequest(macroDescription: macro.description)));
MacroStartedRequest(macroDescription: macro.description),
id: nextRequestId));
}

const Utf8Decoder()
Expand Down Expand Up @@ -64,7 +65,8 @@ class MacroClient {
switch (hostRequest.type) {
case HostRequestType.augmentRequest:
_sendResponse(Response.augmentResponse(
await macros.single.augment(_host, hostRequest.asAugmentRequest)));
await macros.single.augment(_host, hostRequest.asAugmentRequest),
requestId: hostRequest.id));
default:
// Ignore unknown request.
// TODO(davidmorgan): make handling of unknown request types a designed
Expand Down Expand Up @@ -96,7 +98,8 @@ class RemoteMacroHost implements Host {

@override
Future<Model> query(Query query) async {
_client._sendRequest(MacroRequest.queryRequest(QueryRequest(query: query)));
_client._sendRequest(MacroRequest.queryRequest(QueryRequest(query: query),
id: nextRequestId));
// TODO(davidmorgan): this is needed because the constructor doesn't wait
// for responses to `MacroStartedRequest`, so we need to discard the
// responses. Properly track requests and responses.
Expand All @@ -105,7 +108,7 @@ class RemoteMacroHost implements Host {
if (nextResponse.type == ResponseType.macroStartedResponse) {
continue;
}
return nextResponse.asQueryResponse.model;
return nextResponse.asQueryResponse.model!;
}
}

Expand Down
27 changes: 18 additions & 9 deletions pkgs/_macro_client/test/macro_client_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ void main() {
'{"type":"MacroStartedRequest","value":'
'{"macroDescription":{"runsInPhases":[2]}}}');

socket.writeln(
json.encode(HostRequest.augmentRequest(AugmentRequest(phase: 2))));
var requestId = nextRequestId;
socket.writeln(json.encode(
HostRequest.augmentRequest(AugmentRequest(phase: 2), id: requestId)));
final augmentResponse = await responses.next;
expect(
augmentResponse,
'{"type":"AugmentResponse","value":'
'{"augmentations":[{"code":"int get x => 3;"}]}}');
'{"augmentations":[{"code":"int get x => 3;"}]},'
'"requestId":$requestId}');
});

test('sends query requests to host, sends reponse', () async {
Expand All @@ -73,23 +75,30 @@ void main() {
'{"type":"MacroStartedRequest","value":'
'{"macroDescription":{"runsInPhases":[3]}}}');

socket.writeln(json.encode(HostRequest.augmentRequest(AugmentRequest(
phase: 3, target: QualifiedName('package:foo/foo.dart#Foo')))));
var requestId = nextRequestId;
socket.writeln(json.encode(HostRequest.augmentRequest(
AugmentRequest(
phase: 3, target: QualifiedName('package:foo/foo.dart#Foo')),
id: nextRequestId)));
final queryRequest = await responses.next;
expect(
queryRequest,
'{"type":"QueryRequest","value":'
'{"query":{"target":"package:foo/foo.dart#Foo"}}}',
'{"query":{"target":"package:foo/foo.dart#Foo"}},'
'"id":$requestId}',
);

socket.writeln(json.encode(Response.queryResponse(QueryResponse(
model: Model(uris: {'package:foo/foo.dart': Library()})))));
socket.writeln(json.encode(Response.queryResponse(
QueryResponse(
model: Model(uris: {'package:foo/foo.dart': Library()})),
requestId: requestId)));

final augmentRequest = await responses.next;
expect(
augmentRequest,
'{"type":"AugmentResponse","value":'
'{"augmentations":[{"code":"// {\\"uris\\":{\\"package:foo/foo.dart\\":{}}}"}]}}',
'{"augmentations":[{"code":"// {\\"uris\\":{\\"package:foo/foo.dart\\":{}}}"}]},'
'"requestId":$requestId}',
);
});
});
Expand Down
11 changes: 7 additions & 4 deletions pkgs/_macro_host/lib/macro_host.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class MacroHost {
// TODO(davidmorgan): this just assumes the macro is running, actually
// track macro lifecycle.
final response = await macroServer.sendToMacro(
name, HostRequest.augmentRequest(request));
name, HostRequest.augmentRequest(request, id: nextRequestId));
return response.asAugmentResponse;
}
}
Expand All @@ -88,12 +88,15 @@ class _HostService implements HostService {
_macroPhases!.complete(request
.asMacroStartedRequest.macroDescription.runsInPhases
.toSet());
return Response.macroStartedResponse(MacroStartedResponse());
return Response.macroStartedResponse(MacroStartedResponse(),
requestId: request.id);
case MacroRequestType.queryRequest:
return Response.queryResponse(
await queryService.handle(request.asQueryRequest));
await queryService.handle(request.asQueryRequest),
requestId: request.id);
default:
return Response.errorResponse(ErrorResponse(error: 'unsupported'));
return Response.errorResponse(ErrorResponse(error: 'unsupported'),
requestId: request.id);
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions pkgs/_macro_server/test/macro_server_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ class TestHostService implements HostService {
Future<Response> handle(MacroRequest request) async {
if (request.type == MacroRequestType.macroStartedRequest) {
_macroStartedRequestsController.add(request.asMacroStartedRequest);
return Response.macroStartedResponse(MacroStartedResponse());
return Response.macroStartedResponse(MacroStartedResponse(),
requestId: request.id);
}
return Response.errorResponse(ErrorResponse(error: 'unimplemented'));
return Response.errorResponse(ErrorResponse(error: 'unimplemented'),
requestId: request.id);
}
}
19 changes: 13 additions & 6 deletions pkgs/dart_model/lib/src/dart_model.g.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated. To make changes edit schemas/*.schema.json
// then run from the repo root: dart tool/model_generator/bin/main.dart
// then run from the repo root: dart tool/dart_model_generator/bin/main.dart

/// An augmentation to Dart code. TODO(davidmorgan): this is a placeholder.
extension type Augmentation.fromJson(Map<String, Object?> node) {
Expand Down Expand Up @@ -165,12 +165,19 @@ enum StaticTypeType {
}

extension type StaticType.fromJson(Map<String, Object?> node) {
static StaticType neverType(NeverType neverType) =>
StaticType.fromJson({'type': 'NeverType', 'value': null});
static StaticType neverType(NeverType neverType) => StaticType.fromJson({
'type': 'NeverType',
'value': neverType,
});
static StaticType nullableType(NullableType nullableType) =>
StaticType.fromJson({'type': 'NullableType', 'value': nullableType.node});
static StaticType voidType(VoidType voidType) =>
StaticType.fromJson({'type': 'VoidType', 'value': voidType.string});
StaticType.fromJson({
'type': 'NullableType',
'value': nullableType,
});
static StaticType voidType(VoidType voidType) => StaticType.fromJson({
'type': 'VoidType',
'value': voidType,
});
StaticTypeType get type {
switch (node['type'] as String) {
case 'NeverType':
Expand Down
17 changes: 17 additions & 0 deletions pkgs/macro_service/lib/src/macro_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,20 @@ abstract interface class MacroService {
/// Handles [request].
Future<Response> handle(HostRequest request);
}

/// Shared implementation of auto incrementing 32 bit IDs.
///
/// These roll back to 0 once it is greater than 2^32.
///
/// These are only unique to the process which generates the request,
/// so for instance the host and macro services may generate conflicting ids
/// and that is allowed.
int get nextRequestId {
final next = _nextRequestId++;
if (_nextRequestId > 2 ^ 32) {
_nextRequestId = 0;
}
return next;
}

int _nextRequestId = 0;
91 changes: 71 additions & 20 deletions pkgs/macro_service/lib/src/macro_service.g.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// This file is generated. To make changes edit schemas/*.schema.json
// then run from the repo root: dart tool/model_generator/bin/main.dart
// then run from the repo root: dart tool/dart_model_generator/bin/main.dart

import 'package:dart_model/dart_model.dart';

Expand Down Expand Up @@ -66,9 +66,15 @@ enum HostRequestType {
}

extension type HostRequest.fromJson(Map<String, Object?> node) {
static HostRequest augmentRequest(AugmentRequest augmentRequest) =>
HostRequest.fromJson(
{'type': 'AugmentRequest', 'value': augmentRequest.node});
static HostRequest augmentRequest(
AugmentRequest augmentRequest, {
required int id,
}) =>
HostRequest.fromJson({
'type': 'AugmentRequest',
'value': augmentRequest,
'id': id,
});
HostRequestType get type {
switch (node['type'] as String) {
case 'AugmentRequest':
Expand All @@ -84,6 +90,9 @@ extension type HostRequest.fromJson(Map<String, Object?> node) {
}
return AugmentRequest.fromJson(node['value'] as Map<String, Object?>);
}

/// The id of this request, must be returned in responses.
int get id => node['id'] as int;
}

/// Information about a macro that the macro provides to the host.
Expand Down Expand Up @@ -125,12 +134,23 @@ enum MacroRequestType {

extension type MacroRequest.fromJson(Map<String, Object?> node) {
static MacroRequest macroStartedRequest(
MacroStartedRequest macroStartedRequest) =>
MacroRequest.fromJson(
{'type': 'MacroStartedRequest', 'value': macroStartedRequest.node});
static MacroRequest queryRequest(QueryRequest queryRequest) =>
MacroRequest.fromJson(
{'type': 'QueryRequest', 'value': queryRequest.node});
MacroStartedRequest macroStartedRequest, {
required int id,
}) =>
MacroRequest.fromJson({
'type': 'MacroStartedRequest',
'value': macroStartedRequest,
'id': id,
});
static MacroRequest queryRequest(
QueryRequest queryRequest, {
required int id,
}) =>
MacroRequest.fromJson({
'type': 'QueryRequest',
'value': queryRequest,
'id': id,
});
MacroRequestType get type {
switch (node['type'] as String) {
case 'MacroStartedRequest':
Expand All @@ -155,6 +175,9 @@ extension type MacroRequest.fromJson(Map<String, Object?> node) {
}
return QueryRequest.fromJson(node['value'] as Map<String, Object?>);
}

/// The id of this request, must be returned in responses.
int get id => node['id'] as int;
}

/// Macro's query about the code it should augment.
Expand Down Expand Up @@ -189,17 +212,42 @@ enum ResponseType {
}

extension type Response.fromJson(Map<String, Object?> node) {
static Response augmentResponse(AugmentResponse augmentResponse) =>
Response.fromJson(
{'type': 'AugmentResponse', 'value': augmentResponse.node});
static Response errorResponse(ErrorResponse errorResponse) =>
Response.fromJson({'type': 'ErrorResponse', 'value': errorResponse.node});
static Response augmentResponse(
AugmentResponse augmentResponse, {
required int requestId,
}) =>
Response.fromJson({
'type': 'AugmentResponse',
'value': augmentResponse,
'requestId': requestId,
});
static Response errorResponse(
ErrorResponse errorResponse, {
required int requestId,
}) =>
Response.fromJson({
'type': 'ErrorResponse',
'value': errorResponse,
'requestId': requestId,
});
static Response macroStartedResponse(
MacroStartedResponse macroStartedResponse) =>
Response.fromJson(
{'type': 'MacroStartedResponse', 'value': macroStartedResponse.node});
static Response queryResponse(QueryResponse queryResponse) =>
Response.fromJson({'type': 'QueryResponse', 'value': queryResponse.node});
MacroStartedResponse macroStartedResponse, {
required int requestId,
}) =>
Response.fromJson({
'type': 'MacroStartedResponse',
'value': macroStartedResponse,
'requestId': requestId,
});
static Response queryResponse(
QueryResponse queryResponse, {
required int requestId,
}) =>
Response.fromJson({
'type': 'QueryResponse',
'value': queryResponse,
'requestId': requestId,
});
ResponseType get type {
switch (node['type'] as String) {
case 'AugmentResponse':
Expand Down Expand Up @@ -242,4 +290,7 @@ extension type Response.fromJson(Map<String, Object?> node) {
}
return QueryResponse.fromJson(node['value'] as Map<String, Object?>);
}

/// The id of the [AugmentRequest] this is responding to.
int get requestId => node['requestId'] as int;
}
Loading

0 comments on commit 6f762a6

Please sign in to comment.