Version 2.15.0-298.0.dev
Merge commit '304330b6fde5a0c1e35000901e0445666eef6749' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server_abstract.dart b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
index 9e758b6..4280b0d 100644
--- a/pkg/analysis_server/lib/src/analysis_server_abstract.dart
+++ b/pkg/analysis_server/lib/src/analysis_server_abstract.dart
@@ -44,12 +44,14 @@
show EvictingFileByteStore;
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
+import 'package:analyzer/src/dart/analysis/results.dart';
import 'package:analyzer/src/dart/ast/element_locator.dart';
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dartdoc/dartdoc_directive_info.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/services/available_declarations.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
+import 'package:analyzer/src/util/performance/operation_performance.dart';
import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
@@ -302,8 +304,14 @@
}
DartdocDirectiveInfo getDartdocDirectiveInfoFor(ResolvedUnitResult result) {
+ return getDartdocDirectiveInfoForSession(result.session);
+ }
+
+ DartdocDirectiveInfo getDartdocDirectiveInfoForSession(
+ AnalysisSession session,
+ ) {
return declarationsTracker
- ?.getContext(result.session.analysisContext)
+ ?.getContext(session.analysisContext)
?.dartdocDirectiveInfo ??
DartdocDirectiveInfo();
}
@@ -312,7 +320,14 @@
/// context that produced the [result], or `null` if there is no cache for the
/// context.
DocumentationCache? getDocumentationCacheFor(ResolvedUnitResult result) {
- var context = result.session.analysisContext;
+ return getDocumentationCacheForSession(result.session);
+ }
+
+ /// Return the object used to cache the documentation for elements in the
+ /// context that produced the [session], or `null` if there is no cache for
+ /// the context.
+ DocumentationCache? getDocumentationCacheForSession(AnalysisSession session) {
+ var context = session.analysisContext;
var tracker = declarationsTracker?.getContext(context);
if (tracker == null) {
return null;
@@ -462,6 +477,31 @@
contextManager.refresh();
}
+ ResolvedForCompletionResultImpl? resolveForCompletion({
+ required String path,
+ required int offset,
+ required OperationPerformanceImpl performance,
+ }) {
+ if (!file_paths.isDart(resourceProvider.pathContext, path)) {
+ return null;
+ }
+
+ var driver = getAnalysisDriver(path);
+ if (driver == null) {
+ return null;
+ }
+
+ try {
+ return driver.resolveForCompletion(
+ path: path,
+ offset: offset,
+ performance: performance,
+ );
+ } catch (e, st) {
+ instrumentationService.logException(e, st);
+ }
+ }
+
/// Sends an error notification to the user.
void sendServerErrorNotification(
String message,
diff --git a/pkg/analysis_server/lib/src/domain_completion.dart b/pkg/analysis_server/lib/src/domain_completion.dart
index 6bf064f..10b3a53 100644
--- a/pkg/analysis_server/lib/src/domain_completion.dart
+++ b/pkg/analysis_server/lib/src/domain_completion.dart
@@ -31,6 +31,7 @@
import 'package:analyzer_plugin/protocol/protocol_common.dart';
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
+import 'package:collection/collection.dart';
/// Instances of the class [CompletionDomainHandler] implement a
/// [RequestHandler] that handles requests in the completion domain.
@@ -302,9 +303,13 @@
'request',
(performance) async {
var resolvedUnit = await performance.runAsync(
- 'getResolvedUnit',
+ 'resolveForCompletion',
(performance) async {
- return await server.getResolvedUnit(file);
+ return server.resolveForCompletion(
+ path: file,
+ offset: offset,
+ performance: performance,
+ );
},
);
if (resolvedUnit == null) {
@@ -329,13 +334,21 @@
);
performanceList.add(completionPerformance);
- var completionRequest = DartCompletionRequest.forResolvedUnit(
- resolvedUnit: resolvedUnit,
+ var analysisSession = resolvedUnit.analysisSession;
+ var enclosingNode =
+ resolvedUnit.resolvedNodes.lastOrNull ?? resolvedUnit.parsedUnit;
+
+ var completionRequest = DartCompletionRequest(
+ analysisSession: analysisSession,
+ filePath: resolvedUnit.path,
+ fileContent: resolvedUnit.content,
+ unitElement: resolvedUnit.unitElement,
+ enclosingNode: enclosingNode,
offset: offset,
- dartdocDirectiveInfo: server.getDartdocDirectiveInfoFor(
- resolvedUnit,
- ),
- documentationCache: server.getDocumentationCacheFor(resolvedUnit),
+ dartdocDirectiveInfo:
+ server.getDartdocDirectiveInfoForSession(analysisSession),
+ documentationCache:
+ server.getDocumentationCacheForSession(analysisSession),
);
setNewRequest(completionRequest);
@@ -391,15 +404,17 @@
}
}
- server.sendResponse(
- CompletionGetSuggestions2Result(
- completionRequest.replacementOffset,
- completionRequest.replacementLength,
- lengthRestricted,
- librariesToImport.keys.map((e) => '$e').toList(),
- isIncomplete,
- ).toResponse(request.id),
- );
+ performance.run('sendResponse', (_) {
+ server.sendResponse(
+ CompletionGetSuggestions2Result(
+ completionRequest.replacementOffset,
+ completionRequest.replacementLength,
+ lengthRestricted,
+ librariesToImport.keys.map((e) => '$e').toList(),
+ isIncomplete,
+ ).toResponse(request.id),
+ );
+ });
},
);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 2a8a2ae..a09c3b7 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -19,7 +19,6 @@
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/instrumentation/service.dart';
import 'package:analyzer/source/error_processor.dart';
-import 'package:analyzer/source/source_range.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
import 'package:analyzer_plugin/src/utilities/change_builder/change_builder_core.dart';
@@ -209,7 +208,6 @@
final analysisOptions = unit.session.analysisContext.analysisOptions;
var overrideSet = _readOverrideSet(unit);
- var lockRanges = <String, List<SourceRange>>{};
for (var error in errors) {
final processor = ErrorProcessor.getProcessor(analysisOptions, error);
// Only fix errors not filtered out in analysis options.
@@ -221,7 +219,7 @@
error,
(name) => [],
);
- await _fixSingleError(fixContext, unit, error, overrideSet, lockRanges);
+ await _fixSingleError(fixContext, unit, error, overrideSet);
}
}
@@ -238,7 +236,6 @@
var errors = List.from(unitResult.errors, growable: false);
errors.sort((a, b) => a.offset.compareTo(b.offset));
- var lockRanges = <String, List<SourceRange>>{};
for (var error in errors) {
var processor = ErrorProcessor.getProcessor(analysisOptions, error);
// Only fix errors not filtered out in analysis options.
@@ -250,8 +247,7 @@
error,
(name) => [],
);
- await _fixSingleError(
- fixContext, unitResult, error, overrideSet, lockRanges);
+ await _fixSingleError(fixContext, unitResult, error, overrideSet);
}
}
}
@@ -264,13 +260,11 @@
DartFixContext fixContext,
ResolvedUnitResult result,
AnalysisError diagnostic,
- TransformOverrideSet? overrideSet,
- Map<String, List<SourceRange>> lockRanges) async {
+ TransformOverrideSet? overrideSet) async {
var context = CorrectionProducerContext.create(
applyingBulkFixes: true,
dartFixContext: fixContext,
diagnostic: diagnostic,
- lockRanges: lockRanges,
overrideSet: overrideSet,
resolvedResult: result,
selectionOffset: diagnostic.offset,
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
index c1d4638..96389a0 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/abstract_producer.dart
@@ -241,7 +241,7 @@
/// A map keyed by lock names whose value is a list of the ranges for which a
/// lock has already been acquired.
- final Map<String, List<SourceRange>> lockRanges;
+ final Map<String, List<SourceRange>> _lockRanges = {};
CorrectionProducerContext._({
required this.resolvedResult,
@@ -253,7 +253,6 @@
this.overrideSet,
this.selectionOffset = -1,
this.selectionLength = 0,
- required this.lockRanges,
}) : file = resolvedResult.path,
session = resolvedResult.session,
sessionHelper = AnalysisSessionHelper(resolvedResult.session),
@@ -277,7 +276,6 @@
TransformOverrideSet? overrideSet,
int selectionOffset = -1,
int selectionLength = 0,
- Map<String, List<SourceRange>>? lockRanges,
}) {
var selectionEnd = selectionOffset + selectionLength;
var locator = NodeLocator(selectionOffset, selectionEnd);
@@ -294,7 +292,6 @@
overrideSet: overrideSet,
selectionOffset: selectionOffset,
selectionLength: selectionLength,
- lockRanges: lockRanges ?? {},
);
}
}
@@ -473,7 +470,7 @@
/// ensure this behavior by attempting to acquire a lock prior to creating any
/// edits, and only create the edits if a lock could be acquired.
bool acquireLockOnRange(String lockName, SourceRange range) {
- var ranges = _context.lockRanges.putIfAbsent(lockName, () => []);
+ var ranges = _context._lockRanges.putIfAbsent(lockName, () => []);
if (ranges.contains(range)) {
return false;
}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
index c464828..e26e0061 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/data_driven_test_support.dart
@@ -2,8 +2,6 @@
// 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:analysis_server/src/services/correction/bulk_fix_processor.dart';
-import 'package:analysis_server/src/services/correction/change_workspace.dart';
import 'package:analysis_server/src/services/correction/dart/data_driven.dart';
import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart';
@@ -11,72 +9,13 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_manager.dart';
import 'package:analyzer/error/error.dart';
-import 'package:analyzer/src/dart/analysis/byte_store.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
-import 'package:analyzer/src/services/available_declarations.dart';
-import 'package:analyzer/src/test_utilities/platform.dart';
-import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit;
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
-import 'package:test/test.dart';
-import '../../../../../utils/test_instrumentation_service.dart';
import '../fix_processor.dart';
/// A base class defining support for writing fix processor tests for
/// data-driven fixes.
-abstract class DataDrivenBulkFixProcessorTest
- extends DataDrivenFixProcessorTest {
- /// Return `true` if this test uses config files.
- bool get useConfigFiles => false;
-
- /// The workspace in which fixes contributor operates.
- @override
- DartChangeWorkspace get workspace {
- return DartChangeWorkspace([session]);
- }
-
- @override
- Future<void> assertHasFix(String expected,
- {bool Function(AnalysisError)? errorFilter,
- int? length,
- String? target,
- int? expectedNumberOfFixesForKind,
- String? matchFixMessage,
- bool allowFixAllFixes = false}) async {
- if (useLineEndingsForPlatform) {
- expected = normalizeNewlinesForPlatform(expected);
- }
- var processor = await computeFixes();
- change = processor.builder.sourceChange;
-
- // apply to "file"
- var fileEdits = change.edits;
- expect(fileEdits, hasLength(1));
-
- var fileContent = testCode;
- if (target != null) {
- expect(fileEdits.first.file, convertPath(target));
- fileContent = getFile(target).readAsStringSync();
- }
-
- resultCode = SourceEdit.applySequence(fileContent, change.edits[0].edits);
- expect(resultCode, expected);
- }
-
- /// Computes fixes for the specified [testUnit].
- Future<BulkFixProcessor> computeFixes() async {
- var tracker = DeclarationsTracker(MemoryByteStore(), resourceProvider);
- var analysisContext = contextFor(testFile);
- tracker.addContext(analysisContext);
- var processor = BulkFixProcessor(TestInstrumentationService(), workspace,
- useConfigFiles: useConfigFiles);
- await processor.fixErrors([analysisContext]);
- return processor;
- }
-}
-
-/// A base class defining support for writing fix processor tests for
-/// data-driven fixes.
abstract class DataDrivenFixProcessorTest extends FixProcessorTest {
/// Return the URI used to import the library created by [setPackageContent].
String get importUri => 'package:p/lib.dart';
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_bulk_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_bulk_test.dart
deleted file mode 100644
index 6e30841..0000000
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_bulk_test.dart
+++ /dev/null
@@ -1,178 +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_reflective_loader/test_reflective_loader.dart';
-
-import 'data_driven_test_support.dart';
-
-void main() {
- defineReflectiveSuite(() {
- defineReflectiveTests(FlutterBulkTest);
- });
-}
-
-@reflectiveTest
-class FlutterBulkTest extends DataDrivenBulkFixProcessorTest {
- Future<void>
- test_material_ThemeData_textSelectionHandleColor_deprecated() async {
- setPackageContent('''
-class ThemeData {
- ThemeData({
- @deprecated Color? textSelectionHandleColor,
- @deprecated bool useTextSelectionTheme = false,
- TextSelectionThemeData? textSelectionTheme}) {}
-}
-class TextSelectionThemeData {
- TextSelectionThemeData({Color selectionHandleColor}) {}
-}
-class Color {}
-class Colors {
- static Color yellow = Color();
-}
-''');
- addPackageDataFile('''
-version: 1
-transforms:
- - title: "Migrate to 'TextSelectionThemeData'"
- date: 2020-09-24
- element:
- uris: ['$importUri']
- constructor: ''
- inClass: 'ThemeData'
- oneOf:
- - if: "textSelectionColor != '' && cursorColor != '' && textSelectionHandleColor != ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(cursorColor: {% cursorColor %}, selectionColor: {% textSelectionColor %}, selectionHandleColor: {% textSelectionHandleColor %},)'
- requiredIf: "textSelectionColor != '' && cursorColor != '' && textSelectionHandleColor != ''"
- - kind: 'removeParameter'
- name: 'textSelectionColor'
- - kind: 'removeParameter'
- name: 'cursorColor'
- - kind: 'removeParameter'
- name: 'textSelectionHandleColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor == '' && cursorColor != '' && textSelectionHandleColor != ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(cursorColor: {% cursorColor %}, selectionHandleColor: {% textSelectionHandleColor %},)'
- requiredIf: "textSelectionColor == '' && cursorColor != '' && textSelectionHandleColor != ''"
- - kind: 'removeParameter'
- name: 'cursorColor'
- - kind: 'removeParameter'
- name: 'textSelectionHandleColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor != '' && cursorColor != '' && textSelectionHandleColor == ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(cursorColor: {% cursorColor %}, selectionColor: {% textSelectionColor %},)'
- requiredIf: "textSelectionColor != '' && cursorColor != '' && textSelectionHandleColor == ''"
- - kind: 'removeParameter'
- name: 'textSelectionColor'
- - kind: 'removeParameter'
- name: 'cursorColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor != '' && cursorColor == '' && textSelectionHandleColor != ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(selectionColor: {% textSelectionColor %}, selectionHandleColor: {% textSelectionHandleColor %},)'
- requiredIf: "textSelectionColor != '' && cursorColor == '' && textSelectionHandleColor != ''"
- - kind: 'removeParameter'
- name: 'textSelectionColor'
- - kind: 'removeParameter'
- name: 'textSelectionHandleColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor == '' && cursorColor != '' && textSelectionHandleColor == ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(cursorColor: {% cursorColor %})'
- requiredIf: "textSelectionColor == '' && cursorColor != '' && textSelectionHandleColor == ''"
- - kind: 'removeParameter'
- name: 'cursorColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor != '' && cursorColor == '' && textSelectionHandleColor == ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(selectionColor: {% textSelectionColor %})'
- requiredIf: "textSelectionColor != '' && cursorColor == '' && textSelectionHandleColor == ''"
- - kind: 'removeParameter'
- name: 'textSelectionColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "textSelectionColor == '' && cursorColor == '' && textSelectionHandleColor != ''"
- changes:
- - kind: 'addParameter'
- index: 73
- name: 'textSelectionTheme'
- style: optional_named
- argumentValue:
- expression: 'TextSelectionThemeData(selectionHandleColor: {% textSelectionHandleColor %})'
- requiredIf: "textSelectionColor == '' && cursorColor == '' && textSelectionHandleColor != ''"
- - kind: 'removeParameter'
- name: 'textSelectionHandleColor'
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- - if: "useTextSelectionTheme != ''"
- changes:
- - kind: 'removeParameter'
- name: 'useTextSelectionTheme'
- variables:
- textSelectionColor:
- kind: 'fragment'
- value: 'arguments[textSelectionColor]'
- cursorColor:
- kind: 'fragment'
- value: 'arguments[cursorColor]'
- textSelectionHandleColor:
- kind: 'fragment'
- value: 'arguments[textSelectionHandleColor]'
- useTextSelectionTheme:
- kind: 'fragment'
- value: 'arguments[useTextSelectionTheme]'
-''');
- await resolveTestCode('''
-import '$importUri';
-
-void f() {
- ThemeData(textSelectionHandleColor: Colors.yellow, useTextSelectionTheme: false);
-}
-''');
- await assertHasFix('''
-import '$importUri';
-
-void f() {
- ThemeData(textSelectionTheme: TextSelectionThemeData(selectionHandleColor: Colors.yellow));
-}
-''');
- }
-}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
index b2ced8a..64cffa2 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_all.dart
@@ -11,7 +11,6 @@
import 'diagnostics/test_all.dart' as diagnostics;
import 'element_matcher_test.dart' as element_matcher;
import 'end_to_end_test.dart' as end_to_end;
-import 'flutter_bulk_test.dart' as flutter_bulk;
import 'flutter_use_case_test.dart' as flutter_use_case;
import 'modify_parameters_test.dart' as modify_parameters;
import 'rename_parameter_test.dart' as rename_parameter;
@@ -31,7 +30,6 @@
diagnostics.main();
element_matcher.main();
end_to_end.main();
- flutter_bulk.main();
flutter_use_case.main();
modify_parameters.main();
rename_parameter.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
index 87759cd..736da8c 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_argument_test.dart
@@ -21,7 +21,7 @@
@override
String get lintCode => LintNames.avoid_redundant_argument_values;
- Future<void> test_independentInvocations() async {
+ Future<void> test_singleFile() async {
await resolveTestCode('''
void f({bool valWithDefault = true, bool val}) {}
void f2({bool valWithDefault = true, bool val}) {}
@@ -41,23 +41,6 @@
}
''');
}
-
- Future<void> test_multipleInSingleInvocation() async {
- await resolveTestCode('''
-void f() {
- g(a: 0, b: 1, c: 2);
-}
-
-void g({int a = 0, int b = 1, int c = 2}) {}
-''');
- await assertHasFix('''
-void f() {
- g();
-}
-
-void g({int a = 0, int b = 1, int c = 2}) {}
-''');
- }
}
@reflectiveTest
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index f0cabd7..fee6a89 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -1328,6 +1328,7 @@
);
return ResolvedForCompletionResultImpl(
+ analysisSession: currentSession,
path: path,
uri: file.uri,
exists: file.exists,
diff --git a/pkg/analyzer/lib/src/dart/analysis/results.dart b/pkg/analyzer/lib/src/dart/analysis/results.dart
index cc7a673..a8184db 100644
--- a/pkg/analyzer/lib/src/dart/analysis/results.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/results.dart
@@ -168,6 +168,7 @@
}
class ResolvedForCompletionResultImpl {
+ final AnalysisSession analysisSession;
final String path;
final Uri uri;
final bool exists;
@@ -196,6 +197,7 @@
final List<AstNode> resolvedNodes;
ResolvedForCompletionResultImpl({
+ required this.analysisSession,
required this.path,
required this.uri,
required this.exists,
diff --git a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
index 459974c..777ccda 100644
--- a/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
+++ b/pkg/analyzer_plugin/lib/src/protocol/protocol_internal.dart
@@ -4,7 +4,6 @@
import 'dart:collection';
import 'dart:convert' hide JsonDecoder;
-import 'dart:math' as math;
import 'package:analyzer_plugin/protocol/protocol.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart';
@@ -29,17 +28,6 @@
/// If the invariants can't be preserved, then a [ConflictingEditException] is
/// thrown.
void addEditForSource(SourceFileEdit sourceFileEdit, SourceEdit sourceEdit) {
- /// If the [leftEdit] and the [rightEdit] can be merged, then merge them.
- SourceEdit? _merge(SourceEdit leftEdit, SourceEdit rightEdit) {
- assert(leftEdit.offset <= rightEdit.offset);
- if (leftEdit.isDeletion && rightEdit.isDeletion) {
- var offset = leftEdit.offset;
- var end = math.max(leftEdit.end, rightEdit.end);
- return SourceEdit(offset, end - offset, '');
- }
- return null;
- }
-
var edits = sourceFileEdit.edits;
var length = edits.length;
var index = 0;
@@ -51,12 +39,7 @@
// The [previousEdit] has an offset that is strictly greater than the offset
// of the [sourceEdit] so we only need to look at the end of the
// [sourceEdit] to know whether they overlap.
- if (sourceEdit.end > previousEdit.offset) {
- var mergedEdit = _merge(sourceEdit, previousEdit);
- if (mergedEdit != null) {
- edits[index - 1] = mergedEdit;
- return;
- }
+ if (sourceEdit.offset + sourceEdit.length > previousEdit.offset) {
throw ConflictingEditException(
newEdit: sourceEdit, existingEdit: previousEdit);
}
@@ -71,12 +54,7 @@
if ((sourceEdit.offset == nextEdit.offset &&
sourceEdit.length > 0 &&
nextEdit.length > 0) ||
- nextEdit.end > sourceEdit.offset) {
- var mergedEdit = _merge(nextEdit, sourceEdit);
- if (mergedEdit != null) {
- edits[index] = mergedEdit;
- return;
- }
+ nextEdit.offset + nextEdit.length > sourceEdit.offset) {
throw ConflictingEditException(
newEdit: sourceEdit, existingEdit: nextEdit);
}
@@ -490,11 +468,3 @@
/// the given [id], where the request was received at the given [requestTime].
Response toResponse(String id, int requestTime);
}
-
-extension SourceEditExtensions on SourceEdit {
- /// Return `true` if this source edit represents a deletion.
- bool get isDeletion => replacement.isEmpty;
-
- /// Return `true` if this source edit represents an insertion.
- bool get isInsertion => length == 0;
-}
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
index 6ed22f9..a571f04 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_core_test.dart
@@ -17,7 +17,6 @@
defineReflectiveSuite(() {
defineReflectiveTests(ChangeBuilderImplTest);
defineReflectiveTests(EditBuilderImplTest);
- defineReflectiveTests(FileEditBuilderImpl_ConflictingTest);
defineReflectiveTests(FileEditBuilderImplTest);
defineReflectiveTests(LinkedEditBuilderImplTest);
});
@@ -298,33 +297,27 @@
}
}
-/// Tests that are specifically targeted at the handling of conflicting edits.
@reflectiveTest
-class FileEditBuilderImpl_ConflictingTest extends AbstractChangeBuilderTest {
+class FileEditBuilderImplTest extends AbstractChangeBuilderTest {
String path = '/test.dart';
- Matcher get hasConflict => throwsA(isA<ConflictingEditException>());
-
- Future<void> test_deletion_deletion_adjacent_left() async {
- var firstOffset = 30;
- var firstLength = 5;
- var secondOffset = 23;
- var secondLength = 7;
+ Future<void> test_addDeletion() async {
+ var offset = 23;
+ var length = 7;
await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(firstOffset, firstLength));
- builder.addDeletion(SourceRange(secondOffset, secondLength));
+ builder.addDeletion(SourceRange(offset, length));
});
var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(2));
- expect(edits[0].offset, firstOffset);
- expect(edits[0].length, firstLength);
+ expect(edits, hasLength(1));
+ expect(edits[0].offset, offset);
+ expect(edits[0].length, length);
expect(edits[0].replacement, isEmpty);
- expect(edits[1].offset, secondOffset);
- expect(edits[1].length, secondLength);
- expect(edits[1].replacement, isEmpty);
}
- Future<void> test_deletion_deletion_adjacent_right() async {
+ Future<void> test_addDeletion_adjacent_lowerOffsetFirst() async {
+ // TODO(brianwilkerson) This should also merge the deletions, but is written
+ // to ensure that existing uses of FileEditBuilder continue to work even
+ // without that change.
var firstOffset = 23;
var firstLength = 7;
var secondOffset = 30;
@@ -343,23 +336,31 @@
expect(edits[1].replacement, isEmpty);
}
- Future<void> test_deletion_deletion_overlap_left() async {
- var firstOffset = 27;
- var firstLength = 8;
- var secondOffset = 23;
- var secondLength = 7;
+ Future<void> test_addDeletion_adjacent_lowerOffsetSecond() async {
+ // TODO(brianwilkerson) This should also merge the deletions, but is written
+ // to ensure that existing uses of FileEditBuilder continue to work even
+ // without that change.
+ var firstOffset = 23;
+ var firstLength = 7;
+ var secondOffset = 30;
+ var secondLength = 5;
await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(firstOffset, firstLength));
builder.addDeletion(SourceRange(secondOffset, secondLength));
+ builder.addDeletion(SourceRange(firstOffset, firstLength));
});
var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
+ expect(edits, hasLength(2));
expect(edits[0].offset, secondOffset);
- expect(edits[0].length, firstOffset + firstLength - secondOffset);
+ expect(edits[0].length, secondLength);
expect(edits[0].replacement, isEmpty);
+ expect(edits[1].offset, firstOffset);
+ expect(edits[1].length, firstLength);
+ expect(edits[1].replacement, isEmpty);
}
- Future<void> test_deletion_deletion_overlap_right() async {
+ @failingTest
+ Future<void> test_addDeletion_overlapping() async {
+ // This support is not yet implemented.
var firstOffset = 23;
var firstLength = 7;
var secondOffset = 27;
@@ -375,169 +376,6 @@
expect(edits[0].replacement, isEmpty);
}
- Future<void> test_deletion_insertion_adjacent_left() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 23;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- expect(() {
- builder.addSimpleInsertion(insertionOffset, insertionText);
- }, hasConflict);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, deletionOffset);
- expect(edits[0].length, deletionLength);
- expect(edits[0].replacement, '');
- }
-
- Future<void> test_deletion_insertion_adjacent_right() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 30;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- builder.addSimpleInsertion(insertionOffset, insertionText);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(2));
- expect(edits[0].offset, insertionOffset);
- expect(edits[0].length, 0);
- expect(edits[0].replacement, insertionText);
- expect(edits[1].offset, deletionOffset);
- expect(edits[1].length, deletionLength);
- expect(edits[1].replacement, isEmpty);
- }
-
- Future<void> test_deletion_insertion_overlap() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 26;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- expect(() {
- builder.addSimpleInsertion(insertionOffset, insertionText);
- }, hasConflict);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, deletionOffset);
- expect(edits[0].length, deletionLength);
- expect(edits[0].replacement, '');
- }
-
- Future<void> test_insertion_deletion_adjacent_left() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 23;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addSimpleInsertion(insertionOffset, insertionText);
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(2));
- expect(edits[0].offset, deletionOffset);
- expect(edits[0].length, deletionLength);
- expect(edits[0].replacement, isEmpty);
- expect(edits[1].offset, insertionOffset);
- expect(edits[1].length, 0);
- expect(edits[1].replacement, insertionText);
- }
-
- Future<void> test_insertion_deletion_adjacent_right() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 30;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addSimpleInsertion(insertionOffset, insertionText);
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(2));
- expect(edits[0].offset, insertionOffset);
- expect(edits[0].length, 0);
- expect(edits[0].replacement, insertionText);
- expect(edits[1].offset, deletionOffset);
- expect(edits[1].length, deletionLength);
- expect(edits[1].replacement, isEmpty);
- }
-
- Future<void> test_insertion_deletion_overlap() async {
- var deletionOffset = 23;
- var deletionLength = 7;
- var insertionOffset = 26;
- var insertionText = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addSimpleInsertion(insertionOffset, insertionText);
- expect(() {
- builder.addDeletion(SourceRange(deletionOffset, deletionLength));
- }, hasConflict);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, insertionOffset);
- expect(edits[0].length, 0);
- expect(edits[0].replacement, insertionText);
- }
-
- Future<void> test_replacement_replacement_overlap_left() async {
- var offset = 23;
- var length = 7;
- var text = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addSimpleReplacement(SourceRange(offset, length), text);
- expect(() {
- builder.addSimpleReplacement(SourceRange(offset - 2, length), text);
- }, hasConflict);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, offset);
- expect(edits[0].length, length);
- expect(edits[0].replacement, text);
- }
-
- Future<void> test_replacement_replacement_overlap_right() async {
- var offset = 23;
- var length = 7;
- var text = 'x';
- await builder.addGenericFileEdit(path, (builder) {
- builder.addSimpleReplacement(SourceRange(offset, length), text);
- expect(() {
- builder.addSimpleReplacement(SourceRange(offset + 2, length), text);
- }, hasConflict);
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, offset);
- expect(edits[0].length, length);
- expect(edits[0].replacement, text);
- }
-}
-
-@reflectiveTest
-class FileEditBuilderImplTest extends AbstractChangeBuilderTest {
- String path = '/test.dart';
-
- Future<void> test_addDeletion() async {
- var offset = 23;
- var length = 7;
- await builder.addGenericFileEdit(path, (builder) {
- builder.addDeletion(SourceRange(offset, length));
- });
- var edits = builder.sourceChange.edits[0].edits;
- expect(edits, hasLength(1));
- expect(edits[0].offset, offset);
- expect(edits[0].length, length);
- expect(edits[0].replacement, isEmpty);
- }
-
Future<void> test_addInsertion() async {
await builder.addGenericFileEdit(path, (builder) {
builder.addInsertion(10, (builder) {
@@ -634,6 +472,40 @@
expect(edits[1].replacement, text);
}
+ Future<void> test_addSimpleReplacement_overlapsHead() async {
+ var offset = 23;
+ var length = 7;
+ var text = 'xyz';
+ await builder.addGenericFileEdit(path, (builder) {
+ builder.addSimpleReplacement(SourceRange(offset, length), text);
+ expect(() {
+ builder.addSimpleReplacement(SourceRange(offset - 2, length), text);
+ }, throwsA(isA<ConflictingEditException>()));
+ });
+ var edits = builder.sourceChange.edits[0].edits;
+ expect(edits, hasLength(1));
+ expect(edits[0].offset, offset);
+ expect(edits[0].length, length);
+ expect(edits[0].replacement, text);
+ }
+
+ Future<void> test_addSimpleReplacement_overlapsTail() async {
+ var offset = 23;
+ var length = 7;
+ var text = 'xyz';
+ await builder.addGenericFileEdit(path, (builder) {
+ builder.addSimpleReplacement(SourceRange(offset, length), text);
+ expect(() {
+ builder.addSimpleReplacement(SourceRange(offset + 2, length), text);
+ }, throwsA(isA<ConflictingEditException>()));
+ });
+ var edits = builder.sourceChange.edits[0].edits;
+ expect(edits, hasLength(1));
+ expect(edits[0].offset, offset);
+ expect(edits[0].length, length);
+ expect(edits[0].replacement, text);
+ }
+
Future<void> test_createEditBuilder() async {
await builder.addGenericFileEdit(path, (builder) {
var offset = 4;
diff --git a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
index dc1ec97..92068f6 100644
--- a/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
+++ b/pkg/compiler/lib/src/deferred_load/program_split_constraints/nodes.dart
@@ -214,6 +214,13 @@
return node;
}
+ NamedNode _lookupNamedNode(String nodeName) {
+ if (!namedNodes.containsKey(nodeName)) {
+ throw 'Missing reference node for $nodeName';
+ }
+ return namedNodes[nodeName];
+ }
+
/// Returns a [ReferenceNode] referencing [importUriAndPrefix].
/// [ReferenceNode]s are typically created in bulk, by mapping over a list of
/// strings of imports in the form 'uri#prefix'. In further builder calls,
@@ -228,17 +235,15 @@
/// Creates an unnamed [RelativeOrderNode] referencing two [NamedNode]s.
RelativeOrderNode orderNode(String predecessor, String successor) {
return RelativeOrderNode(
- predecessor: namedNodes[predecessor], successor: namedNodes[successor]);
+ predecessor: _lookupNamedNode(predecessor),
+ successor: _lookupNamedNode(successor));
}
/// Creates a [CombinerNode] which can be referenced by [name] in further
/// calls to the builder.
CombinerNode combinerNode(String name, Set<String> nodes, CombinerType type) {
ReferenceNode _lookup(String nodeName) {
- if (!namedNodes.containsKey(nodeName)) {
- throw 'Missing reference node for $nodeName';
- }
- var node = namedNodes[nodeName];
+ var node = _lookupNamedNode(nodeName);
if (node is! ReferenceNode) {
// TODO(joshualitt): Implement nested combiners.
throw '$name references node $nodeName which is not a ReferenceNode.';
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index a545f1a..3e75181 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -21,6 +21,7 @@
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';
@@ -114,6 +115,7 @@
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
new file mode 100644
index 0000000..26e6abc
--- /dev/null
+++ b/pkg/dartdev/lib/src/commands/doc.dart
@@ -0,0 +1,84 @@
+// 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 0145fab..aa4f922 100644
--- a/pkg/dartdev/pubspec.yaml
+++ b/pkg/dartdev/pubspec.yaml
@@ -17,6 +17,7 @@
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
new file mode 100644
index 0000000..84e5faf
--- /dev/null
+++ b/pkg/dartdev/test/commands/doc_test.dart
@@ -0,0 +1,70 @@
+// 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/runtime/vm/dart.cc b/runtime/vm/dart.cc
index ced30ca..00a1877 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -582,6 +582,14 @@
// This waits until only the VM isolate remains in the list.
void Dart::WaitForIsolateShutdown() {
+ int64_t start_time = 0;
+ if (FLAG_trace_shutdown) {
+ start_time = UptimeMillis();
+ OS::PrintErr("[+%" Pd64
+ "ms] SHUTDOWN: Waiting for service "
+ "and kernel isolates to shutdown\n",
+ start_time);
+ }
ASSERT(!Isolate::creation_enabled_);
MonitorLocker ml(Isolate::isolate_creation_monitor_);
intptr_t num_attempts = 0;
@@ -592,6 +600,25 @@
if (num_attempts > 10) {
DumpAliveIsolates(num_attempts, /*only_application_isolates=*/false);
}
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: %" Pd
+ " time out waiting for "
+ "service and kernel isolates to shutdown\n",
+ UptimeMillis(), num_attempts);
+ }
+ }
+ }
+ if (FLAG_trace_shutdown) {
+ int64_t stop_time = UptimeMillis();
+ OS::PrintErr("[+%" Pd64
+ "ms] SHUTDOWN: Done waiting for service "
+ "and kernel isolates to shutdown\n",
+ stop_time);
+ if ((stop_time - start_time) > 500) {
+ OS::PrintErr("[+%" Pd64
+ "ms] SHUTDOWN: waited too long for service "
+ "and kernel isolates to shutdown\n",
+ (stop_time - start_time));
}
}
@@ -642,6 +669,10 @@
UptimeMillis());
}
WaitForApplicationIsolateShutdown();
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done shutting down app isolates\n",
+ UptimeMillis());
+ }
}
// Shutdown the kernel isolate.
@@ -658,12 +689,8 @@
}
ServiceIsolate::Shutdown();
- // Wait for the remaining isolate (service isolate) to shutdown
+ // Wait for the remaining isolate (service/kernel isolate) to shutdown
// before shutting down the thread pool.
- if (FLAG_trace_shutdown) {
- OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Waiting for isolate shutdown\n",
- UptimeMillis());
- }
WaitForIsolateShutdown();
#if !defined(PRODUCT)
@@ -696,6 +723,10 @@
thread_pool_->Shutdown();
delete thread_pool_;
thread_pool_ = NULL;
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done deleting thread pool\n",
+ UptimeMillis());
+ }
Api::Cleanup();
delete predefined_handles_;
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index c1e6cbe..c546dba 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -62,6 +62,7 @@
DECLARE_FLAG(bool, print_metrics);
DECLARE_FLAG(bool, trace_service);
+DECLARE_FLAG(bool, trace_shutdown);
DECLARE_FLAG(bool, warn_on_pause_with_no_debugger);
// Reload flags.
@@ -488,6 +489,13 @@
}
void IsolateGroup::Shutdown() {
+ char* name;
+
+ if (FLAG_trace_shutdown) {
+ name = Utils::StrDup(source()->name);
+ OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Shutdown starting for group %s\n",
+ Dart::UptimeMillis(), name);
+ }
// Ensure to join all threads before waiting for pending GC tasks (the thread
// pool can trigger idle notification, which can start new GC tasks).
//
@@ -529,11 +537,28 @@
// After this isolate group has died we might need to notify a pending
// `Dart_Cleanup()` call.
{
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64
+ "ms] SHUTDOWN: Notifying "
+ "isolate group shutdown (%s)\n",
+ Dart::UptimeMillis(), name);
+ }
MonitorLocker ml(Isolate::isolate_creation_monitor_);
if (!Isolate::creation_enabled_ &&
!IsolateGroup::HasApplicationIsolateGroups()) {
ml.Notify();
}
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64
+ "ms] SHUTDOWN: Done Notifying "
+ "isolate group shutdown (%s)\n",
+ Dart::UptimeMillis(), name);
+ }
+ }
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64 "ms] SHUTDOWN: Done shutdown for group %s\n",
+ Dart::UptimeMillis(), name);
+ free(name);
}
}
@@ -2635,6 +2660,10 @@
// The current thread is running on the isolate group's thread pool.
// So we cannot safely delete the isolate group (and it's pool).
// Instead we will destroy the isolate group on the VM-global pool.
+ if (FLAG_trace_shutdown) {
+ OS::PrintErr("[+%" Pd64 "ms] : Scheduling shutdown on VM pool %s\n",
+ Dart::UptimeMillis(), isolate_group->source()->name);
+ }
Dart::thread_pool()->Run<ShutdownGroupTask>(isolate_group);
}
} else {
diff --git a/tools/VERSION b/tools/VERSION
index 786d33b..bd7b815 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 297
+PRERELEASE 298
PRERELEASE_PATCH 0
\ No newline at end of file