Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 7cbcf48

Browse files
author
Dart CI
committed
Version 2.19.0-396.0.dev
Merge d26cf07 into dev
2 parents 45b7433 + d26cf07 commit 7cbcf48

File tree

5 files changed

+407
-12
lines changed

5 files changed

+407
-12
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
Future<void> main(List<String> args) async {
10+
print("""
11+
================================================================================
12+
Stress test tool for language-server protocol.
13+
14+
Example run:
15+
out/ReleaseX64/dart-sdk/bin/dart \\
16+
pkg/analysis_server/tool/lspTestWithParameters.dart \\
17+
--root=pkg/analysis_server \\
18+
--sdk=out/ReleaseX64/dart-sdk/ \\
19+
--click=pkg/analyzer/lib/src/dart/analysis/driver.dart \\
20+
--line=506 \\
21+
--column=20
22+
23+
Additional options:
24+
-v / --verbose Be more verbose. Specify several times for more verbosity.
25+
--verbosity=<int> Set verbosity directly. Defaults to 0. A higher number is
26+
more verbose.
27+
--every=<int> Set how often - in ms - to fire an event. Defaults to 100.
28+
29+
================================================================================
30+
""");
31+
{
32+
Uri exe = Uri.base.resolve(Platform.resolvedExecutable);
33+
Uri librariesDart =
34+
exe.resolve("../lib/_internal/sdk_library_metadata/lib/libraries.dart");
35+
if (!File.fromUri(librariesDart).existsSync()) {
36+
throw "Execute with a dart that has "
37+
"'../lib/_internal/sdk_library_metadata/lib/libraries.dart' "
38+
"available (e.g. out/ReleaseX64/dart-sdk/bin/dart)";
39+
}
40+
}
41+
Uri? rootUri;
42+
Uri? sdkUri;
43+
Uri? clickOnUri;
44+
int? clickLine;
45+
int? clickColumn;
46+
int everyMs = 100;
47+
for (String arg in args) {
48+
if (arg.startsWith("--root=")) {
49+
rootUri = Uri.base.resolve(arg.substring("--root=".length).trim());
50+
} else if (arg.startsWith("--sdk=")) {
51+
sdkUri = Uri.base.resolve(arg.substring("--sdk=".length).trim());
52+
} else if (arg.startsWith("--click=")) {
53+
clickOnUri = Uri.base.resolve(arg.substring("--click=".length).trim());
54+
} else if (arg.startsWith("--line=")) {
55+
clickLine = int.parse(arg.substring("--line=".length).trim());
56+
} else if (arg.startsWith("--column=")) {
57+
clickColumn = int.parse(arg.substring("--column=".length).trim());
58+
} else if (arg.startsWith("--every=")) {
59+
everyMs = int.parse(arg.substring("--every=".length).trim());
60+
} else if (arg == "--verbose" || arg == "-v") {
61+
verbosity++;
62+
} else if (arg.startsWith("--verbosity=")) {
63+
verbosity = int.parse(arg.substring("--verbosity=".length).trim());
64+
} else {
65+
throw "Unknown argument: $arg";
66+
}
67+
}
68+
69+
if (rootUri == null) {
70+
throw "Didn't get a root uri. Specify with --root=";
71+
}
72+
if (!Directory.fromUri(rootUri).existsSync()) {
73+
throw "Directory $rootUri doesn't exist. "
74+
"Specify existing directory with --root=";
75+
}
76+
77+
if (sdkUri == null) {
78+
throw "Didn't get a sdk path. Specify with --sdk=";
79+
}
80+
if (!Directory.fromUri(sdkUri).existsSync()) {
81+
throw "Directory $sdkUri doesn't exist. "
82+
"Specify existing directory with --sdk=";
83+
}
84+
85+
if (clickOnUri == null) {
86+
throw "Didn't get a sdk path. Specify with --click=";
87+
}
88+
if (!File.fromUri(clickOnUri).existsSync()) {
89+
throw "File $clickOnUri doesn't exist. "
90+
"Specify existing file with --click=";
91+
}
92+
93+
if (clickLine == null) {
94+
throw "Didn't get a line to click on. Specify with --line=";
95+
}
96+
if (clickColumn == null) {
97+
throw "Didn't get a column to click on. Specify with --column=";
98+
}
99+
100+
Process p = await Process.start(Platform.resolvedExecutable, [
101+
"language-server",
102+
]);
103+
104+
p.stdout.listen(listenToStdout);
105+
Timer.periodic(const Duration(seconds: 1), (timer) {
106+
bool reportedSomething = false;
107+
for (MapEntry<int, Stopwatch> waitingFor
108+
in outstandingRequestsWithId.entries) {
109+
if (waitingFor.value.elapsed > const Duration(seconds: 1)) {
110+
if (!reportedSomething) {
111+
print("----");
112+
reportedSomething = true;
113+
}
114+
print("==> Has been waiting for ${waitingFor.key} for "
115+
"${waitingFor.value.elapsed}");
116+
}
117+
}
118+
if (reportedSomething) {
119+
print("----");
120+
} else {
121+
print(" -- not waiting for anything -- ");
122+
}
123+
});
124+
125+
await send(p, initMessage(pid, rootUri));
126+
await receivedCompleter.future;
127+
await send(p, initNotification);
128+
await receivedCompleter.future;
129+
await send(p, initMore(sdkUri));
130+
await receivedCompleter.future;
131+
132+
// Try to let it get done...
133+
await Future.delayed(const Duration(seconds: 2));
134+
135+
final Duration everyDuration = Duration(milliseconds: everyMs);
136+
while (true) {
137+
await send(
138+
p,
139+
gotoDef(
140+
largestIdSeen + 1,
141+
clickOnUri,
142+
clickLine,
143+
clickColumn,
144+
));
145+
await Future.delayed(everyDuration);
146+
}
147+
}
148+
149+
final buffer = <int>[];
150+
151+
int? headerContentLength;
152+
153+
Map<String, dynamic> initNotification = {
154+
"jsonrpc": "2.0",
155+
"method": "initialized",
156+
"params": {}
157+
};
158+
159+
/// There's something weird about getting (several) id 3's that wasn't
160+
/// requested...
161+
int largestIdSeen = 3;
162+
Map<int, Stopwatch> outstandingRequestsWithId = {};
163+
Completer<Map<String, dynamic>> receivedCompleter = Completer();
164+
165+
int verbosity = 0;
166+
Map<String, dynamic> gotoDef(int id, Uri uri, int line, int char) {
167+
return {
168+
"jsonrpc": "2.0",
169+
"id": id,
170+
"method": "textDocument/definition",
171+
"params": {
172+
"textDocument": {"uri": "$uri"},
173+
"position": {"line": line, "character": char}
174+
}
175+
};
176+
}
177+
178+
// Messages taken from what VSCode sent.
179+
180+
Map<String, dynamic> initMessage(int processId, Uri rootUri) {
181+
String rootPath = rootUri.toFilePath();
182+
String name = rootUri.pathSegments.last;
183+
if (name.isEmpty) {
184+
name = rootUri.pathSegments[rootUri.pathSegments.length - 2];
185+
}
186+
return {
187+
"id": 0,
188+
"jsonrpc": "2.0",
189+
"method": "initialize",
190+
"params": {
191+
"processId": processId,
192+
"clientInfo": {"name": "lspTestScript", "version": "0.0.1"},
193+
"locale": "en",
194+
"rootPath": rootPath,
195+
"rootUri": "$rootUri",
196+
"capabilities": {},
197+
"initializationOptions": {},
198+
"workspaceFolders": [
199+
{"uri": "$rootUri", "name": rootUri.pathSegments.last}
200+
]
201+
}
202+
};
203+
}
204+
205+
Map<String, dynamic> initMore(Uri sdkUri) {
206+
String sdkPath = sdkUri.toFilePath();
207+
return {
208+
"id": 1,
209+
"jsonrpc": "2.0",
210+
"result": [
211+
{
212+
"useLsp": true,
213+
"sdkPath": sdkPath,
214+
"allowAnalytics": false,
215+
}
216+
]
217+
};
218+
}
219+
220+
void listenToStdout(List<int> event) {
221+
// General idea taken from
222+
// pkg/analysis_server/lib/src/lsp/lsp_packet_transformer.dart
223+
for (int element in event) {
224+
buffer.add(element);
225+
if (verbosity > 3 && buffer.length % 1000 == 999) {
226+
print("DEBUG MESSAGE: Stdout buffer with length ${buffer.length} so far: "
227+
"${utf8.decode(buffer)}");
228+
}
229+
if (headerContentLength == null && _endsWithCrLfCrLf()) {
230+
String headerRaw = utf8.decode(buffer);
231+
buffer.clear();
232+
List<String> headers = headerRaw.split("\r\n");
233+
for (String header in headers) {
234+
if (header.startsWith("Content-Length:")) {
235+
String contentLength =
236+
header.substring("Content-Length:".length).trim();
237+
headerContentLength = int.parse(contentLength);
238+
break;
239+
}
240+
}
241+
} else if (headerContentLength != null &&
242+
buffer.length == headerContentLength!) {
243+
String messageString = utf8.decode(buffer);
244+
buffer.clear();
245+
headerContentLength = null;
246+
Map<String, dynamic> message =
247+
json.decode(messageString) as Map<String, dynamic>;
248+
dynamic possibleId = message["id"];
249+
if (possibleId is int) {
250+
if (possibleId > largestIdSeen) {
251+
largestIdSeen = possibleId;
252+
}
253+
254+
if (verbosity > 0) {
255+
if (messageString.length > 100) {
256+
print("Got message ${messageString.substring(0, 100)}...");
257+
} else {
258+
print("Got message $messageString");
259+
}
260+
}
261+
262+
Stopwatch? stopwatch = outstandingRequestsWithId.remove(possibleId);
263+
if (stopwatch != null) {
264+
stopwatch.stop();
265+
if (verbosity > 2) {
266+
print(" => Got response for $possibleId in ${stopwatch.elapsed}");
267+
}
268+
}
269+
} else if (verbosity > 1) {
270+
if (messageString.length > 100) {
271+
print("Got message ${messageString.substring(0, 100)}...");
272+
} else {
273+
print("Got message $messageString");
274+
}
275+
}
276+
receivedCompleter.complete(message);
277+
receivedCompleter = Completer();
278+
}
279+
}
280+
}
281+
282+
Future<void> send(Process p, Map<String, dynamic> json) async {
283+
// Mostly copied from
284+
// pkg/analysis_server/lib/src/lsp/channel/lsp_byte_stream_channel.dart
285+
final jsonEncodedBody = jsonEncode(json);
286+
final utf8EncodedBody = utf8.encode(jsonEncodedBody);
287+
final header = 'Content-Length: ${utf8EncodedBody.length}\r\n'
288+
'Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n\r\n';
289+
final asciiEncodedHeader = ascii.encode(header);
290+
291+
dynamic possibleId = json["id"];
292+
if (possibleId is int && possibleId > largestIdSeen) {
293+
largestIdSeen = possibleId;
294+
outstandingRequestsWithId[possibleId] = Stopwatch()..start();
295+
if (verbosity > 2) {
296+
print("Sending message with id $possibleId");
297+
}
298+
}
299+
300+
// Header is always ascii, body is always utf8!
301+
p.stdin.add(asciiEncodedHeader);
302+
p.stdin.add(utf8EncodedBody);
303+
await p.stdin.flush();
304+
if (verbosity > 2) {
305+
print("\n\nMessage sent...\n\n");
306+
}
307+
}
308+
309+
/// Copied from pkg/analysis_server/lib/src/lsp/lsp_packet_transformer.dart.
310+
bool _endsWithCrLfCrLf() {
311+
var l = buffer.length;
312+
return l > 4 &&
313+
buffer[l - 1] == 10 &&
314+
buffer[l - 2] == 13 &&
315+
buffer[l - 3] == 10 &&
316+
buffer[l - 4] == 13;
317+
}

pkg/analyzer/lib/dart/analysis/results.dart

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ class CannotResolveUriResult
3737
SomeParsedLibraryResult,
3838
SomeResolvedLibraryResult {}
3939

40+
/// The type of [InvalidResult] returned when the AnalysisContext has been
41+
/// disposed.
42+
///
43+
/// Clients may not extend, implement or mix-in this class.
44+
class DisposedAnalysisContextResult
45+
implements
46+
InvalidResult,
47+
SomeErrorsResult,
48+
SomeFileResult,
49+
SomeParsedLibraryResult,
50+
SomeParsedUnitResult,
51+
SomeResolvedLibraryResult,
52+
SomeResolvedUnitResult,
53+
SomeUnitElementResult {}
54+
4055
/// The declaration of an [Element].
4156
abstract class ElementDeclarationResult {
4257
/// The [Element] that this object describes.

0 commit comments

Comments
 (0)