Version 2.18.0-66.0.dev

Merge commit 'f8d011ebb4adafe7f12a50ccf7b8282a14938568' into 'dev'
diff --git a/pkg/analysis_server/test/lsp/initialization_test.dart b/pkg/analysis_server/test/lsp/initialization_test.dart
index 68fc852..94d60fa 100644
--- a/pkg/analysis_server/test/lsp/initialization_test.dart
+++ b/pkg/analysis_server/test/lsp/initialization_test.dart
@@ -25,6 +25,16 @@
 
 @reflectiveTest
 class InitializationTest extends AbstractLspAnalysisServerTest {
+  /// Waits for any in-progress analysis context rebuild.
+  ///
+  /// Pumps the event queue before and after, to ensure any server code that
+  /// runs after the rebuild has had chance to run.
+  Future<void> get contextRebuildComplete async {
+    await pumpEventQueue(times: 5000);
+    await server.analysisContextsRebuilt;
+    await pumpEventQueue(times: 5000);
+  }
+
   TextDocumentRegistrationOptions registrationOptionsFor(
     List<Registration> registrations,
     Method method,
@@ -561,7 +571,7 @@
     // Opening both files should only add the project folder once.
     await openFile(file1Uri, '');
     await openFile(file2Uri, '');
-    await pumpEventQueue(times: 5000);
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([projectFolderPath]));
 
     // Closing only one of the files should not remove the project folder
@@ -576,7 +586,7 @@
     // the context.
     resetContextBuildCounter();
     await closeFile(file2Uri);
-    await pumpEventQueue(times: 5000);
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([]));
     expect(server.contextManager.driverMap, hasLength(0));
     expectContextBuilds();
@@ -611,7 +621,7 @@
 
     // Opening a file nested within the project should add the project folder.
     await openFile(nestedFileUri, '');
-    await pumpEventQueue(times: 500);
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([projectFolderPath]));
 
     // Ensure the file was cached in each driver. This happens as a result of
@@ -851,7 +861,7 @@
     // Opening both files should only add the project folder once.
     await openFile(file1Uri, '');
     await openFile(file2Uri, '');
-    await pumpEventQueue(times: 5000);
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([projectFolderPath]));
     expect(server.contextManager.driverMap, hasLength(1));
 
@@ -867,7 +877,7 @@
     // the context.
     resetContextBuildCounter();
     await closeFile(file2Uri);
-    await pumpEventQueue(times: 5000);
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([]));
     expect(server.contextManager.driverMap, hasLength(0));
     expectContextBuilds();
@@ -909,6 +919,7 @@
     // Opening a file nested within the project should cause the project folder
     // to be added
     await openFile(nestedFileUri, '');
+    await contextRebuildComplete;
     expect(server.contextManager.includedPaths, equals([projectFolderPath]));
 
     // Ensure the file was cached in each driver. This happens as a result of
diff --git a/pkg/dartdev/lib/src/commands/debug_adapter.dart b/pkg/dartdev/lib/src/commands/debug_adapter.dart
index 817f91b..e3fcf99 100644
--- a/pkg/dartdev/lib/src/commands/debug_adapter.dart
+++ b/pkg/dartdev/lib/src/commands/debug_adapter.dart
@@ -63,6 +63,14 @@
       enableDds: args[argDds],
       enableAuthCodes: args[argAuthCodes],
       test: args[argTest],
+      // Protocol errors should be written to stderr to help debug (or in the
+      // case of a user running this command to explain it's for tools).
+      onError: (e) => stderr.writeln(
+        'Input could not be parsed as a Debug Adapter Protocol message.\n'
+        'The "dart debug_adapter" command is intended for use by tooling that '
+        'communicates using the Debug Adapter Protocol.\n\n'
+        '$e',
+      ),
     );
 
     await server.channel.closed;
diff --git a/pkg/dartdev/test/commands/debug_adapter_test.dart b/pkg/dartdev/test/commands/debug_adapter_test.dart
index 00a7e8c..f6d33ff2 100644
--- a/pkg/dartdev/test/commands/debug_adapter_test.dart
+++ b/pkg/dartdev/test/commands/debug_adapter_test.dart
@@ -2,6 +2,8 @@
 // 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:convert';
+
 import 'package:test/test.dart';
 
 import '../utils.dart';
@@ -26,4 +28,25 @@
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
   });
+
+  test('invalid input provides a suitable message', () async {
+    final p = project();
+    final process = await p.start(['debug_adapter']);
+
+    // Capture stderr
+    final errorOutput = StringBuffer();
+    process.stderr.transform(utf8.decoder).listen(errorOutput.write);
+
+    // Write invalid headers and await quit.
+    process.stdin.write('foo\r\nbar\r\n\r\n');
+    await process.exitCode;
+
+    expect(
+        errorOutput.toString(),
+        allOf(
+          contains('Input could not be parsed'),
+          contains('is intended for use by tooling'),
+          contains('foo\r\nbar'),
+        ));
+  });
 }
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index d095e3b..209649a 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -419,7 +419,8 @@
     this.enableDds = true,
     this.enableAuthCodes = true,
     this.logger,
-  }) : super(channel) {
+    Function? onError,
+  }) : super(channel, onError: onError) {
     channel.closed.then((_) => shutdown());
 
     _isolateManager = IsolateManager(this);
diff --git a/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart b/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
index eecc025..b10813e 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_cli_adapter.dart
@@ -34,12 +34,14 @@
     bool enableDds = true,
     bool enableAuthCodes = true,
     Logger? logger,
+    Function? onError,
   }) : super(
           channel,
           ipv6: ipv6,
           enableDds: enableDds,
           enableAuthCodes: enableAuthCodes,
           logger: logger,
+          onError: onError,
         );
 
   /// Whether the VM Service closing should be used as a signal to terminate the
diff --git a/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart b/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
index 468ea09..e80f061 100644
--- a/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart_test_adapter.dart
@@ -33,12 +33,14 @@
     bool enableDds = true,
     bool enableAuthCodes = true,
     Logger? logger,
+    Function? onError,
   }) : super(
           channel,
           ipv6: ipv6,
           enableDds: enableDds,
           enableAuthCodes: enableAuthCodes,
           logger: logger,
+          onError: onError,
         );
 
   /// Whether the VM Service closing should be used as a signal to terminate the
diff --git a/pkg/dds/lib/src/dap/base_debug_adapter.dart b/pkg/dds/lib/src/dap/base_debug_adapter.dart
index 215a975..92fcf2e 100644
--- a/pkg/dds/lib/src/dap/base_debug_adapter.dart
+++ b/pkg/dds/lib/src/dap/base_debug_adapter.dart
@@ -35,8 +35,8 @@
   /// such as `runInTerminal`.
   final _serverToClientRequestCompleters = <int, Completer<Object?>>{};
 
-  BaseDebugAdapter(this._channel) {
-    _channel.listen(_handleIncomingMessage);
+  BaseDebugAdapter(this._channel, {Function? onError}) {
+    _channel.listen(_handleIncomingMessage, onError: onError);
   }
 
   /// Parses arguments for [attachRequest] into a type of [TAttachArgs].
diff --git a/pkg/dds/lib/src/dap/protocol_stream_transformers.dart b/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
index f166e55..7d70edb 100644
--- a/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
+++ b/pkg/dds/lib/src/dap/protocol_stream_transformers.dart
@@ -5,13 +5,16 @@
 import 'dart:async';
 import 'dart:convert';
 
-class InvalidEncodingError {
-  final String headers;
-  InvalidEncodingError(this.headers);
+import 'exceptions.dart';
 
-  @override
-  String toString() =>
-      'Encoding in supplied headers is not supported.\n\nHeaders:\n$headers';
+class InvalidEncodingException extends InvalidHeadersException {
+  InvalidEncodingException(String headers)
+      : super('Encoding in supplied headers is not supported.', headers);
+}
+
+class InvalidHeadersException extends DebugAdapterException {
+  InvalidHeadersException(String message, String headers)
+      : super('$message\n\nHeaders:\n$headers');
 }
 
 /// Transforms a stream of LSP/DAP data in the form:
@@ -36,23 +39,30 @@
         input = stream.expand((b) => b).listen(
           (codeUnit) {
             buffer.add(codeUnit);
-            if (isParsingHeaders && _endsWithCrLfCrLf(buffer)) {
-              headers = _parseHeaders(buffer);
-              buffer.clear();
-              isParsingHeaders = false;
-            } else if (!isParsingHeaders &&
-                buffer.length >= headers!.contentLength) {
-              // UTF-8 is the default - and only supported - encoding for LSP.
-              // The string 'utf8' is valid since it was published in the original spec.
-              // Any other encodings should be rejected with an error.
-              if ([null, 'utf-8', 'utf8']
-                  .contains(headers?.encoding?.toLowerCase())) {
-                _output.add(utf8.decode(buffer));
-              } else {
-                _output.addError(InvalidEncodingError(headers!.rawHeaders));
+            try {
+              if (isParsingHeaders && _endsWithCrLfCrLf(buffer)) {
+                headers = _parseHeaders(buffer);
+                buffer.clear();
+                isParsingHeaders = false;
+              } else if (!isParsingHeaders &&
+                  buffer.length >= headers!.contentLength) {
+                // UTF-8 is the default - and only supported - encoding for LSP.
+                // The string 'utf8' is valid since it was published in the original spec.
+                // Any other encodings should be rejected with an error.
+                if ([null, 'utf-8', 'utf8']
+                    .contains(headers?.encoding?.toLowerCase())) {
+                  _output.add(utf8.decode(buffer));
+                } else {
+                  _output.addError(
+                    InvalidEncodingException(headers!.rawHeaders),
+                  );
+                }
+                buffer.clear();
+                isParsingHeaders = true;
               }
-              buffer.clear();
-              isParsingHeaders = true;
+            } on DebugAdapterException catch (e) {
+              _output.addError(e);
+              _output.close();
             }
           },
           onError: _output.addError,
@@ -96,8 +106,10 @@
           'The stream has utf8 content:\n${utf8.decode(buffer)}');
     }
     final headers = asString.split('\r\n');
-    final lengthHeader =
-        headers.firstWhere((h) => h.startsWith('Content-Length'));
+    final lengthHeader = headers.firstWhere(
+        (h) => h.startsWith('Content-Length'),
+        orElse: () => throw InvalidHeadersException(
+            'No Content-Length header was supplied', asString));
     final length = lengthHeader.split(':').last.trim();
     final contentTypeHeader = headers
         .firstWhere((h) => h.startsWith('Content-Type'), orElse: () => '');
diff --git a/pkg/dds/lib/src/dap/server.dart b/pkg/dds/lib/src/dap/server.dart
index 6de1eaa..9b208a6 100644
--- a/pkg/dds/lib/src/dap/server.dart
+++ b/pkg/dds/lib/src/dap/server.dart
@@ -33,6 +33,7 @@
     this.enableAuthCodes = true,
     this.test = false,
     this.logger,
+    Function? onError,
   }) : channel = ByteStreamServerChannel(_input, _output, logger) {
     adapter = test
         ? DartTestDebugAdapter(
@@ -41,6 +42,7 @@
             enableDds: enableDds,
             enableAuthCodes: enableAuthCodes,
             logger: logger,
+            onError: onError,
           )
         : DartCliDebugAdapter(
             channel,
@@ -48,6 +50,7 @@
             enableDds: enableDds,
             enableAuthCodes: enableAuthCodes,
             logger: logger,
+            onError: onError,
           );
   }
 
diff --git a/pkg/dds/test/dap/integration/protocol_test.dart b/pkg/dds/test/dap/integration/protocol_test.dart
new file mode 100644
index 0000000..06f5fd9
--- /dev/null
+++ b/pkg/dds/test/dap/integration/protocol_test.dart
@@ -0,0 +1,29 @@
+// 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 'dart:convert';
+
+import 'package:test/test.dart';
+
+import 'test_support.dart';
+
+main() {
+  group('dap protocol', () {
+    test('prints a suitable error if the server receives malformed input',
+        () async {
+      final errorOutput = Completer<String>();
+      final server = await DapTestSession.startServer(
+        onError: (e) => errorOutput.complete('$e'),
+      );
+      addTearDown(() => server.stop());
+      server.sink.add(utf8.encode('not\r\n\r\nvalid'));
+      expect(
+        await errorOutput.future,
+        contains('No Content-Length header was supplied'),
+      );
+    });
+    // These tests can be slow due to starting up the external server process.
+  }, timeout: Timeout.none);
+}
diff --git a/pkg/dds/test/dap/integration/test_server.dart b/pkg/dds/test/dap/integration/test_server.dart
index 519f5e1..2da9a3a 100644
--- a/pkg/dds/test/dap/integration/test_server.dart
+++ b/pkg/dds/test/dap/integration/test_server.dart
@@ -33,7 +33,10 @@
   StreamSink<List<int>> get sink => stdinController.sink;
   Stream<List<int>> get stream => stdoutController.stream;
 
-  InProcessDapTestServer._(List<String> args) {
+  InProcessDapTestServer._(
+    List<String> args, {
+    Function? onError,
+  }) {
     _server = DapServer(
       stdinController.stream,
       stdoutController.sink,
@@ -42,6 +45,7 @@
       ipv6: args.contains('--ipv6'),
       enableAuthCodes: !args.contains('--no-auth-codes'),
       test: args.contains('--test'),
+      onError: onError,
     );
   }
 
@@ -52,11 +56,15 @@
 
   static Future<InProcessDapTestServer> create({
     Logger? logger,
+    Function? onError,
     List<String>? additionalArgs,
   }) async {
-    return InProcessDapTestServer._([
-      ...?additionalArgs,
-    ]);
+    return InProcessDapTestServer._(
+      [
+        ...?additionalArgs,
+      ],
+      onError: onError,
+    );
   }
 }
 
@@ -74,12 +82,19 @@
 
   OutOfProcessDapTestServer._(
     this._process,
-    Logger? logger,
-  ) {
-    // Treat anything written to stderr as the DAP crashing and fail the test.
+    Logger? logger, {
+    Function? onError,
+  }) {
+    // Handle any stderr from the process. If an error handler was provided by
+    // the test, call it. Otherwise throw to fail the test as it's likely
+    // unexpected.
     _process.stderr.transform(utf8.decoder).listen((error) {
       logger?.call(error);
-      throw error;
+      if (onError != null) {
+        onError(error);
+      } else {
+        throw error;
+      }
     });
     unawaited(_process.exitCode.then((code) {
       final message = 'Out-of-process DAP server terminated with code $code';
@@ -99,6 +114,7 @@
 
   static Future<OutOfProcessDapTestServer> create({
     Logger? logger,
+    Function? onError,
     List<String>? additionalArgs,
   }) async {
     final ddsEntryScript =
@@ -115,8 +131,8 @@
       ...?additionalArgs,
     ];
 
-    final _process = await Process.start(Platform.resolvedExecutable, args);
+    final process = await Process.start(Platform.resolvedExecutable, args);
 
-    return OutOfProcessDapTestServer._(_process, logger);
+    return OutOfProcessDapTestServer._(process, logger, onError: onError);
   }
 }
diff --git a/pkg/dds/test/dap/integration/test_support.dart b/pkg/dds/test/dap/integration/test_support.dart
index 1c0dc21..1b1dc25 100644
--- a/pkg/dds/test/dap/integration/test_support.dart
+++ b/pkg/dds/test/dap/integration/test_support.dart
@@ -269,7 +269,7 @@
   }
 
   static Future<DapTestSession> setUp({List<String>? additionalArgs}) async {
-    final server = await _startServer(additionalArgs: additionalArgs);
+    final server = await startServer(additionalArgs: additionalArgs);
     final client = await DapTestClient.connect(
       server,
       captureVmServiceTraffic: verboseLogging,
@@ -279,17 +279,20 @@
   }
 
   /// Starts a DAP server that can be shared across tests.
-  static Future<DapTestServer> _startServer({
+  static Future<DapTestServer> startServer({
     Logger? logger,
+    Function? onError,
     List<String>? additionalArgs,
   }) async {
     return useInProcessDap
         ? await InProcessDapTestServer.create(
             logger: logger,
+            onError: onError,
             additionalArgs: additionalArgs,
           )
         : await OutOfProcessDapTestServer.create(
             logger: logger,
+            onError: onError,
             additionalArgs: additionalArgs,
           );
   }
diff --git a/runtime/vm/dwarf.cc b/runtime/vm/dwarf.cc
index d661bd7..1296ad0 100644
--- a/runtime/vm/dwarf.cc
+++ b/runtime/vm/dwarf.cc
@@ -815,6 +815,9 @@
 static constexpr intptr_t kResolvedFileRootLen = sizeof(kResolvedFileRoot) - 1;
 static constexpr char kResolvedSdkRoot[] = "org-dartlang-sdk:///sdk/";
 static constexpr intptr_t kResolvedSdkRootLen = sizeof(kResolvedSdkRoot) - 1;
+static constexpr char kResolvedGoogle3Root[] = "google3:///";
+static constexpr intptr_t kResolvedGoogle3RootLen =
+    sizeof(kResolvedGoogle3Root) - 1;
 
 static const char* ConvertResolvedURI(const char* str) {
   const intptr_t len = strlen(str);
@@ -831,6 +834,10 @@
     // Leave "sdk/" as a prefix in the returned path.
     return str + (kResolvedSdkRootLen - 4);
   }
+  if (len > kResolvedGoogle3RootLen &&
+      strncmp(str, kResolvedGoogle3Root, kResolvedGoogle3RootLen) == 0) {
+    return str + kResolvedGoogle3RootLen;  // Strip off the entire prefix.
+  }
   return nullptr;
 }
 
diff --git a/tools/VERSION b/tools/VERSION
index 09f482f..8165b14 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 65
+PRERELEASE 66
 PRERELEASE_PATCH 0
\ No newline at end of file