Version 2.16.0-7.0.dev
Merge commit '8809779d80c6135070249067639aa4bccbf99184' into 'dev'
diff --git a/DEPS b/DEPS
index e93a12b..a79507d 100644
--- a/DEPS
+++ b/DEPS
@@ -44,9 +44,9 @@
# co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
# hashes. It requires access to the dart-build-access group, which EngProd
# has.
- "co19_rev": "73545b2bf6c7f8e4eb931bb04d86e38d6485deee",
+ "co19_rev": "39c5bbc03bf5c5c0a3f9341abe811871eababfaf",
# This line prevents conflicts when both packages are rolled simultaneously.
- "co19_2_rev": "52d3d2a6d2550ccd69c8facf8a36099be66f1493",
+ "co19_2_rev": "86f01ade433fbb4eec3a7f93f5e1e4fed9a84fb3",
# The internal benchmarks to use. See go/dart-benchmarks-internal
"benchmarks_internal_rev": "076df10d9b77af337f2d8029725787155eb1cd52",
@@ -141,7 +141,7 @@
"process_rev": "56ece43b53b64c63ae51ec184b76bd5360c28d0b",
"protobuf_rev": "c1eb6cb51af39ccbaa1a8e19349546586a5c8e31",
"pub_rev": "96404e0749864c9fbf8b12e1d424e8078809e00a",
- "pub_semver_rev": "cee044a3dc867c1c8464408ed435bd21949510c0",
+ "pub_semver_rev": "a43ad72fb6b7869607581b5fedcb186d1e74276a",
"root_certificates_rev": "692f6d6488af68e0121317a9c2c9eb393eb0ee50",
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
"shelf_static_rev": "202ec1a53c9a830c17cf3b718d089cf7eba568ad",
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index b1b1ca0..98ff8fb 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -1781,15 +1781,11 @@
}
void _removePotentiallyAffectedLibraries(String path) {
- _logger.run('Invalidate affected by $path.', () {
- _logger.writeln('Work in $name');
- var affected = <FileState>{};
- _fsState.collectAffected(path, affected);
- _logger.writeln('Remove ${affected.length} libraries.');
- _libraryContext?.elementFactory.removeLibraries(
- affected.map((e) => e.uriStr).toSet(),
- );
- });
+ var affected = <FileState>{};
+ _fsState.collectAffected(path, affected);
+ _libraryContext?.elementFactory.removeLibraries(
+ affected.map((e) => e.uriStr).toSet(),
+ );
}
void _reportException(String path, Object exception, StackTrace stackTrace) {
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index b728d00..e74bf6d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -129,6 +129,9 @@
UnlinkedUnit? _unlinked2;
+ /// Files that reference this file.
+ final List<FileState> referencingFiles = [];
+
List<FileState?>? _importedFiles;
List<FileState?>? _exportedFiles;
List<FileState?>? _partedFiles;
@@ -199,7 +202,10 @@
return _exportedFiles ??= _unlinked2!.exports.map((directive) {
var uri = _selectRelativeUri(directive);
return _fileForRelativeUri(uri).map(
- (file) => file,
+ (file) {
+ file?.referencingFiles.add(this);
+ return file;
+ },
(_) => null,
);
}).toList();
@@ -213,7 +219,10 @@
return _importedFiles ??= _unlinked2!.imports.map((directive) {
var uri = _selectRelativeUri(directive);
return _fileForRelativeUri(uri).map(
- (file) => file,
+ (file) {
+ file?.referencingFiles.add(this);
+ return file;
+ },
(_) => null,
);
}).toList();
@@ -279,6 +288,7 @@
return _fileForRelativeUri(uri).map(
(file) {
if (file != null) {
+ file.referencingFiles.add(this);
_fsState._partToLibraries
.putIfAbsent(file, () => <FileState>[])
.add(this);
@@ -423,6 +433,9 @@
}
}
+ // It is possible that this file does not reference these files.
+ _stopReferencingByThisFile();
+
// Read imports/exports on demand.
_importedFiles = null;
_exportedFiles = null;
@@ -562,6 +575,20 @@
return directive.uri;
}
+ void _stopReferencingByThisFile() {
+ void removeForOne(List<FileState?>? referencedFiles) {
+ if (referencedFiles != null) {
+ for (var referenced in referencedFiles) {
+ referenced?.referencingFiles.remove(this);
+ }
+ }
+ }
+
+ removeForOne(_importedFiles);
+ removeForOne(_exportedFiles);
+ removeForOne(_partedFiles);
+ }
+
static UnlinkedUnit serializeAstUnlinked2(CompilationUnit unit) {
var exports = <UnlinkedNamespaceDirective>[];
var imports = <UnlinkedNamespaceDirective>[];
@@ -740,23 +767,10 @@
/// Collected files that transitively reference a file with the [path].
/// These files are potentially affected by the change.
void collectAffected(String path, Set<FileState> affected) {
- final knownFiles = this.knownFiles.toList();
-
- final fileToReferences = <FileState, List<FileState>>{};
- for (var file in knownFiles) {
- for (var referenced in file.directReferencedFiles) {
- var references = fileToReferences[referenced] ??= [];
- references.add(file);
- }
- }
-
collectAffected(FileState file) {
if (affected.add(file)) {
- var references = fileToReferences[file];
- if (references != null) {
- for (var other in references) {
- collectAffected(other);
- }
+ for (var other in file.referencingFiles) {
+ collectAffected(other);
}
}
}
diff --git a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
index aa594c5..0391099 100644
--- a/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/annotation_resolver.dart
@@ -263,6 +263,19 @@
_visitArguments(node, whyNotPromotedList);
}
+ void _localVariable(
+ AnnotationImpl node,
+ VariableElement element,
+ List<WhyNotPromotedGetter> whyNotPromotedList,
+ ) {
+ if (!element.isConst || node.arguments != null) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.INVALID_ANNOTATION, node);
+ }
+
+ _visitArguments(node, whyNotPromotedList);
+ }
+
void _propertyAccessorElement(
AnnotationImpl node,
SimpleIdentifierImpl name,
@@ -393,8 +406,8 @@
return;
}
- // TODO(scheglov) Must be const.
if (element1 is VariableElement) {
+ _localVariable(node, element1, whyNotPromotedList);
return;
}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_annotation_test.dart
index a72d21b..00abc76 100644
--- a/pkg/analyzer/test/src/diagnostics/invalid_annotation_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/invalid_annotation_test.dart
@@ -110,6 +110,40 @@
]);
}
+ test_localVariable_const() async {
+ await assertNoErrorsInCode(r'''
+void f() {
+ const a = 0;
+ @a
+ var b; // ignore:unused_local_variable
+}
+''');
+ }
+
+ test_localVariable_const_withArguments() async {
+ await assertErrorsInCode(r'''
+void f() {
+ const a = 0;
+ @a(0)
+ var b; // ignore:unused_local_variable
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_ANNOTATION, 28, 5),
+ ]);
+ }
+
+ test_localVariable_final() async {
+ await assertErrorsInCode(r'''
+void f() {
+ final a = 0;
+ @a
+ var b; // ignore:unused_local_variable
+}
+''', [
+ error(CompileTimeErrorCode.INVALID_ANNOTATION, 28, 2),
+ ]);
+ }
+
test_notClass_importWithPrefix() async {
newFile('$testPackageLibPath/annotations.dart', content: r'''
class Property {
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 3e75181..a545f1a 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -21,7 +21,6 @@
import 'src/commands/compile.dart';
import 'src/commands/create.dart';
import 'src/commands/debug_adapter.dart';
-import 'src/commands/doc.dart';
import 'src/commands/fix.dart';
import 'src/commands/language_server.dart';
import 'src/commands/migrate.dart';
@@ -115,7 +114,6 @@
addCommand(CreateCommand(verbose: verbose));
addCommand(DebugAdapterCommand(verbose: verbose));
addCommand(CompileCommand(verbose: verbose));
- addCommand(DocCommand(verbose: verbose));
addCommand(DevToolsCommand(
verbose: verbose,
customDevToolsPath: sdk.devToolsBinaries,
diff --git a/pkg/dartdev/lib/src/commands/doc.dart b/pkg/dartdev/lib/src/commands/doc.dart
deleted file mode 100644
index 26e6abc..0000000
--- a/pkg/dartdev/lib/src/commands/doc.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2021, 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:io' as io;
-
-import 'package:dartdoc/dartdoc.dart';
-import 'package:dartdoc/options.dart';
-import 'package:path/path.dart' as path;
-
-import '../core.dart';
-
-/// A command to create a new project from a set of templates.
-class DocCommand extends DartdevCommand {
- static const String cmdName = 'doc';
- String outputPath;
-
- DocCommand({bool verbose = false})
- : super(
- cmdName,
- 'Generate HTML API documentation from Dart documentation comments.',
- verbose,
- ) {
- outputPath = path.join('.', 'doc', 'api');
- argParser.addOption(
- 'output-dir',
- abbr: 'o',
- defaultsTo: outputPath,
- help: 'Output directory',
- );
- argParser.addFlag(
- 'validate-links',
- negatable: true,
- help: 'Display context aware warnings for broken links (slow)',
- );
- }
-
- @override
- String get invocation => '${super.invocation} <input directory>';
-
- @override
- FutureOr<int> run() async {
- // At least one argument, the input directory, is required.
- if (argResults.rest.isEmpty) {
- usageException("Error: Input directory not specified");
- }
-
- // Determine input directory.
- final dir = io.Directory(argResults.rest[0]);
- if (!dir.existsSync()) {
- usageException("Error: Input directory doesn't exist: ${dir.path}");
- }
-
- // Parse options.
- final options = [
- '--input=${dir.path}',
- '--output=${argResults['output-dir']}',
- ];
- if (argResults['validate-links']) {
- options.add('--validate-links');
- } else {
- options.add('--no-validate-links');
- }
- final config = await parseOptions(pubPackageMetaProvider, options);
- if (config == null) {
- // There was an error while parsing options.
- return 2;
- }
-
- // Call DartDoc.
- if (verbose) {
- log.stdout('Calling DartDoc with the following options: $options');
- }
- final packageConfigProvider = PhysicalPackageConfigProvider();
- final packageBuilder = PubPackageBuilder(
- config, pubPackageMetaProvider, packageConfigProvider);
- final dartdoc = config.generateDocs
- ? await Dartdoc.fromContext(config, packageBuilder)
- : await Dartdoc.withEmptyGenerator(config, packageBuilder);
- dartdoc.executeGuarded();
- return 0;
- }
-}
diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml
index aa4f922..0145fab 100644
--- a/pkg/dartdev/pubspec.yaml
+++ b/pkg/dartdev/pubspec.yaml
@@ -17,7 +17,6 @@
dart2native:
path: ../dart2native
dart_style: any
- dartdoc: any
dds:
path: ../dds
devtools_server: any
diff --git a/pkg/dartdev/test/commands/doc_test.dart b/pkg/dartdev/test/commands/doc_test.dart
deleted file mode 100644
index 84e5faf..0000000
--- a/pkg/dartdev/test/commands/doc_test.dart
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2021, 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 'package:test/test.dart';
-
-import '../utils.dart';
-
-const int compileErrorExitCode = 64;
-
-void main() {
- group('doc', defineCompileTests, timeout: longTimeout);
-}
-
-void defineCompileTests() {
- test('Passing no args fails', () {
- final p = project();
- var result = p.runSync(['doc']);
- expect(result.stderr, contains('Input directory not specified'));
- expect(result.exitCode, compileErrorExitCode);
- });
-
- test('--help', () {
- final p = project();
- final result = p.runSync(['doc', '--help']);
- expect(
- result.stdout,
- contains('Usage: dart doc [arguments] <input directory>'),
- );
-
- expect(result.exitCode, 0);
- });
-
- test('Document a library', () {
- final source = '''
-/// This is Foo. It uses [Bar].
-class Foo {
- Bar bar;
-}
-
-/// Bar is very nice.
-class Bar {
- _i = 42;
-}
- ''';
-
- final p = project(mainSrc: 'void main() { print("Hello, World"); }');
- p.file('lib/foo.dart', source);
- final result = p.runSync(['doc', '--validate-links', p.dirPath]);
- print(
- 'exit: ${result.exitCode}, stderr:\n${result.stderr}\nstdout:\n${result.stdout}');
- expect(result.stdout, contains('Documenting dartdev_temp'));
- });
-
- test('Document a library with broken link is flagged', () {
- final source = '''
-/// This is Foo. It uses [Baz].
-class Foo {
- // Bar bar;
-}
- ''';
-
- final p = project(mainSrc: 'void main() { print("Hello, World"); }');
- p.file('lib/foo.dart', source);
- final result = p.runSync(['doc', '--validate-links', p.dirPath]);
- print(
- 'exit: ${result.exitCode}, stderr:\n${result.stderr}\nstdout:\n${result.stdout}');
- expect(result.stdout, contains('Documenting dartdev_temp'));
- });
-}
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index 67be4b5..655d646 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -13,6 +13,7 @@
import 'package:vm_service/vm_service.dart' as vm;
import '../../../dds.dart';
+import '../../rpc_error_codes.dart';
import '../base_debug_adapter.dart';
import '../exceptions.dart';
import '../isolate_manager.dart';
@@ -49,6 +50,9 @@
/// will work.
const threadExceptionExpression = r'$_threadException';
+/// Typedef for handlers of VM Service stream events.
+typedef _StreamEventHandler<T> = FutureOr<void> Function(T data);
+
/// Pattern for extracting useful error messages from an evaluation exception.
final _evalErrorMessagePattern = RegExp('Error: (.*)');
@@ -354,6 +358,17 @@
late final sendLogsToClient = args.sendLogsToClient ?? false;
+ /// Whether or not the DAP is terminating.
+ ///
+ /// When set to `true`, some requests that return "Service Disappeared" errors
+ /// will be caught and dropped as these are expected if the process is
+ /// terminating.
+ ///
+ /// This flag may be set by incoming requests from the client
+ /// (terminateRequest/disconnectRequest) or when a process terminates, or the
+ /// VM Service disconnects.
+ bool isTerminating = false;
+
DartDebugAdapter(
ByteStreamServerChannel channel, {
this.ipv6 = false,
@@ -531,16 +546,20 @@
this.vmService = vmService;
unawaited(vmService.onDone.then((_) => _handleVmServiceClosed()));
+
+ // Handlers must be wrapped to handle Service Disappeared errors if async
+ // code tries to call the VM Service after termination begins.
+ final wrap = _wrapHandlerWithErrorHandling;
_subscriptions.addAll([
- vmService.onIsolateEvent.listen(handleIsolateEvent),
- vmService.onDebugEvent.listen(handleDebugEvent),
- vmService.onLoggingEvent.listen(handleLoggingEvent),
- vmService.onExtensionEvent.listen(handleExtensionEvent),
- vmService.onServiceEvent.listen(handleServiceEvent),
+ vmService.onIsolateEvent.listen(wrap(handleIsolateEvent)),
+ vmService.onDebugEvent.listen(wrap(handleDebugEvent)),
+ vmService.onLoggingEvent.listen(wrap(handleLoggingEvent)),
+ vmService.onExtensionEvent.listen(wrap(handleExtensionEvent)),
+ vmService.onServiceEvent.listen(wrap(handleServiceEvent)),
if (_subscribeToOutputStreams)
- vmService.onStdoutEvent.listen(_handleStdoutEvent),
+ vmService.onStdoutEvent.listen(wrap(_handleStdoutEvent)),
if (_subscribeToOutputStreams)
- vmService.onStderrEvent.listen(_handleStderrEvent),
+ vmService.onStderrEvent.listen(wrap(_handleStderrEvent)),
]);
await Future.wait([
vmService.streamListen(vm.EventStreams.kIsolate),
@@ -558,8 +577,20 @@
// Let the subclass do any existing setup once we have a connection.
await debuggerConnected(vmInfo);
- // Process any existing isolates that may have been created before the
- // streams above were set up.
+ await _withErrorHandling(
+ () => _configureExistingIsolates(vmService, vmInfo, resumeIfStarting),
+ );
+
+ _debuggerInitializedCompleter.complete();
+ }
+
+ /// Process any existing isolates that may have been created before the
+ /// streams above were set up.
+ Future<void> _configureExistingIsolates(
+ vm.VmService vmService,
+ vm.VM vmInfo,
+ bool resumeIfStarting,
+ ) async {
final existingIsolateRefs = vmInfo.isolates;
final existingIsolates = existingIsolateRefs != null
? await Future.wait(existingIsolateRefs
@@ -596,8 +627,6 @@
}
}
}));
-
- _debuggerInitializedCompleter.complete();
}
/// Handles the clients "continue" ("resume") request for the thread in
@@ -695,6 +724,8 @@
DisconnectArguments? args,
void Function() sendResponse,
) async {
+ isTerminating = true;
+
await disconnectImpl();
await shutdown();
sendResponse();
@@ -839,6 +870,7 @@
return;
}
+ isTerminating = true;
_hasSentTerminatedEvent = true;
// Always add a leading newline since the last written text might not have
// had one.
@@ -1318,6 +1350,8 @@
TerminateArguments? args,
void Function() sendResponse,
) async {
+ isTerminating = true;
+
await terminateImpl();
await shutdown();
sendResponse();
@@ -1661,6 +1695,7 @@
}
Future<void> _handleVmServiceClosed() async {
+ isTerminating = true;
if (terminateOnVmServiceClose) {
handleSessionTerminate();
}
@@ -1767,6 +1802,40 @@
streamClosed: streamClosedCompleter.future,
);
}
+
+ /// Wraps a function with an error handler that handles errors that occur when
+ /// the VM Service/DDS shuts down.
+ ///
+ /// When the debug adapter is terminating, it's possible in-flight requests
+ /// triggered by handlers will fail with "Service Disappeared". This is
+ /// normal and such errors can be ignored, rather than allowed to pass
+ /// uncaught.
+ _StreamEventHandler<T> _wrapHandlerWithErrorHandling<T>(
+ _StreamEventHandler<T> handler,
+ ) {
+ return (data) => _withErrorHandling(() => handler(data));
+ }
+
+ /// Calls a function with an error handler that handles errors that occur when
+ /// the VM Service/DDS shuts down.
+ ///
+ /// When the debug adapter is terminating, it's possible in-flight requests
+ /// will fail with "Service Disappeared". This is normal and such errors can
+ /// be ignored, rather than allowed to pass uncaught.
+ FutureOr<T?> _withErrorHandling<T>(FutureOr<T> Function() func) async {
+ try {
+ return await func();
+ } on vm.RPCError catch (e) {
+ // If we're been asked to shut down while this request was occurring,
+ // it's normal to get kServiceDisappeared so we should handle this
+ // silently.
+ if (isTerminating && e.code == RpcErrorCodes.kServiceDisappeared) {
+ return null;
+ }
+
+ rethrow;
+ }
+ }
}
/// An implementation of [LaunchRequestArguments] that includes all fields used
diff --git a/pkg/dds/test/dap/integration/debug_test.dart b/pkg/dds/test/dap/integration/debug_test.dart
index 6e7565b..12f17b4 100644
--- a/pkg/dds/test/dap/integration/debug_test.dart
+++ b/pkg/dds/test/dap/integration/debug_test.dart
@@ -2,6 +2,7 @@
// 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:io';
import 'package:dds/src/dap/protocol_generated.dart';
@@ -159,6 +160,19 @@
final source = await client.getValidSource(topFrame.source!);
expect(source.content, contains('void print(Object? object) {'));
});
+
+ test('can shutdown during startup', () async {
+ final testFile = dap.createTestFile(simpleArgPrintingProgram);
+
+ // Terminate the app immediately upon recieving the first Thread event.
+ // The DAP is also responding to this event to configure the isolate (eg.
+ // set breakpoints and exception pause behaviour) and will cause it to
+ // receive "Service has disappeared" responses if these are in-flight as
+ // the process terminates. These should not go unhandled since they are
+ // normal during shutdown.
+ unawaited(dap.client.event('thread').then((_) => dap.client.terminate()));
+ await dap.client.start(file: testFile);
+ });
// 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_client.dart b/pkg/dds/test/dap/integration/test_client.dart
index 66f44da..af40d1c 100644
--- a/pkg/dds/test/dap/integration/test_client.dart
+++ b/pkg/dds/test/dap/integration/test_client.dart
@@ -399,7 +399,7 @@
} else {
completer.completeError(message);
}
- } else if (message is Event) {
+ } else if (message is Event && !_eventController.isClosed) {
_eventController.add(message);
// When we see a terminated event, close the event stream so if any
diff --git a/pkg/dds/tool/dap/README.md b/pkg/dds/tool/dap/README.md
index 216d648..a65f707 100644
--- a/pkg/dds/tool/dap/README.md
+++ b/pkg/dds/tool/dap/README.md
@@ -15,7 +15,7 @@
For details on the standard DAP functionality, see [the Debug Adapter Protocol Overview](https://microsoft.github.io/debug-adapter-protocol/) and [the Debug Adapter Protocol Specification](https://microsoft.github.io/debug-adapter-protocol/specification). Custom extensions are detailed below.
-**Flutter**: To run Flutter apps, the equivalent command should be run through the `flutter` tool. This is unavailable at the time of writing, but details will be linked here once available.
+**Flutter**: Flutter apps should be run using the debug adapter in the `flutter` tool - [see this document](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/lib/src/debug_adapters/README.md).
## Launch/Attach Arguments
diff --git a/pkg/vm/lib/transformations/type_flow/analysis.dart b/pkg/vm/lib/transformations/type_flow/analysis.dart
index 61870d6..7cdec21 100644
--- a/pkg/vm/lib/transformations/type_flow/analysis.dart
+++ b/pkg/vm/lib/transformations/type_flow/analysis.dart
@@ -764,6 +764,16 @@
/// different arguments reaches this limit.
static const int maxInvocationsPerSelector = 5000;
+ /// [_DirectInvocation] can be approximated with raw arguments
+ /// if number of operations in its summary exceeds this threshold.
+ static const int largeSummarySize = 300;
+
+ /// If summary exceeds [largeSummarySize] and number of
+ /// [_DirectInvocation] objects with same selector but
+ /// different arguments exceeds this limit, then approximate
+ /// [_DirectInvocation] with raw arguments is created and used.
+ static const int maxDirectInvocationsPerSelector = 10;
+
int count = 0;
_Invocation? approximation;
}
@@ -773,14 +783,19 @@
class _InvocationsCache {
final TypeFlowAnalysis _typeFlowAnalysis;
final Set<_Invocation> _invocations = new Set<_Invocation>();
- final Map<Selector, _SelectorApproximation> _approximations =
- <Selector, _SelectorApproximation>{};
+ final Map<InterfaceSelector, _SelectorApproximation>
+ _interfaceSelectorApproximations =
+ <InterfaceSelector, _SelectorApproximation>{};
+ final Map<DirectSelector, _SelectorApproximation>
+ _directSelectorApproximations =
+ <DirectSelector, _SelectorApproximation>{};
_InvocationsCache(this._typeFlowAnalysis);
_Invocation getInvocation(Selector selector, Args<Type> args) {
++Statistics.invocationsQueriedInCache;
- _Invocation invocation = (selector is DirectSelector)
+ final bool isDirectSelector = (selector is DirectSelector);
+ _Invocation invocation = isDirectSelector
? new _DirectInvocation(selector, args)
: new _DispatchableInvocation(selector, args);
_Invocation? result = _invocations.lookup(invocation);
@@ -788,12 +803,36 @@
return result;
}
- if (selector is InterfaceSelector) {
+ if (isDirectSelector) {
+ // If there is a selector approximation (meaning the summary is large)
+ // then number of distinct invocations per selector should be limited
+ // in order to bound analysis time.
+
+ final sa = _directSelectorApproximations[selector];
+ if (sa != null) {
+ if (sa.count >=
+ _SelectorApproximation.maxDirectInvocationsPerSelector) {
+ _Invocation? approximation = sa.approximation;
+ if (approximation == null) {
+ final rawArgs =
+ _typeFlowAnalysis.summaryCollector.rawArguments(selector);
+ sa.approximation =
+ approximation = _DirectInvocation(selector, rawArgs);
+ approximation.init();
+ Statistics.approximateDirectInvocationsCreated++;
+ }
+ Statistics.approximateDirectInvocationsUsed++;
+ return approximation;
+ }
+ ++sa.count;
+ }
+ } else if (selector is InterfaceSelector) {
// Detect if there are too many invocations per selector. In such case,
// approximate extra invocations with a single invocation with raw
// arguments.
- final sa = (_approximations[selector] ??= new _SelectorApproximation());
+ final sa = (_interfaceSelectorApproximations[selector] ??=
+ new _SelectorApproximation());
if (sa.count >= _SelectorApproximation.maxInvocationsPerSelector) {
_Invocation? approximation = sa.approximation;
@@ -803,9 +842,9 @@
sa.approximation =
approximation = _DispatchableInvocation(selector, rawArgs);
approximation.init();
- Statistics.approximateInvocationsCreated++;
+ Statistics.approximateInterfaceInvocationsCreated++;
}
- Statistics.approximateInvocationsUsed++;
+ Statistics.approximateInterfaceInvocationsUsed++;
return approximation;
}
@@ -820,6 +859,10 @@
++Statistics.invocationsAddedToCache;
return invocation;
}
+
+ void addDirectSelectorApproximation(DirectSelector selector) {
+ _directSelectorApproximations[selector] ??= new _SelectorApproximation();
+ }
}
class _FieldValue extends _DependencyTracker {
@@ -1530,7 +1573,17 @@
_Invocation get currentInvocation => workList.callStack.last;
Summary getSummary(Member member) {
- return _summaries[member] ??= summaryCollector.createSummary(member);
+ Summary? summary = _summaries[member];
+ if (summary == null) {
+ _summaries[member] = summary = summaryCollector.createSummary(member);
+ if (summary.statements.length >=
+ _SelectorApproximation.largeSummarySize) {
+ final DirectSelector selector =
+ currentInvocation.selector as DirectSelector;
+ _invocationsCache.addDirectSelectorApproximation(selector);
+ }
+ }
+ return summary;
}
_FieldValue getFieldValue(Field field) {
diff --git a/pkg/vm/lib/transformations/type_flow/summary.dart b/pkg/vm/lib/transformations/type_flow/summary.dart
index 72b0b68..a309138 100644
--- a/pkg/vm/lib/transformations/type_flow/summary.dart
+++ b/pkg/vm/lib/transformations/type_flow/summary.dart
@@ -682,8 +682,8 @@
if (staticTypeForNarrowing != null) {
types[i] = argType.intersection(staticTypeForNarrowing, typeHierarchy);
} else {
- // TODO(sjindel/tfa): Narrowing is performed inside a [TypeCheck] later.
- types[i] = args[i];
+ // Narrowing is performed inside a [TypeCheck] later.
+ types[i] = argType;
}
}
diff --git a/pkg/vm/lib/transformations/type_flow/utils.dart b/pkg/vm/lib/transformations/type_flow/utils.dart
index c85dfc7..42f54c0 100644
--- a/pkg/vm/lib/transformations/type_flow/utils.dart
+++ b/pkg/vm/lib/transformations/type_flow/utils.dart
@@ -188,8 +188,10 @@
static int invocationsQueriedInCache = 0;
static int invocationsAddedToCache = 0;
static int maxInvocationsCachedPerSelector = 0;
- static int approximateInvocationsCreated = 0;
- static int approximateInvocationsUsed = 0;
+ static int approximateDirectInvocationsCreated = 0;
+ static int approximateDirectInvocationsUsed = 0;
+ static int approximateInterfaceInvocationsCreated = 0;
+ static int approximateInterfaceInvocationsUsed = 0;
static int deepInvocationsDeferred = 0;
static int classesDropped = 0;
static int membersDropped = 0;
@@ -222,8 +224,10 @@
invocationsQueriedInCache = 0;
invocationsAddedToCache = 0;
maxInvocationsCachedPerSelector = 0;
- approximateInvocationsCreated = 0;
- approximateInvocationsUsed = 0;
+ approximateDirectInvocationsCreated = 0;
+ approximateDirectInvocationsUsed = 0;
+ approximateInterfaceInvocationsCreated = 0;
+ approximateInterfaceInvocationsUsed = 0;
deepInvocationsDeferred = 0;
classesDropped = 0;
membersDropped = 0;
@@ -267,8 +271,10 @@
${invocationsQueriedInCache} invocations queried in cache
${invocationsAddedToCache} invocations added to cache
${maxInvocationsCachedPerSelector} maximum invocations cached per selector
- ${approximateInvocationsCreated} approximate invocations created
- ${approximateInvocationsUsed} times approximate invocation is used
+ ${approximateDirectInvocationsCreated} approximate direct invocations created
+ ${approximateDirectInvocationsUsed} times direct interface invocation is used
+ ${approximateInterfaceInvocationsCreated} approximate interface invocations created
+ ${approximateInterfaceInvocationsUsed} times approximate interface invocation is used
${deepInvocationsDeferred} times invocation processing was deferred due to deep call stack
${classesDropped} classes dropped
${membersDropped} members dropped
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index 6c373a5..87d430f 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -3130,6 +3130,7 @@
CompilerState compiler_state(thread(), /*is_aot=*/true, optimized(),
CompilerState::ShouldTrace(function));
+ compiler_state.set_function(function);
{
ic_data_array = new (zone) ZoneGrowableArray<const ICData*>();
diff --git a/runtime/vm/compiler/backend/flow_graph.cc b/runtime/vm/compiler/backend/flow_graph.cc
index a98f355..b0195987 100644
--- a/runtime/vm/compiler/backend/flow_graph.cc
+++ b/runtime/vm/compiler/backend/flow_graph.cc
@@ -2866,4 +2866,8 @@
}
}
+void FlowGraph::Print(const char* phase) {
+ FlowGraphPrinter::PrintGraph(phase, this);
+}
+
} // namespace dart
diff --git a/runtime/vm/compiler/backend/flow_graph.h b/runtime/vm/compiler/backend/flow_graph.h
index fe59272..c847bab 100644
--- a/runtime/vm/compiler/backend/flow_graph.h
+++ b/runtime/vm/compiler/backend/flow_graph.h
@@ -119,6 +119,8 @@
const ParsedFunction& parsed_function() const { return parsed_function_; }
const Function& function() const { return parsed_function_.function(); }
+ void Print(const char* phase = "unknown");
+
// The number of directly accessable parameters (above the frame pointer).
// All other parameters can only be indirectly loaded via metadata found in
// the arguments descriptor.
diff --git a/runtime/vm/compiler/backend/flow_graph_compiler.h b/runtime/vm/compiler/backend/flow_graph_compiler.h
index 98488ae..e195a18 100644
--- a/runtime/vm/compiler/backend/flow_graph_compiler.h
+++ b/runtime/vm/compiler/backend/flow_graph_compiler.h
@@ -488,6 +488,9 @@
BlockEntryInstr* current_block() const { return current_block_; }
void set_current_block(BlockEntryInstr* value) { current_block_ = value; }
+
+ Instruction* current_instruction() const { return current_instruction_; }
+
static bool CanOptimize();
bool CanOptimizeFunction() const;
bool CanOSRFunction() const;
@@ -1143,8 +1146,6 @@
current_instruction_ = current_instruction;
}
- Instruction* current_instruction() { return current_instruction_; }
-
void CompactBlock(BlockEntryInstr* block);
void CompactBlocks();
diff --git a/runtime/vm/compiler/compiler_pass.cc b/runtime/vm/compiler/compiler_pass.cc
index d6071ee..0d91ec1 100644
--- a/runtime/vm/compiler/compiler_pass.cc
+++ b/runtime/vm/compiler/compiler_pass.cc
@@ -8,6 +8,7 @@
#include "vm/compiler/backend/branch_optimizer.h"
#include "vm/compiler/backend/constant_propagator.h"
#include "vm/compiler/backend/flow_graph_checker.h"
+#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/inliner.h"
#include "vm/compiler/backend/linearscan.h"
@@ -206,6 +207,7 @@
Get(kCanonicalize)->Run(state);
}
+ CompilerState::Current().set_current_pass(this, state);
PrintGraph(state, kTraceBefore, round);
{
TIMELINE_DURATION(thread, CompilerVerbose, name());
@@ -217,9 +219,12 @@
}
PrintGraph(state, kTraceAfter, round);
#if defined(DEBUG)
- FlowGraphChecker(state->flow_graph(), state->inline_id_to_function)
- .Check(name());
+ if (CompilerState::Current().is_optimizing()) {
+ FlowGraphChecker(state->flow_graph(), state->inline_id_to_function)
+ .Check(name());
+ }
#endif
+ CompilerState::Current().set_current_pass(nullptr, nullptr);
}
}
@@ -555,4 +560,6 @@
flow_graph->RemoveRedefinitions();
});
+COMPILER_PASS(GenerateCode, { state->graph_compiler->CompileGraph(); });
+
} // namespace dart
diff --git a/runtime/vm/compiler/compiler_pass.h b/runtime/vm/compiler/compiler_pass.h
index 1438db8..9afa960 100644
--- a/runtime/vm/compiler/compiler_pass.h
+++ b/runtime/vm/compiler/compiler_pass.h
@@ -52,12 +52,14 @@
V(TypePropagation) \
V(UseTableDispatch) \
V(WidenSmiToInt32) \
- V(EliminateWriteBarriers)
+ V(EliminateWriteBarriers) \
+ V(GenerateCode)
class AllocationSinking;
class BlockScheduler;
class CallSpecializer;
class FlowGraph;
+class FlowGraphCompiler;
class Function;
class Precompiler;
class SpeculativeInliningPolicy;
@@ -95,6 +97,8 @@
intptr_t sticky_flags;
+ FlowGraphCompiler* graph_compiler = nullptr;
+
private:
FlowGraph* flow_graph_;
};
@@ -144,6 +148,10 @@
enum PipelineMode { kJIT, kAOT };
+ static void GenerateCode(CompilerPassState* state) {
+ CompilerPass::Get(CompilerPass::kGenerateCode)->Run(state);
+ }
+
static void RunGraphIntrinsicPipeline(CompilerPassState* state);
static void RunInliningPipeline(PipelineMode mode, CompilerPassState* state);
diff --git a/runtime/vm/compiler/compiler_state.cc b/runtime/vm/compiler/compiler_state.cc
index b886b6f..dde29ec 100644
--- a/runtime/vm/compiler/compiler_state.cc
+++ b/runtime/vm/compiler/compiler_state.cc
@@ -7,6 +7,7 @@
#include <functional>
#include "vm/compiler/aot/precompiler.h"
+#include "vm/compiler/backend/flow_graph_compiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/backend/slot.h"
#include "vm/growable_array.h"
@@ -128,4 +129,30 @@
return *interpolate_;
}
+void CompilerState::ReportCrash() {
+ OS::PrintErr("=== Crash occured when compiling %s in %s mode in %s pass\n",
+ function() != nullptr ? function()->ToFullyQualifiedCString()
+ : "unknown function",
+ is_aot() ? "AOT"
+ : is_optimizing() ? "optimizing JIT"
+ : "unoptimized JIT",
+ pass() != nullptr ? pass()->name() : "unknown");
+ if (pass_state() != nullptr && pass()->id() == CompilerPass::kGenerateCode) {
+ if (pass_state()->graph_compiler->current_block() != nullptr) {
+ OS::PrintErr("=== When compiling block %s\n",
+ pass_state()->graph_compiler->current_block()->ToCString());
+ }
+ if (pass_state()->graph_compiler->current_instruction() != nullptr) {
+ OS::PrintErr(
+ "=== When compiling instruction %s\n",
+ pass_state()->graph_compiler->current_instruction()->ToCString());
+ }
+ }
+ if (pass_state() != nullptr && pass_state()->flow_graph() != nullptr) {
+ pass_state()->flow_graph()->Print(pass()->name());
+ } else {
+ OS::PrintErr("=== Flow Graph not available\n");
+ }
+}
+
} // namespace dart
diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h
index 1520b40..ff3ac87 100644
--- a/runtime/vm/compiler/compiler_state.h
+++ b/runtime/vm/compiler/compiler_state.h
@@ -17,6 +17,8 @@
namespace dart {
+class CompilerPass;
+struct CompilerPassState;
class Function;
class LocalScope;
class LocalVariable;
@@ -105,6 +107,20 @@
// Returns _StringBase._interpolateSingle
const Function& StringBaseInterpolateSingle();
+ const Function* function() const { return function_; }
+
+ void set_function(const Function& function) { function_ = &function; }
+ void set_current_pass(const CompilerPass* pass,
+ const CompilerPassState* pass_state) {
+ pass_ = pass;
+ pass_state_ = pass_state;
+ }
+
+ const CompilerPass* pass() const { return pass_; }
+ const CompilerPassState* pass_state() const { return pass_state_; }
+
+ void ReportCrash();
+
private:
CHA cha_;
intptr_t deopt_id_ = 0;
@@ -127,6 +143,10 @@
const Function* interpolate_ = nullptr;
const Function* interpolate_single_ = nullptr;
+ const Function* function_ = nullptr;
+ const CompilerPass* pass_ = nullptr;
+ const CompilerPassState* pass_state_ = nullptr;
+
CompilerState* previous_;
};
diff --git a/runtime/vm/compiler/jit/compiler.cc b/runtime/vm/compiler/jit/compiler.cc
index 5b08899..ff96abb 100644
--- a/runtime/vm/compiler/jit/compiler.cc
+++ b/runtime/vm/compiler/jit/compiler.cc
@@ -504,6 +504,7 @@
CompilerState compiler_state(thread(), /*is_aot=*/false, optimized(),
CompilerState::ShouldTrace(function));
+ compiler_state.set_function(function);
{
// Extract type feedback before the graph is built, as the graph
@@ -577,10 +578,9 @@
&speculative_policy, pass_state.inline_id_to_function,
pass_state.inline_id_to_token_pos, pass_state.caller_inline_id,
ic_data_array);
- {
- TIMELINE_DURATION(thread(), CompilerVerbose, "CompileGraph");
- graph_compiler.CompileGraph();
- }
+ pass_state.graph_compiler = &graph_compiler;
+ CompilerPass::GenerateCode(&pass_state);
+
{
TIMELINE_DURATION(thread(), CompilerVerbose, "FinalizeCompilation");
diff --git a/runtime/vm/heap/marker.cc b/runtime/vm/heap/marker.cc
index e979a01..43b4c62 100644
--- a/runtime/vm/heap/marker.cc
+++ b/runtime/vm/heap/marker.cc
@@ -783,13 +783,31 @@
ResetSlices();
for (intptr_t i = 0; i < num_tasks; i++) {
ASSERT(visitors_[i] == NULL);
- visitors_[i] = new SyncMarkingVisitor(
+ SyncMarkingVisitor* visitor = new SyncMarkingVisitor(
isolate_group_, page_space, &marking_stack_, &deferred_marking_stack_);
+ visitors_[i] = visitor;
- // Begin marking on a helper thread.
- bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
- this, isolate_group_, page_space, visitors_[i]);
- ASSERT(result);
+ if (i < (num_tasks - 1)) {
+ // Begin marking on a helper thread.
+ bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
+ this, isolate_group_, page_space, visitor);
+ ASSERT(result);
+ } else {
+ // Last worker is the main thread, which will only mark roots.
+ TIMELINE_FUNCTION_GC_DURATION(Thread::Current(), "ConcurrentMark");
+ int64_t start = OS::GetCurrentMonotonicMicros();
+ IterateRoots(visitor);
+ int64_t stop = OS::GetCurrentMonotonicMicros();
+ visitor->AddMicros(stop - start);
+ if (FLAG_log_marker_tasks) {
+ THR_Print("Task marked %" Pd " bytes in %" Pd64 " micros.\n",
+ visitor->marked_bytes(), visitor->marked_micros());
+ }
+ // Continue non-root marking concurrently.
+ bool result = Dart::thread_pool()->Run<ConcurrentMarkTask>(
+ this, isolate_group_, page_space, visitor);
+ ASSERT(result);
+ }
}
isolate_group_->DeferredMarkLiveTemporaries();
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index aab5dec..58cab0e 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -9,6 +9,9 @@
#include "platform/atomic.h"
#include "vm/allocation.h"
#include "vm/code_patcher.h"
+#if !defined(DART_PRECOMPILED_RUNTIME)
+#include "vm/compiler/compiler_state.h"
+#endif
#include "vm/debugger.h"
#include "vm/instructions.h"
#include "vm/isolate.h"
@@ -1263,6 +1266,11 @@
StackFrame::DumpCurrentTrace();
} else if (thread->execution_state() == Thread::kThreadInVM) {
StackFrame::DumpCurrentTrace();
+#if !defined(DART_PRECOMPILED_RUNTIME)
+ if (thread->HasCompilerState()) {
+ thread->compiler_state().ReportCrash();
+ }
+#endif
}
}
}
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 6824dd8..1eb3222 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -474,8 +474,10 @@
// Has |this| exited Dart code?
bool HasExitedDartCode() const;
+ bool HasCompilerState() const { return compiler_state_ != nullptr; }
+
CompilerState& compiler_state() {
- ASSERT(compiler_state_ != nullptr);
+ ASSERT(HasCompilerState());
return *compiler_state_;
}
diff --git a/tools/VERSION b/tools/VERSION
index cc5ba9d..b8d6a86 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 16
PATCH 0
-PRERELEASE 6
+PRERELEASE 7
PRERELEASE_PATCH 0
\ No newline at end of file