Skip to content

Commit 5922143

Browse files
authored
[macros] Split generation explicitly into numbered rounds. (#3861)
1 parent b5908b1 commit 5922143

File tree

15 files changed

+211
-153
lines changed

15 files changed

+211
-153
lines changed

working/macros/dart_model/dart_model/lib/query.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'delta.dart';
65
import 'model.dart';
76

87
extension type Query.fromJson(Map<String, Object?> node) {
@@ -91,5 +90,5 @@ extension type Operation.fromJson(Map<String, Object?> node) {
9190

9291
abstract interface class Service {
9392
Future<Model> query(Query query);
94-
Future<Stream<Delta>> watch(Query query);
93+
Future<void> changeFiles(Iterable<String> files);
9594
}

working/macros/dart_model/dart_model_analyzer_service/lib/dart_model_analyzer_service.dart

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'dart:async';
6-
import 'dart:convert';
76
import 'dart:io';
87

98
import 'package:analyzer/dart/analysis/analysis_context.dart';
@@ -12,20 +11,27 @@ import 'package:analyzer/dart/analysis/session.dart';
1211
import 'package:analyzer/dart/element/element.dart';
1312
// ignore: implementation_imports
1413
import 'package:analyzer/src/dart/constant/value.dart';
15-
import 'package:dart_model/delta.dart';
1614
import 'package:dart_model/model.dart';
1715
import 'package:dart_model/query.dart';
18-
import 'package:stream_transform/stream_transform.dart';
16+
import 'package:pool/pool.dart';
1917

2018
// ignore_for_file: deprecated_member_use
2119
class DartModelAnalyzerService implements Service {
2220
final AnalysisContext? context;
2321
AnalysisSession? session;
22+
final Pool pool = Pool(1);
2423

2524
DartModelAnalyzerService({this.context, this.session});
2625

2726
@override
2827
Future<Model> query(Query query) async {
28+
// Lock so we don't query while reanalyzing changed files.
29+
return await pool.withResource(() {
30+
return _query(query);
31+
});
32+
}
33+
34+
Future<Model> _query(Query query) async {
2935
if (context != null) {
3036
session = context!.currentSession;
3137
}
@@ -71,29 +77,18 @@ class DartModelAnalyzerService implements Service {
7177
}
7278

7379
@override
74-
Future<Stream<Delta>> watch(Query query) async {
75-
if (context != null) {
76-
session = context!.currentSession;
80+
Future<void> changeFiles(Iterable<String> files) async {
81+
// Lock so we don't query while reanalyzing changed files.
82+
return await pool.withResource(() {
83+
return _changeFiles(files);
84+
});
85+
}
86+
87+
Future<void> _changeFiles(Iterable<String> files) async {
88+
for (final file in files) {
89+
context!.changeFile(file);
7790
}
78-
// TODO(davidmorgan): watch recursivly.
79-
final changes = Directory('${context!.contextRoot.root.path}/lib').watch();
80-
Model previousModel = Model();
81-
return Stream.fromIterable(<FileSystemEvent?>[null])
82-
.followedBy(changes)
83-
.asyncMap((change) async {
84-
if (change != null) context!.changeFile(change.path);
85-
await context!.applyPendingFileChanges();
86-
final model = await this.query(query);
87-
var delta = Delta.compute(previousModel, model);
88-
// Round trip to check serialization works.
89-
// TODO(davidmorgan): add test coverage.
90-
delta = Delta.fromJson(json.decode(json.encode(delta)));
91-
previousModel = model;
92-
return delta.isEmpty ? null : delta;
93-
})
94-
.where((e) => e != null)
95-
.map((e) => e!)
96-
.asBroadcastStream();
91+
await context!.applyPendingFileChanges();
9792
}
9893

9994
Model queryLibrary(LibraryElement libraryElement, Query query) {

working/macros/dart_model/dart_model_analyzer_service/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ environment:
77
dependencies:
88
analyzer: '>=5.2.0 <7.0.0'
99
dart_model: any
10+
pool: ^1.5.1
1011
stream_transform: any
1112

1213
dependency_overrides:

working/macros/dart_model/dart_model_repl/lib/dart_model_repl.dart

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'dart:async';
56
import 'dart:convert';
67
import 'dart:io';
78

@@ -11,11 +12,12 @@ import 'package:async/async.dart';
1112
import 'package:dart_model/model.dart';
1213
import 'package:dart_model/query.dart';
1314
import 'package:dart_model_analyzer_service/dart_model_analyzer_service.dart';
15+
import 'package:macro_host/macro_host.dart';
1416

1517
class DartModelRepl {
1618
final _stdinLines =
1719
StreamQueue(LineSplitter().bind(Utf8Decoder().bind(stdin)));
18-
Service? service;
20+
MacroHost? host;
1921

2022
Future<void> run() async {
2123
print(''''
@@ -26,8 +28,8 @@ Welcome to ${green}package:dart_model_repl$reset!
2628
}
2729

2830
Future<bool> readInput() async {
29-
if (service == null) {
30-
print('No service running; try "analyze <workspace>".');
31+
if (host == null) {
32+
print('No host running; try "analyze <workspace>".');
3133
}
3234

3335
stdout.write('> ');
@@ -39,7 +41,7 @@ Welcome to ${green}package:dart_model_repl$reset!
3941
}
4042

4143
if (line.startsWith('analyze ')) {
42-
createAnalyzer(line.substring('analyze '.length));
44+
createHost(line.substring('analyze '.length));
4345
return true;
4446
}
4547

@@ -50,7 +52,7 @@ Welcome to ${green}package:dart_model_repl$reset!
5052
? Query.uri(rest)
5153
: Query.qualifiedName(
5254
uri: maybeQualifiedName.uri, name: maybeQualifiedName.name);
53-
final model = await service!.query(query);
55+
final model = await host!.service.query(query);
5456
print(model.prettyPrint());
5557
return true;
5658
}
@@ -63,7 +65,8 @@ Welcome to ${green}package:dart_model_repl$reset!
6365
: Query.qualifiedName(
6466
uri: maybeQualifiedName.uri, name: maybeQualifiedName.name);
6567
final model = Model();
66-
(await service!.watch(query)).listen((delta) {
68+
host!.watch(query, 0, (round) {
69+
final delta = round.delta;
6770
delta.update(model);
6871
print('=== current model for $query');
6972
print(model.prettyPrint());
@@ -89,12 +92,24 @@ watch <URI>[#name]
8992
''');
9093
}
9194

92-
void createAnalyzer(String workspace) {
95+
void createHost(String workspace) {
9396
final contextBuilder = ContextBuilder();
9497
final analysisContext = contextBuilder.createContext(
9598
contextRoot:
9699
ContextLocator().locateRoots(includedPaths: [workspace]).first);
97-
service = DartModelAnalyzerService(context: analysisContext);
100+
final service = DartModelAnalyzerService(context: analysisContext);
101+
File? uriConverter(Uri uri) {
102+
final path = analysisContext.currentSession.uriConverter.uriToPath(uri);
103+
if (path == null) return null;
104+
return File(path);
105+
}
106+
107+
host = MacroHost(workspace, service, uriConverter);
108+
109+
// TOD0(davidmorgan): this is a hack to keep the REPL working after file
110+
// watching moved from the service to the host; consider whether it's worth
111+
// doing properly.
112+
unawaited(host!.run());
98113
}
99114
}
100115

working/macros/dart_model/dart_model_repl/pubspec.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies:
99
async: ^2.11.0
1010
dart_model: any
1111
dart_model_analyzer_service: any
12+
macro_host: any
1213

1314
dependency_overrides:
1415
dart_model:
@@ -17,3 +18,9 @@ dependency_overrides:
1718
dart_model_analyzer_service:
1819
path:
1920
../dart_model_analyzer_service
21+
macro_host:
22+
path:
23+
../macro_host
24+
macro_protocol:
25+
path:
26+
../macro_protocol

working/macros/dart_model/macro_client/lib/socket_host.dart

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@ import 'dart:async';
66
import 'dart:convert';
77
import 'dart:io';
88

9-
import 'package:dart_model/delta.dart';
109
import 'package:dart_model/model.dart';
1110
import 'package:dart_model/query.dart';
1211
import 'package:macro_protocol/host.dart';
1312
import 'package:macro_protocol/message.dart';
1413

15-
class SocketHost implements Service, Host {
14+
class SocketHost implements Host {
1615
final Socket socket;
1716
final StreamController<Object?> messagesController =
1817
StreamController.broadcast();
1918
Stream<Object?> get messages => messagesController.stream;
2019
int _id = 0;
21-
final Map<int, StreamController<Delta>> _deltaStreams = {};
20+
final Map<int, StreamController<Round>> _roundStreams = {};
2221

2322
SocketHost(this.socket) {
2423
socket
@@ -29,38 +28,30 @@ class SocketHost implements Service, Host {
2928
}
3029

3130
@override
32-
Future<Model> query(Query query) async {
33-
socket.writeln(json.encode(QueryRequest(query)));
34-
return Model.fromJson((await messages.first) as Map<String, Object?>);
35-
}
36-
37-
@override
38-
Future<Stream<Delta>> watch(Query query) async {
31+
Future<Stream<Round>> watch(Query query) async {
3932
++_id;
4033
socket.writeln(json.encode(WatchRequest(query: query, id: _id)));
41-
final result = StreamController<Delta>();
42-
_deltaStreams[_id] = result;
34+
final result = StreamController<Round>();
35+
_roundStreams[_id] = result;
4336
return result.stream;
4437
}
4538

4639
void _handle(Message message) {
47-
if (message.isWatchResponse) {
48-
final response = message.asWatchResponse;
49-
_deltaStreams[response.id]!.add(response.delta);
40+
if (message.isRound) {
41+
final response = message.asRound;
42+
_roundStreams[response.id]!.add(response);
5043
} else {
5144
messagesController.add(message);
5245
}
5346
}
5447

55-
@override
56-
Service get service => this;
57-
5848
@override
5949
Future<void> augment({
6050
required QualifiedName macro,
51+
required int round,
6152
required Map<String, String> augmentationsByUri,
6253
}) async {
63-
socket.writeln(json.encode(
64-
AugmentRequest(macro: macro, augmentationsByUri: augmentationsByUri)));
54+
socket.writeln(json.encode(AugmentRequest(
55+
macro: macro, round: round, augmentationsByUri: augmentationsByUri)));
6556
}
6657
}

working/macros/dart_model/macro_host/bin/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Future<void> main(List<String> arguments) async {
2525
contextRoot:
2626
ContextLocator().locateRoots(includedPaths: [workspace]).first);
2727
final host = DartModelAnalyzerService(context: analysisContext);
28-
await MacroHost(host, (uri) {
28+
await MacroHost(workspace, host, (uri) {
2929
final path = analysisContext.currentSession.uriConverter.uriToPath(uri);
3030
if (path == null) return null;
3131
return File(path);

0 commit comments

Comments
 (0)