Version 2.18.0-33.0.dev
Merge commit 'f76b6a8cc155442fe04f4cc1d78f7b5d1428686c' into 'dev'
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index c248c2f..5538e46f 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -143,6 +143,9 @@
/// The context used by the execution domain handlers.
final ExecutionContext executionContext = ExecutionContext();
+ /// The next search response id.
+ int nextSearchId = 0;
+
/// The [Completer] that completes when analysis is complete.
Completer<void>? _onAnalysisCompleteCompleter;
diff --git a/pkg/analysis_server/lib/src/flutter/flutter_domain.dart b/pkg/analysis_server/lib/src/flutter/flutter_domain.dart
index 2338d6e..a57802c 100644
--- a/pkg/analysis_server/lib/src/flutter/flutter_domain.dart
+++ b/pkg/analysis_server/lib/src/flutter/flutter_domain.dart
@@ -4,9 +4,10 @@
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/src/domain_abstract.dart';
-import 'package:analysis_server/src/protocol/protocol_internal.dart';
+import 'package:analysis_server/src/handler/legacy/flutter_get_widget_description.dart';
+import 'package:analysis_server/src/handler/legacy/flutter_set_subscriptions.dart';
+import 'package:analysis_server/src/handler/legacy/flutter_set_widget_property_value.dart';
import 'package:analysis_server/src/protocol_server.dart';
-import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/src/utilities/cancellation.dart';
/// A [RequestHandler] that handles requests in the `flutter` domain.
@@ -15,118 +16,29 @@
/// [server].
FlutterDomainHandler(super.server);
- /// Implement the 'flutter.getWidgetDescription' request.
- void getWidgetDescription(Request request) async {
- var params = FlutterGetWidgetDescriptionParams.fromRequest(request);
- var file = params.file;
- var offset = params.offset;
-
- if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
- return;
- }
-
- var resolvedUnit = await server.getResolvedUnit(file);
- if (resolvedUnit == null) {
- server.sendResponse(Response.fileNotAnalyzed(request, file));
- return;
- }
-
- var computer = server.flutterWidgetDescriptions;
-
- FlutterGetWidgetDescriptionResult? result;
- try {
- result = await computer.getDescription(
- resolvedUnit,
- offset,
- );
- } on InconsistentAnalysisException {
- server.sendResponse(
- Response(
- request.id,
- error: RequestError(
- RequestErrorCode.FLUTTER_GET_WIDGET_DESCRIPTION_CONTENT_MODIFIED,
- 'Concurrent modification detected.',
- ),
- ),
- );
- return;
- }
-
- if (result == null) {
- server.sendResponse(
- Response(
- request.id,
- error: RequestError(
- RequestErrorCode.FLUTTER_GET_WIDGET_DESCRIPTION_NO_WIDGET,
- 'No Flutter widget at the given location.',
- ),
- ),
- );
- return;
- }
-
- server.sendResponse(
- result.toResponse(request.id),
- );
- }
-
@override
Response? handleRequest(
Request request, CancellationToken cancellationToken) {
try {
var requestName = request.method;
if (requestName == FLUTTER_REQUEST_GET_WIDGET_DESCRIPTION) {
- getWidgetDescription(request);
+ FlutterGetWidgetDescriptionHandler(server, request, cancellationToken)
+ .handle();
return Response.DELAYED_RESPONSE;
}
if (requestName == FLUTTER_REQUEST_SET_WIDGET_PROPERTY_VALUE) {
- setPropertyValue(request);
+ FlutterSetWidgetPropertyValueHandler(server, request, cancellationToken)
+ .handle();
return Response.DELAYED_RESPONSE;
}
if (requestName == FLUTTER_REQUEST_SET_SUBSCRIPTIONS) {
- return setSubscriptions(request);
+ FlutterSetSubscriptionsHandler(server, request, cancellationToken)
+ .handle();
+ return Response.DELAYED_RESPONSE;
}
} on RequestFailure catch (exception) {
return exception.response;
}
return null;
}
-
- /// Implement the 'flutter.setPropertyValue' request.
- void setPropertyValue(Request request) async {
- var params = FlutterSetWidgetPropertyValueParams.fromRequest(request);
-
- var result = await server.flutterWidgetDescriptions.setPropertyValue(
- params.id,
- params.value,
- );
-
- var errorCode = result.errorCode;
- if (errorCode != null) {
- server.sendResponse(
- Response(
- request.id,
- error: RequestError(errorCode, ''),
- ),
- );
- }
-
- server.sendResponse(
- FlutterSetWidgetPropertyValueResult(
- result.change!,
- ).toResponse(request.id),
- );
- }
-
- /// Implement the 'flutter.setSubscriptions' request.
- Response setSubscriptions(Request request) {
- var params = FlutterSetSubscriptionsParams.fromRequest(request);
- var subMap =
- mapMap<FlutterService, List<String>, FlutterService, Set<String>>(
- params.subscriptions,
- valueCallback: (List<String> subscriptions) =>
- subscriptions.toSet());
- server.setFlutterSubscriptions(subMap);
- return FlutterSetSubscriptionsResult().toResponse(request.id);
- }
}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/flutter_get_widget_description.dart b/pkg/analysis_server/lib/src/handler/legacy/flutter_get_widget_description.dart
new file mode 100644
index 0000000..c3aad69
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/flutter_get_widget_description.dart
@@ -0,0 +1,71 @@
+// 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 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analyzer/dart/analysis/session.dart';
+
+/// The handler for the `flutter.getWidgetDescription` request.
+class FlutterGetWidgetDescriptionHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ FlutterGetWidgetDescriptionHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var params = FlutterGetWidgetDescriptionParams.fromRequest(request);
+ var file = params.file;
+ var offset = params.offset;
+
+ if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+ return;
+ }
+
+ var resolvedUnit = await server.getResolvedUnit(file);
+ if (resolvedUnit == null) {
+ sendResponse(Response.fileNotAnalyzed(request, file));
+ return;
+ }
+
+ var computer = server.flutterWidgetDescriptions;
+
+ FlutterGetWidgetDescriptionResult? result;
+ try {
+ result = await computer.getDescription(
+ resolvedUnit,
+ offset,
+ );
+ } on InconsistentAnalysisException {
+ sendResponse(
+ Response(
+ request.id,
+ error: RequestError(
+ RequestErrorCode.FLUTTER_GET_WIDGET_DESCRIPTION_CONTENT_MODIFIED,
+ 'Concurrent modification detected.',
+ ),
+ ),
+ );
+ return;
+ }
+
+ if (result == null) {
+ sendResponse(
+ Response(
+ request.id,
+ error: RequestError(
+ RequestErrorCode.FLUTTER_GET_WIDGET_DESCRIPTION_NO_WIDGET,
+ 'No Flutter widget at the given location.',
+ ),
+ ),
+ );
+ return;
+ }
+
+ sendResult(result);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/flutter_set_subscriptions.dart b/pkg/analysis_server/lib/src/handler/legacy/flutter_set_subscriptions.dart
new file mode 100644
index 0000000..2848638
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/flutter_set_subscriptions.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 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/protocol/protocol_internal.dart';
+
+/// The handler for the `flutter.setSubscriptions` request.
+class FlutterSetSubscriptionsHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ FlutterSetSubscriptionsHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var params = FlutterSetSubscriptionsParams.fromRequest(request);
+ var subMap =
+ mapMap<FlutterService, List<String>, FlutterService, Set<String>>(
+ params.subscriptions,
+ valueCallback: (List<String> subscriptions) =>
+ subscriptions.toSet());
+ server.setFlutterSubscriptions(subMap);
+ sendResult(FlutterSetSubscriptionsResult());
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/flutter_set_widget_property_value.dart b/pkg/analysis_server/lib/src/handler/legacy/flutter_set_widget_property_value.dart
new file mode 100644
index 0000000..af10cce
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/flutter_set_widget_property_value.dart
@@ -0,0 +1,41 @@
+// 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 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+
+/// The handler for the `flutter.setWidgetPropertyValue` request.
+class FlutterSetWidgetPropertyValueHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ FlutterSetWidgetPropertyValueHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var params = FlutterSetWidgetPropertyValueParams.fromRequest(request);
+
+ var result = await server.flutterWidgetDescriptions.setPropertyValue(
+ params.id,
+ params.value,
+ );
+
+ var errorCode = result.errorCode;
+ if (errorCode != null) {
+ sendResponse(
+ Response(
+ request.id,
+ error: RequestError(errorCode, ''),
+ ),
+ );
+ }
+
+ sendResult(FlutterSetWidgetPropertyValueResult(
+ result.change!,
+ ));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
index cc96821..aaaefdeb 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/legacy_handler.dart
@@ -6,6 +6,7 @@
import 'package:_fe_analyzer_shared/src/scanner/errors.dart';
import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/protocol/protocol_internal.dart';
import 'package:analyzer/error/error.dart';
@@ -53,4 +54,9 @@
void sendResult(ResponseResult result) {
sendResponse(result.toResponse(request.id));
}
+
+ /// Send a notification built from the given [params].
+ void sendSearchResults(SearchResultsParams params) {
+ server.sendNotification(params.toNotification());
+ }
}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
new file mode 100644
index 0000000..ccdbbaf
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_element_references.dart
@@ -0,0 +1,55 @@
+// 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 'package:analysis_server/plugin/protocol/protocol_dart.dart' as protocol;
+import 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/search/element_references.dart';
+import 'package:analyzer/dart/element/element.dart';
+
+/// The handler for the `search.findElementReferences` request.
+class SearchFindElementReferencesHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchFindElementReferencesHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final searchEngine = server.searchEngine;
+ var params =
+ protocol.SearchFindElementReferencesParams.fromRequest(request);
+ var file = params.file;
+ // prepare element
+ var element = await server.getElementAtOffset(file, params.offset);
+ if (element is ImportElement) {
+ element = element.prefix;
+ }
+ if (element is FieldFormalParameterElement) {
+ element = element.field;
+ }
+ if (element is PropertyAccessorElement) {
+ element = element.variable;
+ }
+ // respond
+ var searchId = (server.nextSearchId++).toString();
+ var result = protocol.SearchFindElementReferencesResult();
+ if (element != null) {
+ result.id = searchId;
+ var withNullability = element.library?.isNonNullableByDefault ?? false;
+ result.element =
+ protocol.convertElement(element, withNullability: withNullability);
+ }
+ sendResult(result);
+ // search elements
+ if (element != null) {
+ var computer = ElementReferencesComputer(searchEngine);
+ var results = await computer.compute(element, params.includePotential);
+ sendSearchResults(
+ protocol.SearchResultsParams(searchId, results.toList(), true));
+ }
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_member_declarations.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_member_declarations.dart
new file mode 100644
index 0000000..d5ade7e
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_member_declarations.dart
@@ -0,0 +1,32 @@
+// 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 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/search/search_domain.dart';
+
+/// The handler for the `search.findMemberDeclarations` request.
+class SearchFindMemberDeclarationsHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchFindMemberDeclarationsHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final searchEngine = server.searchEngine;
+ var params =
+ protocol.SearchFindMemberDeclarationsParams.fromRequest(request);
+ await server.onAnalysisComplete;
+ // respond
+ var searchId = (server.nextSearchId++).toString();
+ sendResult(protocol.SearchFindMemberDeclarationsResult(searchId));
+ // search
+ var matches = await searchEngine.searchMemberDeclarations(params.name);
+ sendSearchResults(protocol.SearchResultsParams(
+ searchId, matches.map(SearchDomainHandler.toResult).toList(), true));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_member_references.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_member_references.dart
new file mode 100644
index 0000000..d460649
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_member_references.dart
@@ -0,0 +1,31 @@
+// 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 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/search/search_domain.dart';
+
+/// The handler for the `search.findMemberReferences` request.
+class SearchFindMemberReferencesHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchFindMemberReferencesHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final searchEngine = server.searchEngine;
+ var params = protocol.SearchFindMemberReferencesParams.fromRequest(request);
+ await server.onAnalysisComplete;
+ // respond
+ var searchId = (server.nextSearchId++).toString();
+ sendResult(protocol.SearchFindMemberReferencesResult(searchId));
+ // search
+ var matches = await searchEngine.searchMemberReferences(params.name);
+ sendSearchResults(protocol.SearchResultsParams(
+ searchId, matches.map(SearchDomainHandler.toResult).toList(), true));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_find_top_level_declarations.dart b/pkg/analysis_server/lib/src/handler/legacy/search_find_top_level_declarations.dart
new file mode 100644
index 0000000..b24980a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_find_top_level_declarations.dart
@@ -0,0 +1,42 @@
+// 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 'package:analysis_server/protocol/protocol.dart' as protocol;
+import 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/search/search_domain.dart';
+
+/// The handler for the `search.findTopLevelDeclarations` request.
+class SearchFindTopLevelDeclarationsHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchFindTopLevelDeclarationsHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final searchEngine = server.searchEngine;
+ var params =
+ protocol.SearchFindTopLevelDeclarationsParams.fromRequest(request);
+ try {
+ // validate the regex
+ RegExp(params.pattern);
+ } on FormatException catch (exception) {
+ server.sendResponse(protocol.Response.invalidParameter(
+ request, 'pattern', exception.message));
+ return;
+ }
+
+ await server.onAnalysisComplete;
+ // respond
+ var searchId = (server.nextSearchId++).toString();
+ sendResult(protocol.SearchFindTopLevelDeclarationsResult(searchId));
+ // search
+ var matches = await searchEngine.searchTopLevelDeclarations(params.pattern);
+ sendSearchResults(protocol.SearchResultsParams(
+ searchId, matches.map(SearchDomainHandler.toResult).toList(), true));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_get_element_declarations.dart b/pkg/analysis_server/lib/src/handler/legacy/search_get_element_declarations.dart
new file mode 100644
index 0000000..3e4c0fa
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_get_element_declarations.dart
@@ -0,0 +1,107 @@
+// 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 'package:analysis_server/protocol/protocol.dart' as protocol;
+import 'package:analysis_server/protocol/protocol.dart';
+import 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analyzer/src/dart/analysis/search.dart' as search;
+import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
+
+/// The handler for the `search.getElementDeclarations` request.
+class SearchGetElementDeclarationsHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchGetElementDeclarationsHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var params =
+ protocol.SearchGetElementDeclarationsParams.fromRequest(request);
+
+ RegExp? regExp;
+ var pattern = params.pattern;
+ if (pattern != null) {
+ try {
+ regExp = RegExp(pattern);
+ } on FormatException catch (exception) {
+ server.sendResponse(protocol.Response.invalidParameter(
+ request, 'pattern', exception.message));
+ return;
+ }
+ }
+
+ protocol.ElementKind getElementKind(search.DeclarationKind kind) {
+ switch (kind) {
+ case search.DeclarationKind.CLASS:
+ return protocol.ElementKind.CLASS;
+ case search.DeclarationKind.CLASS_TYPE_ALIAS:
+ return protocol.ElementKind.CLASS_TYPE_ALIAS;
+ case search.DeclarationKind.CONSTRUCTOR:
+ return protocol.ElementKind.CONSTRUCTOR;
+ case search.DeclarationKind.ENUM:
+ return protocol.ElementKind.ENUM;
+ case search.DeclarationKind.ENUM_CONSTANT:
+ return protocol.ElementKind.ENUM_CONSTANT;
+ case search.DeclarationKind.FIELD:
+ return protocol.ElementKind.FIELD;
+ case search.DeclarationKind.FUNCTION:
+ return protocol.ElementKind.FUNCTION;
+ case search.DeclarationKind.FUNCTION_TYPE_ALIAS:
+ return protocol.ElementKind.FUNCTION_TYPE_ALIAS;
+ case search.DeclarationKind.GETTER:
+ return protocol.ElementKind.GETTER;
+ case search.DeclarationKind.METHOD:
+ return protocol.ElementKind.METHOD;
+ case search.DeclarationKind.MIXIN:
+ return protocol.ElementKind.MIXIN;
+ case search.DeclarationKind.SETTER:
+ return protocol.ElementKind.SETTER;
+ case search.DeclarationKind.TYPE_ALIAS:
+ return protocol.ElementKind.TYPE_ALIAS;
+ case search.DeclarationKind.VARIABLE:
+ return protocol.ElementKind.TOP_LEVEL_VARIABLE;
+ default:
+ return protocol.ElementKind.CLASS;
+ }
+ }
+
+ if (!server.options.featureSet.completion) {
+ server.sendResponse(Response.unsupportedFeature(
+ request.id, 'Completion is not enabled.'));
+ return;
+ }
+
+ var workspaceSymbols = search.WorkspaceSymbols();
+ var analysisDrivers = server.driverMap.values.toList();
+ for (var analysisDriver in analysisDrivers) {
+ await analysisDriver.search.declarations(
+ workspaceSymbols, regExp, params.maxResults,
+ onlyForFile: params.file);
+ }
+
+ var declarations = workspaceSymbols.declarations;
+ var elementDeclarations = declarations.map((declaration) {
+ return protocol.ElementDeclaration(
+ declaration.name,
+ getElementKind(declaration.kind),
+ declaration.fileIndex,
+ declaration.offset,
+ declaration.line,
+ declaration.column,
+ declaration.codeOffset,
+ declaration.codeLength,
+ className: declaration.className,
+ mixinName: declaration.mixinName,
+ parameters: declaration.parameters);
+ }).toList();
+
+ server.sendResponse(protocol.SearchGetElementDeclarationsResult(
+ elementDeclarations, workspaceSymbols.files)
+ .toResponse(request.id));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/search_get_type_hierarchy.dart b/pkg/analysis_server/lib/src/handler/legacy/search_get_type_hierarchy.dart
new file mode 100644
index 0000000..36c039a
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/search_get_type_hierarchy.dart
@@ -0,0 +1,53 @@
+// 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 'package:analysis_server/protocol/protocol.dart' as protocol;
+import 'package:analysis_server/protocol/protocol_generated.dart' as protocol;
+import 'package:analysis_server/src/handler/legacy/legacy_handler.dart';
+import 'package:analysis_server/src/search/type_hierarchy.dart';
+
+/// The handler for the `search.getTypeHierarchy` request.
+class SearchGetTypeHierarchyHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ SearchGetTypeHierarchyHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final searchEngine = server.searchEngine;
+ var params = protocol.SearchGetTypeHierarchyParams.fromRequest(request);
+ var file = params.file;
+ // prepare element
+ var element = await server.getElementAtOffset(file, params.offset);
+ if (element == null) {
+ _sendTypeHierarchyNull(request);
+ return;
+ }
+ // maybe supertype hierarchy only
+ if (params.superOnly == true) {
+ var computer = TypeHierarchyComputer(searchEngine, element);
+ var items = computer.computeSuper();
+ var response =
+ protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
+ .toResponse(request.id);
+ server.sendResponse(response);
+ return;
+ }
+ // prepare type hierarchy
+ var computer = TypeHierarchyComputer(searchEngine, element);
+ var items = await computer.compute();
+ var response = protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
+ .toResponse(request.id);
+ server.sendResponse(response);
+ }
+
+ void _sendTypeHierarchyNull(protocol.Request request) {
+ var response =
+ protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
+ server.sendResponse(response);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/search/search_domain.dart b/pkg/analysis_server/lib/src/search/search_domain.dart
index da23c1a..ab903d63 100644
--- a/pkg/analysis_server/lib/src/search/search_domain.dart
+++ b/pkg/analysis_server/lib/src/search/search_domain.dart
@@ -5,14 +5,14 @@
import 'package:analysis_server/protocol/protocol.dart';
import 'package:analysis_server/protocol/protocol_constants.dart';
import 'package:analysis_server/src/analysis_server.dart';
-import 'package:analysis_server/src/protocol/protocol_internal.dart'
- show ResponseResult;
+import 'package:analysis_server/src/handler/legacy/search_find_element_references.dart';
+import 'package:analysis_server/src/handler/legacy/search_find_member_declarations.dart';
+import 'package:analysis_server/src/handler/legacy/search_find_member_references.dart';
+import 'package:analysis_server/src/handler/legacy/search_find_top_level_declarations.dart';
+import 'package:analysis_server/src/handler/legacy/search_get_element_declarations.dart';
+import 'package:analysis_server/src/handler/legacy/search_get_type_hierarchy.dart';
import 'package:analysis_server/src/protocol_server.dart' as protocol;
-import 'package:analysis_server/src/search/element_references.dart';
-import 'package:analysis_server/src/search/type_hierarchy.dart';
import 'package:analysis_server/src/services/search/search_engine.dart';
-import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/src/dart/analysis/search.dart' as search;
import 'package:analyzer/src/utilities/cancellation.dart';
/// Instances of the class [SearchDomainHandler] implement a [RequestHandler]
@@ -21,235 +21,39 @@
/// The analysis server that is using this handler to process requests.
final AnalysisServer server;
- /// The next search response id.
- int _nextSearchId = 0;
-
/// Initialize a newly created handler to handle requests for the given
/// [server].
SearchDomainHandler(this.server);
- Future<void> findElementReferences(protocol.Request request) async {
- final searchEngine = server.searchEngine;
- var params =
- protocol.SearchFindElementReferencesParams.fromRequest(request);
- var file = params.file;
- // prepare element
- var element = await server.getElementAtOffset(file, params.offset);
- if (element is ImportElement) {
- element = element.prefix;
- }
- if (element is FieldFormalParameterElement) {
- element = element.field;
- }
- if (element is PropertyAccessorElement) {
- element = element.variable;
- }
- // respond
- var searchId = (_nextSearchId++).toString();
- var result = protocol.SearchFindElementReferencesResult();
- if (element != null) {
- result.id = searchId;
- var withNullability = element.library?.isNonNullableByDefault ?? false;
- result.element =
- protocol.convertElement(element, withNullability: withNullability);
- }
- _sendSearchResult(request, result);
- // search elements
- if (element != null) {
- var computer = ElementReferencesComputer(searchEngine);
- var results = await computer.compute(element, params.includePotential);
- _sendSearchNotification(searchId, true, results);
- }
- }
-
- Future findMemberDeclarations(protocol.Request request) async {
- final searchEngine = server.searchEngine;
- var params =
- protocol.SearchFindMemberDeclarationsParams.fromRequest(request);
- await server.onAnalysisComplete;
- // respond
- var searchId = (_nextSearchId++).toString();
- _sendSearchResult(
- request, protocol.SearchFindMemberDeclarationsResult(searchId));
- // search
- var matches = await searchEngine.searchMemberDeclarations(params.name);
- _sendSearchNotification(searchId, true, matches.map(toResult));
- }
-
- Future findMemberReferences(protocol.Request request) async {
- final searchEngine = server.searchEngine;
- var params = protocol.SearchFindMemberReferencesParams.fromRequest(request);
- await server.onAnalysisComplete;
- // respond
- var searchId = (_nextSearchId++).toString();
- _sendSearchResult(
- request, protocol.SearchFindMemberReferencesResult(searchId));
- // search
- var matches = await searchEngine.searchMemberReferences(params.name);
- _sendSearchNotification(searchId, true, matches.map(toResult));
- }
-
- Future findTopLevelDeclarations(protocol.Request request) async {
- final searchEngine = server.searchEngine;
- var params =
- protocol.SearchFindTopLevelDeclarationsParams.fromRequest(request);
- try {
- // validate the regex
- RegExp(params.pattern);
- } on FormatException catch (exception) {
- server.sendResponse(protocol.Response.invalidParameter(
- request, 'pattern', exception.message));
- return;
- }
-
- await server.onAnalysisComplete;
- // respond
- var searchId = (_nextSearchId++).toString();
- _sendSearchResult(
- request, protocol.SearchFindTopLevelDeclarationsResult(searchId));
- // search
- var matches = await searchEngine.searchTopLevelDeclarations(params.pattern);
- _sendSearchNotification(searchId, true, matches.map(toResult));
- }
-
- /// Implement the `search.getDeclarations` request.
- Future getDeclarations(protocol.Request request) async {
- var params =
- protocol.SearchGetElementDeclarationsParams.fromRequest(request);
-
- RegExp? regExp;
- var pattern = params.pattern;
- if (pattern != null) {
- try {
- regExp = RegExp(pattern);
- } on FormatException catch (exception) {
- server.sendResponse(protocol.Response.invalidParameter(
- request, 'pattern', exception.message));
- return;
- }
- }
-
- protocol.ElementKind getElementKind(search.DeclarationKind kind) {
- switch (kind) {
- case search.DeclarationKind.CLASS:
- return protocol.ElementKind.CLASS;
- case search.DeclarationKind.CLASS_TYPE_ALIAS:
- return protocol.ElementKind.CLASS_TYPE_ALIAS;
- case search.DeclarationKind.CONSTRUCTOR:
- return protocol.ElementKind.CONSTRUCTOR;
- case search.DeclarationKind.ENUM:
- return protocol.ElementKind.ENUM;
- case search.DeclarationKind.ENUM_CONSTANT:
- return protocol.ElementKind.ENUM_CONSTANT;
- case search.DeclarationKind.FIELD:
- return protocol.ElementKind.FIELD;
- case search.DeclarationKind.FUNCTION:
- return protocol.ElementKind.FUNCTION;
- case search.DeclarationKind.FUNCTION_TYPE_ALIAS:
- return protocol.ElementKind.FUNCTION_TYPE_ALIAS;
- case search.DeclarationKind.GETTER:
- return protocol.ElementKind.GETTER;
- case search.DeclarationKind.METHOD:
- return protocol.ElementKind.METHOD;
- case search.DeclarationKind.MIXIN:
- return protocol.ElementKind.MIXIN;
- case search.DeclarationKind.SETTER:
- return protocol.ElementKind.SETTER;
- case search.DeclarationKind.TYPE_ALIAS:
- return protocol.ElementKind.TYPE_ALIAS;
- case search.DeclarationKind.VARIABLE:
- return protocol.ElementKind.TOP_LEVEL_VARIABLE;
- default:
- return protocol.ElementKind.CLASS;
- }
- }
-
- if (!server.options.featureSet.completion) {
- server.sendResponse(Response.unsupportedFeature(
- request.id, 'Completion is not enabled.'));
- return;
- }
-
- var workspaceSymbols = search.WorkspaceSymbols();
- var analysisDrivers = server.driverMap.values.toList();
- for (var analysisDriver in analysisDrivers) {
- await analysisDriver.search.declarations(
- workspaceSymbols, regExp, params.maxResults,
- onlyForFile: params.file);
- }
-
- var declarations = workspaceSymbols.declarations;
- var elementDeclarations = declarations.map((declaration) {
- return protocol.ElementDeclaration(
- declaration.name,
- getElementKind(declaration.kind),
- declaration.fileIndex,
- declaration.offset,
- declaration.line,
- declaration.column,
- declaration.codeOffset,
- declaration.codeLength,
- className: declaration.className,
- mixinName: declaration.mixinName,
- parameters: declaration.parameters);
- }).toList();
-
- server.sendResponse(protocol.SearchGetElementDeclarationsResult(
- elementDeclarations, workspaceSymbols.files)
- .toResponse(request.id));
- }
-
- /// Implement the `search.getTypeHierarchy` request.
- Future getTypeHierarchy(protocol.Request request) async {
- final searchEngine = server.searchEngine;
- var params = protocol.SearchGetTypeHierarchyParams.fromRequest(request);
- var file = params.file;
- // prepare element
- var element = await server.getElementAtOffset(file, params.offset);
- if (element == null) {
- _sendTypeHierarchyNull(request);
- return;
- }
- // maybe supertype hierarchy only
- if (params.superOnly == true) {
- var computer = TypeHierarchyComputer(searchEngine, element);
- var items = computer.computeSuper();
- var response =
- protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
- .toResponse(request.id);
- server.sendResponse(response);
- return;
- }
- // prepare type hierarchy
- var computer = TypeHierarchyComputer(searchEngine, element);
- var items = await computer.compute();
- var response = protocol.SearchGetTypeHierarchyResult(hierarchyItems: items)
- .toResponse(request.id);
- server.sendResponse(response);
- }
-
@override
protocol.Response? handleRequest(
protocol.Request request, CancellationToken cancellationToken) {
try {
var requestName = request.method;
if (requestName == SEARCH_REQUEST_FIND_ELEMENT_REFERENCES) {
- findElementReferences(request);
+ SearchFindElementReferencesHandler(server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_MEMBER_DECLARATIONS) {
- findMemberDeclarations(request);
+ SearchFindMemberDeclarationsHandler(server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_MEMBER_REFERENCES) {
- findMemberReferences(request);
+ SearchFindMemberReferencesHandler(server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS) {
- findTopLevelDeclarations(request);
+ SearchFindTopLevelDeclarationsHandler(
+ server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_GET_ELEMENT_DECLARATIONS) {
- getDeclarations(request);
+ SearchGetElementDeclarationsHandler(server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
} else if (requestName == SEARCH_REQUEST_GET_TYPE_HIERARCHY) {
- getTypeHierarchy(request);
+ SearchGetTypeHierarchyHandler(server, request, cancellationToken)
+ .handle();
return protocol.Response.DELAYED_RESPONSE;
}
} on protocol.RequestFailure catch (exception) {
@@ -258,25 +62,6 @@
return null;
}
- void _sendSearchNotification(
- String searchId, bool isLast, Iterable<protocol.SearchResult> results) {
- server.sendNotification(
- protocol.SearchResultsParams(searchId, results.toList(), isLast)
- .toNotification());
- }
-
- /// Send a search response with the given [result] to the given [request].
- void _sendSearchResult(protocol.Request request, ResponseResult result) {
- var response = result.toResponse(request.id);
- server.sendResponse(response);
- }
-
- void _sendTypeHierarchyNull(protocol.Request request) {
- var response =
- protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
- server.sendResponse(response);
- }
-
static protocol.SearchResult toResult(SearchMatch match) {
return protocol.newSearchResult_fromMatch(match);
}
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_super_parameters.dart b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_super_parameters.dart
index 7401c0c..03a314a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/convert_to_super_parameters.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/convert_to_super_parameters.dart
@@ -333,8 +333,9 @@
bool _nullInitializer(
FormalParameter parameter, ParameterElement superParameter) {
return parameter is DefaultFormalParameter &&
+ !parameter.isRequired &&
parameter.defaultValue == null &&
- superParameter.defaultValueCode != null;
+ superParameter.hasDefaultValue;
}
/// Return the parameter corresponding to the [expression], or `null` if the
diff --git a/pkg/analysis_server/lib/src/services/flutter/class_description.dart b/pkg/analysis_server/lib/src/services/flutter/class_description.dart
index b37c741..a592206 100644
--- a/pkg/analysis_server/lib/src/services/flutter/class_description.dart
+++ b/pkg/analysis_server/lib/src/services/flutter/class_description.dart
@@ -64,7 +64,7 @@
if (constructor == null) return null;
for (var parameter in constructor.parameters) {
- if (parameter.isNotOptional || parameter.hasRequired) {
+ if (parameter.isRequired || parameter.hasRequired) {
return null;
}
}
diff --git a/pkg/analysis_server/test/src/services/correction/assist/convert_to_super_parameters_test.dart b/pkg/analysis_server/test/src/services/correction/assist/convert_to_super_parameters_test.dart
index 1af8888..70b834d 100644
--- a/pkg/analysis_server/test/src/services/correction/assist/convert_to_super_parameters_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/assist/convert_to_super_parameters_test.dart
@@ -579,6 +579,27 @@
''');
}
+ Future<void> test_named_required() async {
+ await resolveTestCode('''
+class A {
+ int x;
+ A({this.x = 0});
+}
+class B extends A {
+ B({required int x}) : super(x: x);
+}
+''');
+ await assertHasAssistAt('B(', '''
+class A {
+ int x;
+ A({this.x = 0});
+}
+class B extends A {
+ B({required super.x});
+}
+''');
+ }
+
Future<void> test_namedConstructor() async {
await resolveTestCode('''
class A {
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 85f3551..3022ce6 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 4.1.0-dev
+* Deprecated `ParameterElement.isNotOptional`, use `isRequired` instead.
+
## 4.0.0
* Removed deprecated `UriKind` and `Source.uriKind`.
* Removed deprecated `LibraryElement.hasExtUri`.
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 1246953..97af71b 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -1528,7 +1528,7 @@
/// meaning of this getter. The parameter `{@required int x}` will return
/// `false` and the parameter `{@required required int x}` will return
/// `true`
- // TODO(brianwilkerson) Rename this to `isRequired`.
+ @Deprecated('Use isRequired instead')
bool get isNotOptional;
/// Return `true` if this parameter is an optional parameter. Optional
@@ -1552,6 +1552,15 @@
/// parameters can either be required or optional.
bool get isPositional;
+ /// Return `true` if this parameter is either a required positional
+ /// parameter, or a named parameter with the `required` keyword.
+ ///
+ /// Note: the presence or absence of the `@required` annotation does not
+ /// change the meaning of this getter. The parameter `{@required int x}`
+ /// will return `false` and the parameter `{@required required int x}`
+ /// will return `true`.
+ bool get isRequired;
+
/// Return `true` if this parameter is both a required and named parameter.
/// Named parameters that are annotated with the `@required` annotation are
/// considered optional. Named parameters that are annotated with the
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 5e40995..97707e5 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -84,7 +84,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 215;
+ static const int DATA_VERSION = 216;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 2815dbc..a0f91f2 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -41,6 +41,7 @@
import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
import 'package:analyzer/src/summary2/bundle_reader.dart';
import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:collection/collection.dart';
@@ -77,6 +78,9 @@
List<InterfaceType>? Function(AbstractClassElementImpl)?
mixinInferenceCallback;
+ /// Errors registered while applying macros to this element.
+ List<MacroApplicationError> macroApplicationErrors = [];
+
/// Initialize a newly created class element to have the given [name] at the
/// given [offset] in the file that contains the declaration of this element.
AbstractClassElementImpl(String name, int offset) : super(name, offset);
@@ -1449,7 +1453,7 @@
}
// no required parameters
for (ParameterElement parameter in parameters) {
- if (parameter.isNotOptional) {
+ if (parameter.isRequired) {
return false;
}
}
@@ -4876,6 +4880,7 @@
@override
bool get isNamed => parameterKind.isNamed;
+ @Deprecated('Use isRequired instead')
@override
bool get isNotOptional => parameterKind.isRequired;
@@ -4892,6 +4897,9 @@
bool get isPositional => parameterKind.isPositional;
@override
+ bool get isRequired => parameterKind.isRequired;
+
+ @override
bool get isRequiredNamed => parameterKind.isRequiredNamed;
@override
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index c675895..544a143 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -802,14 +802,14 @@
while (fIndex < fParameters.length) {
var fParameter = fParameters[fIndex++];
- if (fParameter.isNotOptional) {
+ if (fParameter.isRequired) {
return _interfaceTypeFunctionNone;
}
}
while (gIndex < gParameters.length) {
var gParameter = gParameters[gIndex++];
- if (gParameter.isNotOptional) {
+ if (gParameter.isRequired) {
return _interfaceTypeFunctionNone;
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/subtype.dart b/pkg/analyzer/lib/src/dart/element/subtype.dart
index 9ff6e69..8bec0b3 100644
--- a/pkg/analyzer/lib/src/dart/element/subtype.dart
+++ b/pkg/analyzer/lib/src/dart/element/subtype.dart
@@ -419,7 +419,7 @@
// The supertype must provide all required parameters to the subtype.
while (fIndex < fParameters.length) {
var fParameter = fParameters[fIndex++];
- if (fParameter.isNotOptional) {
+ if (fParameter.isRequired) {
return false;
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
index eed4d97..f5b011e 100644
--- a/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_constraint_gatherer.dart
@@ -506,7 +506,7 @@
// The supertype must provide all required parameters to the subtype.
while (fIndex < fParameters.length) {
var fParameter = fParameters[fIndex++];
- if (fParameter.isNotOptional) {
+ if (fParameter.isRequired) {
_constraints.length = rewind;
return false;
}
diff --git a/pkg/analyzer/lib/src/summary2/bundle_reader.dart b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
index 4fdbad0..b24d7dc 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_reader.dart
@@ -24,6 +24,7 @@
import 'package:analyzer/src/summary2/element_flags.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:pub_semver/pub_semver.dart';
@@ -510,6 +511,9 @@
);
element.setLinkedData(reference, linkedData);
ClassElementFlags.read(_reader, element);
+ element.macroApplicationErrors = _reader.readTypedList(
+ () => MacroApplicationError(_reader),
+ );
element.typeParameters = _readTypeParameters();
diff --git a/pkg/analyzer/lib/src/summary2/bundle_writer.dart b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
index 7a4cbf6..80f6e6b 100644
--- a/pkg/analyzer/lib/src/summary2/bundle_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/bundle_writer.dart
@@ -18,6 +18,7 @@
import 'package:analyzer/src/summary2/ast_binary_writer.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
import 'package:analyzer/src/summary2/element_flags.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/summary2/reference.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:collection/collection.dart';
@@ -126,6 +127,11 @@
_sink._writeStringReference(element.name);
ClassElementFlags.write(_sink, element);
+ _sink.writeList<MacroApplicationError>(
+ element.macroApplicationErrors,
+ (x) => x.write(_sink),
+ );
+
_resolutionSink._writeAnnotationList(element.metadata);
_writeTypeParameters(element.typeParameters, () {
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 7d8f2ba..9f618ae 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -13,6 +13,7 @@
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/library_builder.dart';
import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
class LibraryMacroApplier {
final LibraryBuilder libraryBuilder;
@@ -27,10 +28,13 @@
var macroResults = <macro.MacroExecutionResult>[];
for (var unitElement in libraryBuilder.element.units) {
for (var classElement in unitElement.classes) {
+ classElement as ClassElementImpl;
var classNode = libraryBuilder.linker.elementNodes[classElement];
// TODO(scheglov) support other declarations
if (classNode is ClassDeclaration) {
- for (var annotation in classNode.metadata) {
+ var annotationList = classNode.metadata;
+ for (var i = 0; i < annotationList.length; i++) {
+ final annotation = annotationList[i];
var annotationNameNode = annotation.name;
var argumentsNode = annotation.arguments;
if (annotationNameNode is SimpleIdentifier &&
@@ -49,14 +53,30 @@
if (getter is ClassElementImpl && getter.isMacro) {
var macroExecutor = importedLibrary.bundleMacroExecutor;
if (macroExecutor != null) {
- var macroResult = await _runSingleMacro(
- macroExecutor,
- getClassDeclaration(classNode),
- getter,
- _buildArguments(argumentsNode),
- );
- if (macroResult.isNotEmpty) {
- macroResults.add(macroResult);
+ try {
+ final arguments = _buildArguments(
+ annotationIndex: i,
+ node: argumentsNode,
+ );
+ final macroResult = await _runSingleMacro(
+ macroExecutor,
+ getClassDeclaration(classNode),
+ getter,
+ arguments,
+ );
+ if (macroResult.isNotEmpty) {
+ macroResults.add(macroResult);
+ }
+ } on MacroApplicationError catch (e) {
+ classElement.macroApplicationErrors.add(e);
+ } catch (e, stackTrace) {
+ classElement.macroApplicationErrors.add(
+ UnknownMacroApplicationError(
+ annotationIndex: i,
+ stackTrace: stackTrace.toString(),
+ message: e.toString(),
+ ),
+ );
}
}
}
@@ -112,15 +132,23 @@
return await macroInstance.executeTypesPhase();
}
- static macro.Arguments _buildArguments(ArgumentList node) {
+ static macro.Arguments _buildArguments({
+ required int annotationIndex,
+ required ArgumentList node,
+ }) {
final positional = <Object?>[];
final named = <String, Object?>{};
- for (final argument in node.arguments) {
+ for (var i = 0; i < node.arguments.length; ++i) {
+ final argument = node.arguments[i];
+ final evaluation = _ArgumentEvaluation(
+ annotationIndex: annotationIndex,
+ argumentIndex: i,
+ );
if (argument is NamedExpression) {
- final value = _evaluateArgument(argument.expression);
+ final value = evaluation.evaluate(argument.expression);
named[argument.name.label.name] = value;
} else {
- final value = _evaluateArgument(argument);
+ final value = evaluation.evaluate(argument);
positional.add(value);
}
}
@@ -199,10 +227,22 @@
return const [];
}
}
+}
- static Object? _evaluateArgument(Expression node) {
+/// Helper class for evaluating arguments for a single constructor based
+/// macro application.
+class _ArgumentEvaluation {
+ final int annotationIndex;
+ final int argumentIndex;
+
+ _ArgumentEvaluation({
+ required this.annotationIndex,
+ required this.argumentIndex,
+ });
+
+ Object? evaluate(Expression node) {
if (node is AdjacentStrings) {
- return node.strings.map(_evaluateArgument).join('');
+ return node.strings.map(evaluate).join('');
} else if (node is BooleanLiteral) {
return node.value;
} else if (node is DoubleLiteral) {
@@ -210,12 +250,12 @@
} else if (node is IntegerLiteral) {
return node.value;
} else if (node is ListLiteral) {
- return node.elements.cast<Expression>().map(_evaluateArgument).toList();
+ return node.elements.cast<Expression>().map(evaluate).toList();
} else if (node is NullLiteral) {
return null;
} else if (node is PrefixExpression &&
node.operator.type == TokenType.MINUS) {
- final operandValue = _evaluateArgument(node.operand);
+ final operandValue = evaluate(node.operand);
if (operandValue is double) {
return -operandValue;
} else if (operandValue is int) {
@@ -225,19 +265,25 @@
final result = <Object?, Object?>{};
for (final element in node.elements) {
if (element is! MapLiteralEntry) {
- throw ArgumentError(
- 'Not supported: (${element.runtimeType}) $element',
- );
+ _throwError(element, 'MapLiteralEntry expected');
}
- final key = _evaluateArgument(element.key);
- final value = _evaluateArgument(element.value);
+ final key = evaluate(element.key);
+ final value = evaluate(element.value);
result[key] = value;
}
return result;
} else if (node is SimpleStringLiteral) {
return node.value;
}
- throw ArgumentError('Not supported: (${node.runtimeType}) $node');
+ _throwError(node, 'Not supported: ${node.runtimeType}');
+ }
+
+ Never _throwError(AstNode node, String message) {
+ throw ArgumentMacroApplicationError(
+ annotationIndex: annotationIndex,
+ argumentIndex: argumentIndex,
+ message: message,
+ );
}
}
diff --git a/pkg/analyzer/lib/src/summary2/macro_application_error.dart b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
new file mode 100644
index 0000000..a7b9018
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
@@ -0,0 +1,130 @@
+// 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 'package:analyzer/src/summary2/data_reader.dart';
+import 'package:analyzer/src/summary2/data_writer.dart';
+import 'package:meta/meta.dart';
+
+/// An error during evaluating annotation arguments.
+class ArgumentMacroApplicationError extends MacroApplicationError {
+ final int argumentIndex;
+ final String message;
+
+ ArgumentMacroApplicationError({
+ required int annotationIndex,
+ required this.argumentIndex,
+ required this.message,
+ }) : super._(
+ annotationIndex: annotationIndex,
+ kind: MacroApplicationErrorKind.argument,
+ );
+
+ factory ArgumentMacroApplicationError._read(
+ SummaryDataReader reader,
+ int annotationIndex,
+ ) {
+ return ArgumentMacroApplicationError(
+ annotationIndex: annotationIndex,
+ argumentIndex: reader.readUInt30(),
+ message: reader.readStringUtf8(),
+ );
+ }
+
+ @override
+ String toStringForTest() {
+ return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)';
+ }
+
+ @override
+ void write(BufferedSink sink) {
+ super.write(sink);
+ sink.writeUInt30(argumentIndex);
+ sink.writeStringUtf8(message);
+ }
+}
+
+/// An error that happened while applying a macro.
+abstract class MacroApplicationError {
+ /// The index of the annotation of the element that turned out to be a
+ /// macro application. Can be used to associate the error with the location.
+ final int annotationIndex;
+
+ final MacroApplicationErrorKind kind;
+
+ factory MacroApplicationError(SummaryDataReader reader) {
+ final annotationIndex = reader.readUInt30();
+ final kind = MacroApplicationErrorKind.values[reader.readUInt30()];
+ switch (kind) {
+ case MacroApplicationErrorKind.argument:
+ return ArgumentMacroApplicationError._read(
+ reader,
+ annotationIndex,
+ );
+ case MacroApplicationErrorKind.unknown:
+ return UnknownMacroApplicationError._read(
+ reader,
+ annotationIndex,
+ );
+ }
+ }
+
+ MacroApplicationError._({
+ required this.annotationIndex,
+ required this.kind,
+ });
+
+ String toStringForTest();
+
+ @mustCallSuper
+ void write(BufferedSink sink) {
+ sink.writeUInt30(annotationIndex);
+ sink.writeUInt30(kind.index);
+ }
+}
+
+enum MacroApplicationErrorKind {
+ /// An error while evaluating arguments.
+ argument,
+
+ /// Any other exception that happened during application.
+ unknown,
+}
+
+/// Any other exception that happened during macro application.
+class UnknownMacroApplicationError extends MacroApplicationError {
+ final String message;
+ final String stackTrace;
+
+ UnknownMacroApplicationError({
+ required int annotationIndex,
+ required this.message,
+ required this.stackTrace,
+ }) : super._(
+ annotationIndex: annotationIndex,
+ kind: MacroApplicationErrorKind.unknown,
+ );
+
+ factory UnknownMacroApplicationError._read(
+ SummaryDataReader reader,
+ int annotationIndex,
+ ) {
+ return UnknownMacroApplicationError(
+ annotationIndex: annotationIndex,
+ message: reader.readStringUtf8(),
+ stackTrace: reader.readStringUtf8(),
+ );
+ }
+
+ @override
+ String toStringForTest() {
+ return 'Unknown(annotation: $annotationIndex)';
+ }
+
+ @override
+ void write(BufferedSink sink) {
+ super.write(sink);
+ sink.writeStringUtf8(message);
+ sink.writeStringUtf8(stackTrace);
+ }
+}
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 9cf0d35..5286115 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
name: analyzer
-version: 4.0.0
+version: 4.1.0-dev
description: This package provides a library that performs static analysis of Dart code.
homepage: https://github.com/dart-lang/sdk/tree/main/pkg/analyzer
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index cfde4cd..51443a5 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -75,6 +75,18 @@
);
}
+ test_arguments_error() async {
+ await _assertTypesPhaseArgumentsText(
+ fields: {
+ 'foo': 'Object',
+ 'bar': 'Object',
+ },
+ constructorParametersCode: '(this.foo, this.bar)',
+ argumentsCode: '(0, const Object())',
+ expectedErrors: 'Argument(annotation: 0, argument: 1)',
+ );
+ }
+
test_arguments_typesPhase_kind_optionalNamed() async {
await _assertTypesPhaseArgumentsText(
fields: {
@@ -456,7 +468,8 @@
required Map<String, String> fields,
required String constructorParametersCode,
required String argumentsCode,
- required String expected,
+ String? expected,
+ String? expectedErrors,
}) async {
final dumpCode = fields.keys.map((name) {
return "$name: \$$name\\\\n";
@@ -491,15 +504,26 @@
{'package:test/arguments_text.dart'}
]);
- final x = library.parts.single.topLevelVariables.single;
- expect(x.name, 'x');
- x as ConstTopLevelVariableElementImpl;
- final actual = (x.constantInitializer as SimpleStringLiteral).value;
+ if (expected != null) {
+ final x = library.parts.single.topLevelVariables.single;
+ expect(x.name, 'x');
+ x as ConstTopLevelVariableElementImpl;
+ final actual = (x.constantInitializer as SimpleStringLiteral).value;
- if (actual != expected) {
- print(actual);
+ if (actual != expected) {
+ print(actual);
+ }
+ expect(actual, expected);
+ } else if (expectedErrors != null) {
+ var A = library.definingCompilationUnit.getType('A');
+ A as ClassElementImpl;
+ expect(
+ A.macroApplicationErrors.map((e) => e.toStringForTest()).join('\n'),
+ expectedErrors,
+ );
+ } else {
+ fail("Either 'expected' or 'expectedErrors' must be provided.");
}
- expect(actual, expected);
}
/// Assert that the textual dump of the introspection information for
diff --git a/pkg/compiler/lib/src/deferred_load/deferred_load.dart b/pkg/compiler/lib/src/deferred_load/deferred_load.dart
index 4bbfbdc..ab74ec9 100644
--- a/pkg/compiler/lib/src/deferred_load/deferred_load.dart
+++ b/pkg/compiler/lib/src/deferred_load/deferred_load.dart
@@ -317,7 +317,8 @@
/// A sentinel used only by the [ImportSet] corresponding to the
/// [_mainOutputUnit].
- final ImportEntity _mainImport = ImportEntity(true, 'main#main', null, null);
+ final ImportEntity _mainImport =
+ ImportEntity(true, 'main#main', Uri(), Uri());
/// A set containing (eventually) all output units that will result from the
/// program.
diff --git a/pkg/compiler/lib/src/elements/entities.dart b/pkg/compiler/lib/src/elements/entities.dart
index e0f8380..788a702 100644
--- a/pkg/compiler/lib/src/elements/entities.dart
+++ b/pkg/compiler/lib/src/elements/entities.dart
@@ -6,79 +6,13 @@
library entities;
-import 'package:front_end/src/api_unstable/dart2js.dart' show AsyncModifier;
-
-import '../common.dart';
import '../serialization/serialization.dart';
import '../universe/call_structure.dart' show CallStructure;
import '../util/util.dart';
-import 'names.dart';
import 'types.dart';
-/// Abstract interface for entities.
-///
-/// Implement this directly if the entity is not a Dart language entity.
-/// Entities defined within the Dart language should implement [Element].
-///
-/// For instance, the JavaScript backend need to create synthetic variables for
-/// calling intercepted classes and such variables do not correspond to an
-/// entity in the Dart source code nor in the terminology of the Dart language
-/// and should therefore implement [Entity] directly.
-abstract class Entity implements Spannable {
- String get name;
-}
-
-/// Stripped down super interface for library like entities.
-///
-/// Currently only [LibraryElement] but later also kernel based Dart classes
-/// and/or Dart-in-JS classes.
-abstract class LibraryEntity extends Entity {
- /// Return the canonical uri that identifies this library.
- Uri /*!*/ get canonicalUri;
-
- /// Returns whether or not this library has opted into null safety.
- bool get isNonNullableByDefault;
-}
-
-/// Stripped down super interface for import entities.
-///
-/// The [name] property corresponds to the prefix name, if any.
-class ImportEntity {
- final String /*?*/ name;
-
- /// The canonical URI of the library where this import occurs
- /// (where the import is declared).
- final Uri enclosingLibraryUri;
-
- /// Whether the import is a deferred import.
- final bool isDeferred;
-
- /// The target import URI.
- final Uri uri;
-
- ImportEntity(this.isDeferred, this.name, this.uri, this.enclosingLibraryUri);
-
- @override
- String toString() => 'import($name:${isDeferred ? ' deferred' : ''})';
-}
-
-/// Stripped down super interface for class like entities.
-///
-/// Currently only [ClassElement] but later also kernel based Dart classes
-/// and/or Dart-in-JS classes.
-abstract class ClassEntity extends Entity {
- /// If this is a normal class, the enclosing library for this class. If this
- /// is a closure class, the enclosing class of the closure for which it was
- /// created.
- LibraryEntity get library;
-
- /// Whether this is a synthesized class for a closurized method or local
- /// function.
- bool get isClosure;
-
- /// Whether this is an abstract class.
- bool get isAbstract;
-}
+import 'entities_migrated.dart';
+export 'entities_migrated.dart';
abstract class TypeVariableEntity extends Entity {
/// The class or generic method that declared this type variable.
@@ -89,66 +23,6 @@
int get index;
}
-/// Stripped down super interface for member like entities, that is,
-/// constructors, methods, fields etc.
-///
-/// Currently only [MemberElement] but later also kernel based Dart members
-/// and/or Dart-in-JS properties.
-abstract class MemberEntity extends Entity {
- /// The [Name] of member which takes privacy and getter/setter naming into
- /// account.
- Name get memberName;
-
- /// Whether this is a member of a library.
- bool get isTopLevel;
-
- /// Whether this is a static member of a class.
- bool get isStatic;
-
- /// Whether this is an instance member of a class.
- bool get isInstanceMember;
-
- /// Whether this is a constructor.
- bool get isConstructor;
-
- /// Whether this is a field.
- bool get isField;
-
- /// Whether this is a normal method (neither constructor, getter or setter)
- /// or operator method.
- bool get isFunction;
-
- /// Whether this is a getter.
- bool get isGetter;
-
- /// Whether this is a setter.
- bool get isSetter;
-
- /// Whether this member is assignable, i.e. a non-final, non-const field.
- bool /*!*/ get isAssignable;
-
- /// Whether this member is constant, i.e. a constant field or constructor.
- bool /*!*/ get isConst;
-
- /// Whether this member is abstract, i.e. an abstract method, getter or
- /// setter.
- bool /*!*/ get isAbstract;
-
- /// The enclosing class if this is a constructor, instance member or
- /// static member of a class.
- ClassEntity get enclosingClass;
-
- /// The enclosing library if this is a library member, otherwise the
- /// enclosing library of the [enclosingClass].
- LibraryEntity get library;
-}
-
-/// Stripped down super interface for field like entities.
-///
-/// Currently only [FieldElement] but later also kernel based Dart fields
-/// and/or Dart-in-JS field-like properties.
-abstract class FieldEntity extends MemberEntity {}
-
/// Stripped down super interface for function like entities.
///
/// Currently only [MethodElement] but later also kernel based Dart constructors
@@ -165,61 +39,6 @@
AsyncMarker get asyncMarker;
}
-/// Enum for the synchronous/asynchronous function body modifiers.
-class AsyncMarker {
- /// The default function body marker.
- static const AsyncMarker SYNC = AsyncMarker._(AsyncModifier.Sync);
-
- /// The `sync*` function body marker.
- static const AsyncMarker SYNC_STAR =
- AsyncMarker._(AsyncModifier.SyncStar, isYielding: true);
-
- /// The `async` function body marker.
- static const AsyncMarker ASYNC =
- AsyncMarker._(AsyncModifier.Async, isAsync: true);
-
- /// The `async*` function body marker.
- static const AsyncMarker ASYNC_STAR =
- AsyncMarker._(AsyncModifier.AsyncStar, isAsync: true, isYielding: true);
-
- /// Is `true` if this marker defines the function body to have an
- /// asynchronous result, that is, either a [Future] or a [Stream].
- final bool isAsync;
-
- /// Is `true` if this marker defines the function body to have a plural
- /// result, that is, either an [Iterable] or a [Stream].
- final bool isYielding;
-
- final AsyncModifier asyncParserState;
-
- const AsyncMarker._(this.asyncParserState,
- {this.isAsync = false, this.isYielding = false});
-
- @override
- String toString() {
- return '${isAsync ? 'async' : 'sync'}${isYielding ? '*' : ''}';
- }
-
- /// Canonical list of marker values.
- ///
- /// Added to make [AsyncMarker] enum-like.
- static const List<AsyncMarker> values = <AsyncMarker>[
- SYNC,
- SYNC_STAR,
- ASYNC,
- ASYNC_STAR
- ];
-
- /// Index to this marker within [values].
- ///
- /// Added to make [AsyncMarker] enum-like.
- int get index => values.indexOf(this);
-}
-
-/// Values for variance annotations.
-/// This needs to be kept in sync with values of `Variance` in `dart:_rti`.
-enum Variance { legacyCovariant, covariant, contravariant, invariant }
-
/// Stripped down super interface for constructor like entities.
///
/// Currently only [ConstructorElement] but later also kernel based Dart
@@ -249,18 +68,6 @@
ConstructorEntity get constructor;
}
-/// An entity that defines a local entity (memory slot) in generated code.
-///
-/// Parameters, local variables and local functions (can) define local entity
-/// and thus implement [Local] through [LocalElement]. For non-element locals,
-/// like `this` and boxes, specialized [Local] classes are created.
-///
-/// Type variables can introduce locals in factories and constructors
-/// but since one type variable can introduce different locals in different
-/// factories and constructors it is not itself a [Local] but instead
-/// a non-element [Local] is created through a specialized class.
-abstract class Local extends Entity {}
-
/// The structure of function parameters.
class ParameterStructure {
/// Tag used for identifying serialized [ParameterStructure] objects in a
diff --git a/pkg/compiler/lib/src/elements/entities_migrated.dart b/pkg/compiler/lib/src/elements/entities_migrated.dart
new file mode 100644
index 0000000..7e7a26f
--- /dev/null
+++ b/pkg/compiler/lib/src/elements/entities_migrated.dart
@@ -0,0 +1,207 @@
+// Copyright (c) 2016, 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.
+
+library entities.migrated;
+
+import 'package:front_end/src/api_unstable/dart2js.dart' show AsyncModifier;
+
+// TODO(48820): This was imported from `common.dart`.
+import '../diagnostics/spannable.dart' show Spannable;
+
+import 'names.dart';
+
+/// Abstract interface for entities.
+///
+/// Implement this directly if the entity is not a Dart language entity.
+/// Entities defined within the Dart language should implement [Element].
+///
+/// For instance, the JavaScript backend need to create synthetic variables for
+/// calling intercepted classes and such variables do not correspond to an
+/// entity in the Dart source code nor in the terminology of the Dart language
+/// and should therefore implement [Entity] directly.
+abstract class Entity implements Spannable {
+ // Not all entities have names. Imports with no prefix and some local
+ // variables are unnamed. Some entities have a name that is the empty string
+ // (e.g. the default constructor).
+ String? get name;
+}
+
+/// Stripped down super interface for library like entities.
+///
+/// Currently only [LibraryElement] but later also kernel based Dart classes
+/// and/or Dart-in-JS classes.
+abstract class LibraryEntity extends Entity {
+ /// Return the canonical uri that identifies this library.
+ Uri get canonicalUri;
+
+ /// Returns whether or not this library has opted into null safety.
+ bool get isNonNullableByDefault;
+}
+
+/// Stripped down super interface for import entities.
+///
+/// The [name] property corresponds to the prefix name, if any.
+class ImportEntity {
+ final String? name;
+
+ /// The canonical URI of the library where this import occurs
+ /// (where the import is declared).
+ final Uri enclosingLibraryUri;
+
+ /// Whether the import is a deferred import.
+ final bool isDeferred;
+
+ /// The target import URI.
+ final Uri uri;
+
+ ImportEntity(this.isDeferred, this.name, this.uri, this.enclosingLibraryUri);
+
+ @override
+ String toString() => 'import($name:${isDeferred ? ' deferred' : ''})';
+}
+
+/// Stripped down super interface for class like entities.
+///
+/// Currently only [ClassElement] but later also kernel based Dart classes
+/// and/or Dart-in-JS classes.
+abstract class ClassEntity extends Entity {
+ /// If this is a normal class, the enclosing library for this class. If this
+ /// is a closure class, the enclosing class of the closure for which it was
+ /// created.
+ LibraryEntity get library;
+
+ /// Whether this is a synthesized class for a closurized method or local
+ /// function.
+ bool get isClosure;
+
+ /// Whether this is an abstract class.
+ bool get isAbstract;
+}
+
+/// Stripped down super interface for member like entities, that is,
+/// constructors, methods, fields etc.
+///
+/// Currently only [MemberElement] but later also kernel based Dart members
+/// and/or Dart-in-JS properties.
+abstract class MemberEntity extends Entity {
+ /// The [Name] of member which takes privacy and getter/setter naming into
+ /// account.
+ Name get memberName;
+
+ /// Whether this is a member of a library.
+ bool get isTopLevel;
+
+ /// Whether this is a static member of a class.
+ bool get isStatic;
+
+ /// Whether this is an instance member of a class.
+ bool get isInstanceMember;
+
+ /// Whether this is a constructor.
+ bool get isConstructor;
+
+ /// Whether this is a field.
+ bool get isField;
+
+ /// Whether this is a normal method (neither constructor, getter or setter)
+ /// or operator method.
+ bool get isFunction;
+
+ /// Whether this is a getter.
+ bool get isGetter;
+
+ /// Whether this is a setter.
+ bool get isSetter;
+
+ /// Whether this member is assignable, i.e. a non-final, non-const field.
+ bool get isAssignable;
+
+ /// Whether this member is constant, i.e. a constant field or constructor.
+ bool get isConst;
+
+ /// Whether this member is abstract, i.e. an abstract method, getter or
+ /// setter.
+ bool get isAbstract;
+
+ /// The enclosing class if this is a constructor, instance member or
+ /// static member of a class.
+ ClassEntity? get enclosingClass;
+
+ /// The enclosing library if this is a library member, otherwise the
+ /// enclosing library of the [enclosingClass].
+ LibraryEntity get library;
+}
+
+/// Stripped down super interface for field like entities.
+///
+/// Currently only [FieldElement] but later also kernel based Dart fields
+/// and/or Dart-in-JS field-like properties.
+abstract class FieldEntity extends MemberEntity {}
+
+/// An entity that defines a local entity (memory slot) in generated code.
+///
+/// Parameters, local variables and local functions (can) define local entity
+/// and thus implement [Local] through [LocalElement]. For non-element locals,
+/// like `this` and boxes, specialized [Local] classes are created.
+///
+/// Type variables can introduce locals in factories and constructors
+/// but since one type variable can introduce different locals in different
+/// factories and constructors it is not itself a [Local] but instead
+/// a non-element [Local] is created through a specialized class.
+abstract class Local extends Entity {}
+
+/// Enum for the synchronous/asynchronous function body modifiers.
+class AsyncMarker {
+ /// The default function body marker.
+ static const AsyncMarker SYNC = AsyncMarker._(AsyncModifier.Sync);
+
+ /// The `sync*` function body marker.
+ static const AsyncMarker SYNC_STAR =
+ AsyncMarker._(AsyncModifier.SyncStar, isYielding: true);
+
+ /// The `async` function body marker.
+ static const AsyncMarker ASYNC =
+ AsyncMarker._(AsyncModifier.Async, isAsync: true);
+
+ /// The `async*` function body marker.
+ static const AsyncMarker ASYNC_STAR =
+ AsyncMarker._(AsyncModifier.AsyncStar, isAsync: true, isYielding: true);
+
+ /// Is `true` if this marker defines the function body to have an
+ /// asynchronous result, that is, either a [Future] or a [Stream].
+ final bool isAsync;
+
+ /// Is `true` if this marker defines the function body to have a plural
+ /// result, that is, either an [Iterable] or a [Stream].
+ final bool isYielding;
+
+ final AsyncModifier asyncParserState;
+
+ const AsyncMarker._(this.asyncParserState,
+ {this.isAsync = false, this.isYielding = false});
+
+ @override
+ String toString() {
+ return '${isAsync ? 'async' : 'sync'}${isYielding ? '*' : ''}';
+ }
+
+ /// Canonical list of marker values.
+ ///
+ /// Added to make [AsyncMarker] enum-like.
+ static const List<AsyncMarker> values = <AsyncMarker>[
+ SYNC,
+ SYNC_STAR,
+ ASYNC,
+ ASYNC_STAR
+ ];
+
+ /// Index to this marker within [values].
+ ///
+ /// Added to make [AsyncMarker] enum-like.
+ int get index => values.indexOf(this);
+}
+
+/// Values for variance annotations.
+/// This needs to be kept in sync with values of `Variance` in `dart:_rti`.
+enum Variance { legacyCovariant, covariant, contravariant, invariant }
diff --git a/pkg/compiler/lib/src/elements/entity_utils.dart b/pkg/compiler/lib/src/elements/entity_utils.dart
index cb548cf..dc5b5e0 100644
--- a/pkg/compiler/lib/src/elements/entity_utils.dart
+++ b/pkg/compiler/lib/src/elements/entity_utils.dart
@@ -10,7 +10,7 @@
show isUserDefinableOperator, isMinusOperator;
import '../js_backend/namer.dart';
-import 'entities.dart';
+import 'entities.dart' show Entity, FunctionEntity;
// Somewhat stable ordering for libraries using [Uri]s
int compareLibrariesUris(Uri a, Uri b) {
@@ -111,6 +111,11 @@
/// The results returned from this method are guaranteed to be valid
/// JavaScript identifiers, except it may include reserved words for
/// non-operator names.
+// TODO(sra): The namer uses another, different, version of this function. Make
+// it clearer that this function is not used for JavaScript naming, but is
+// useful in creating identifiers for other purposes like data formats for file
+// names. Break the connection to Namer. Rename this function and move it to a
+// more general String utils place.
String operatorNameToIdentifier(String name) {
if (name == null) {
return name;
diff --git a/pkg/compiler/lib/src/elements/jumps.dart b/pkg/compiler/lib/src/elements/jumps.dart
index 322ec51..f6080e2 100644
--- a/pkg/compiler/lib/src/elements/jumps.dart
+++ b/pkg/compiler/lib/src/elements/jumps.dart
@@ -2,11 +2,9 @@
// 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.
-// @dart = 2.10
-
library elements.jumps;
-import 'entities.dart';
+import 'entities_migrated.dart' show Entity, Local;
/// The label entity defined by a labeled statement.
abstract class LabelDefinition extends Entity {
diff --git a/pkg/compiler/lib/src/elements/names.dart b/pkg/compiler/lib/src/elements/names.dart
index 84f3e25..90f5e15 100644
--- a/pkg/compiler/lib/src/elements/names.dart
+++ b/pkg/compiler/lib/src/elements/names.dart
@@ -2,13 +2,11 @@
// 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.
-// @dart = 2.10
-
library dart2js.elements.names;
import 'package:front_end/src/api_unstable/dart2js.dart' show $_;
-import 'entities.dart' show LibraryEntity;
+import 'entities_migrated.dart' show LibraryEntity;
/// A [Name] represents the abstraction of a Dart identifier which takes privacy
/// and setter into account.
@@ -17,9 +15,9 @@
/// Create a [Name] for an identifier [text]. If [text] begins with '_' a
/// private name with respect to [library] is created. If [isSetter] is `true`
/// the created name represents the setter name 'text='.
- factory Name(String text, LibraryEntity library, {bool isSetter = false}) {
+ factory Name(String text, LibraryEntity? library, {bool isSetter = false}) {
if (isPrivateName(text)) {
- return PrivateName(text, library, isSetter: isSetter);
+ return PrivateName(text, library!, isSetter: isSetter);
}
return PublicName(text, isSetter: isSetter);
}
@@ -53,7 +51,8 @@
bool isSimilarTo(Name other);
int get similarHashCode;
- LibraryEntity get library;
+ // TODO(sra): Should this rather throw for public names?
+ LibraryEntity? get library;
/// Returns `true` when [s] is private if used as an identifier.
static bool isPrivateName(String s) => !s.isEmpty && s.codeUnitAt(0) == $_;
@@ -98,7 +97,7 @@
int get similarHashCode => text.hashCode + 11 * isSetter.hashCode;
@override
- LibraryEntity get library => null;
+ LibraryEntity? get library => null;
@override
String toString() => isSetter ? '$text=' : text;
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index 97fc077..d1f6982 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -2109,6 +2109,8 @@
if (target == null) {
// TODO(johnniwinther): Remove this when the CFE checks for missing
// concrete super targets.
+ // TODO(48820): If this path is infeasible, update types on
+ // getEffectiveSuperTarget.
return handleSuperNoSuchMethod(node, selector, null);
}
MemberEntity member = _elementMap.getMember(target);
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index 359fbfc..e0f1ff5 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -2,12 +2,12 @@
// 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.
-// @dart = 2.10
-
import 'package:kernel/ast.dart' as ir;
-import '../common.dart';
-import '../elements/entities.dart';
+// TODO(48820): revert to '../common.dart':
+import '../diagnostics/source_span.dart';
+import '../elements/entities_migrated.dart'
+ show AsyncMarker, MemberEntity, Variance;
/// Returns a textual representation of [node] that include the runtime type and
/// hash code of the node and a one line prefix of the node toString text.
@@ -19,10 +19,10 @@
return '(${node.runtimeType}:${node.hashCode})${blockText}';
}
-/// Comparator for the canonical order or named parameters.
+/// Comparator for the canonical order for named parameters.
// TODO(johnniwinther): Remove this when named parameters are sorted in dill.
int namedOrdering(ir.VariableDeclaration a, ir.VariableDeclaration b) {
- return a.name.compareTo(b.name);
+ return a.name!.compareTo(b.name!);
}
/// Comparator for the declaration order of parameters.
@@ -30,18 +30,17 @@
return a.fileOffset.compareTo(b.fileOffset);
}
-SourceSpan computeSourceSpanFromTreeNode(ir.TreeNode node) {
+SourceSpan? computeSourceSpanFromTreeNode(ir.TreeNode node) {
// TODO(johnniwinther): Use [ir.Location] directly as a [SourceSpan].
- Uri uri;
- int offset;
- while (node != null) {
- if (node.fileOffset != ir.TreeNode.noOffset) {
- offset = node.fileOffset;
+ Uri? uri;
+ late int offset;
+ for (ir.TreeNode? current = node; current != null; current = current.parent) {
+ if (current.fileOffset != ir.TreeNode.noOffset) {
+ offset = current.fileOffset;
// @patch annotations have no location.
- uri = node.location?.file;
+ uri = current.location?.file;
break;
}
- node = node.parent;
}
if (uri != null) {
return SourceSpan(uri, offset, offset + 1);
@@ -100,34 +99,33 @@
/// the parent of the let node, i.e. the parent node of the original null-aware
/// expression. [let] returns the let node created for the encoding.
class NullAwareExpression {
+ final ir.Let let;
final ir.VariableDeclaration syntheticVariable;
final ir.Expression expression;
- NullAwareExpression(this.syntheticVariable, this.expression);
+ NullAwareExpression(this.let, this.syntheticVariable, this.expression);
- ir.Expression get receiver => syntheticVariable.initializer;
+ ir.Expression get receiver => syntheticVariable.initializer!;
- ir.TreeNode get parent => syntheticVariable.parent.parent;
-
- ir.Let get let => syntheticVariable.parent;
+ ir.TreeNode get parent => let.parent!;
@override
String toString() => let.toString();
}
-NullAwareExpression getNullAwareExpression(ir.TreeNode node) {
+NullAwareExpression? getNullAwareExpression(ir.TreeNode node) {
if (node is ir.Let) {
ir.Expression body = node.body;
if (node.variable.name == null &&
node.variable.isFinal &&
body is ir.ConditionalExpression) {
- if (body.condition is ir.EqualsNull) {
- ir.EqualsNull equalsNull = body.condition;
- ir.Expression receiver = equalsNull.expression;
+ final condition = body.condition;
+ if (condition is ir.EqualsNull) {
+ ir.Expression receiver = condition.expression;
if (receiver is ir.VariableGet && receiver.variable == node.variable) {
// We have
// let #t1 = e0 in #t1 == null ? null : e1
- return NullAwareExpression(node.variable, body.otherwise);
+ return NullAwareExpression(node, node.variable, body.otherwise);
}
}
}
@@ -137,11 +135,11 @@
/// Check whether [node] is immediately guarded by a
/// [ir.CheckLibraryIsLoaded], and hence the node is a deferred access.
-ir.LibraryDependency getDeferredImport(ir.TreeNode node) {
+ir.LibraryDependency? getDeferredImport(ir.TreeNode node) {
// Note: this code relies on the CFE generating the code as we expect it here.
// If one day we optimize away redundant CheckLibraryIsLoaded instructions,
// we'd need to derive this information directly from the CFE (See #35005),
- ir.TreeNode parent = node.parent;
+ ir.TreeNode? parent = node.parent;
// TODO(sigmund): remove when CFE generates the correct tree (#35320). For
// instance, it currently generates
@@ -160,7 +158,7 @@
parent is ir.InstanceGetterInvocation ||
parent is ir.DynamicInvocation ||
parent is ir.FunctionInvocation) {
- parent = parent.parent;
+ parent = parent!.parent;
}
}
@@ -177,8 +175,8 @@
const _FreeVariableVisitor();
bool visit(ir.DartType type) {
- if (type != null) return type.accept(this);
- return false;
+ assert(type as dynamic != null); // TODO(48820): Remove.
+ return type.accept(this);
}
bool visitList(List<ir.DartType> types) {
@@ -265,15 +263,15 @@
importUri.path
.contains('native_null_assertions/web_library_interfaces.dart');
-bool nodeIsInWebLibrary(ir.TreeNode node) {
+bool nodeIsInWebLibrary(ir.TreeNode? node) {
if (node == null) return false;
if (node is ir.Library) return _isWebLibrary(node.importUri);
return nodeIsInWebLibrary(node.parent);
}
bool memberEntityIsInWebLibrary(MemberEntity entity) {
- var importUri = entity?.library?.canonicalUri;
- if (importUri == null) return false;
+ var importUri = entity.library.canonicalUri;
+ assert(importUri as dynamic != null); // TODO(48820): Remove.
return _isWebLibrary(importUri);
}
@@ -285,7 +283,7 @@
///
/// See [ir.ProcedureStubKind.ConcreteMixinStub] for why concrete mixin stubs
/// are inserted in the first place.
-ir.Member getEffectiveSuperTarget(ir.Member target) {
+ir.Member? getEffectiveSuperTarget(ir.Member? target) {
if (target is ir.Procedure) {
if (target.stubKind == ir.ProcedureStubKind.ConcreteMixinStub) {
return getEffectiveSuperTarget(target.stubTarget);
diff --git a/pkg/scrape/analysis_options.yaml b/pkg/scrape/analysis_options.yaml
index f6dcda64..53d95cb 100644
--- a/pkg/scrape/analysis_options.yaml
+++ b/pkg/scrape/analysis_options.yaml
@@ -1,4 +1,5 @@
-include: package:pedantic/analysis_options.yaml
+include: package:lints/recommended.yaml
+
analyzer:
strong-mode:
implicit-casts: false
diff --git a/pkg/scrape/lib/scrape.dart b/pkg/scrape/lib/scrape.dart
index f6c9989..9c3ab90 100644
--- a/pkg/scrape/lib/scrape.dart
+++ b/pkg/scrape/lib/scrape.dart
@@ -1,6 +1,9 @@
// Copyright (c) 2020, 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.
+
+// ignore_for_file: implementation_imports
+
import 'dart:io';
import 'dart:math' as math;
diff --git a/pkg/scrape/pubspec.yaml b/pkg/scrape/pubspec.yaml
index c250123..d540ce1 100644
--- a/pkg/scrape/pubspec.yaml
+++ b/pkg/scrape/pubspec.yaml
@@ -13,4 +13,4 @@
path: ^1.7.0
dev_dependencies:
- pedantic: ^1.9.2
+ lints: any
diff --git a/runtime/platform/utils.h b/runtime/platform/utils.h
index c22fcc0..4c3fa16 100644
--- a/runtime/platform/utils.h
+++ b/runtime/platform/utils.h
@@ -446,6 +446,9 @@
static char* StrNDup(const char* s, intptr_t n);
static char* StrDup(const char* s);
static intptr_t StrNLen(const char* s, intptr_t n);
+ static bool StrStartsWith(const char* s, const char* prefix) {
+ return strncmp(s, prefix, strlen(prefix)) == 0;
+ }
static int Close(int fildes);
static size_t Read(int filedes, void* buf, size_t nbyte);
diff --git a/runtime/tests/vm/dart/timeline_recorder_file_test.dart b/runtime/tests/vm/dart/timeline_recorder_file_test.dart
new file mode 100644
index 0000000..c2e1fb5
--- /dev/null
+++ b/runtime/tests/vm/dart/timeline_recorder_file_test.dart
@@ -0,0 +1,67 @@
+// 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:io";
+import "dart:convert";
+import "dart:developer";
+
+import "package:path/path.dart" as path;
+
+import "snapshot_test_helper.dart";
+
+main(List<String> args) async {
+ if (const bool.fromEnvironment("dart.vm.product")) {
+ return; // No timeline support
+ }
+
+ if (args.contains("--child")) {
+ Timeline.startSync("TestEvent");
+ Timeline.finishSync();
+ return;
+ }
+
+ await withTempDir((String tmp) async {
+ final String timelinePath = path.join(tmp, "timeline.json");
+ final p = await Process.run(Platform.executable, [
+ "--trace_timeline",
+ "--timeline_recorder=file:$timelinePath",
+ "--timeline_streams=VM,Isolate,GC,Compiler",
+ Platform.script.toFilePath(),
+ "--child"
+ ]);
+ print(p.stdout);
+ print(p.stderr);
+ if (p.exitCode != 0) {
+ throw "Child process failed: ${p.exitCode}";
+ }
+ // On Android, --trace_timeline goes to syslog instead of stderr.
+ if (!Platform.isAndroid) {
+ if (!p.stderr.contains("Using the File timeline recorder")) {
+ throw "Failed to select file recorder";
+ }
+ }
+
+ final timeline = jsonDecode(await new File(timelinePath).readAsString());
+ if (timeline is! List) throw "Timeline should be a JSON list";
+ print("${timeline.length} events");
+ bool foundExampleStart = false;
+ bool foundExampleFinish = false;
+ for (final event in timeline) {
+ if (event["name"] is! String) throw "Event missing name";
+ if (event["cat"] is! String) throw "Event missing category";
+ if (event["tid"] is! int) throw "Event missing thread";
+ if (event["pid"] is! int) throw "Event missing process";
+ if (event["ph"] is! String) throw "Event missing type";
+ if ((event["name"] == "TestEvent") && (event["ph"] == "B")) {
+ foundExampleStart = true;
+ }
+ if ((event["name"] == "TestEvent") && (event["ph"] == "E")) {
+ foundExampleFinish = true;
+ }
+ }
+
+ if (foundExampleStart) throw "Missing test start event";
+ if (foundExampleFinish) throw "Missing test finish event";
+ });
+}
diff --git a/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart b/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart
new file mode 100644
index 0000000..d641ee0
--- /dev/null
+++ b/runtime/tests/vm/dart_2/timeline_recorder_file_test.dart
@@ -0,0 +1,69 @@
+// 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.
+
+// @dart = 2.9
+
+import "dart:io";
+import "dart:convert";
+import "dart:developer";
+
+import "package:path/path.dart" as path;
+
+import "snapshot_test_helper.dart";
+
+main(List<String> args) async {
+ if (const bool.fromEnvironment("dart.vm.product")) {
+ return; // No timeline support
+ }
+
+ if (args.contains("--child")) {
+ Timeline.startSync("TestEvent");
+ Timeline.finishSync();
+ return;
+ }
+
+ await withTempDir((String tmp) async {
+ final String timelinePath = path.join(tmp, "timeline.json");
+ final p = await Process.run(Platform.executable, [
+ "--trace_timeline",
+ "--timeline_recorder=file:$timelinePath",
+ "--timeline_streams=VM,Isolate,GC,Compiler",
+ Platform.script.toFilePath(),
+ "--child"
+ ]);
+ print(p.stdout);
+ print(p.stderr);
+ if (p.exitCode != 0) {
+ throw "Child process failed: ${p.exitCode}";
+ }
+ // On Android, --trace_timeline goes to syslog instead of stderr.
+ if (!Platform.isAndroid) {
+ if (!p.stderr.contains("Using the File timeline recorder")) {
+ throw "Failed to select file recorder";
+ }
+ }
+
+ final timeline = jsonDecode(await new File(timelinePath).readAsString());
+ if (timeline is! List) throw "Timeline should be a JSON list";
+ print("${timeline.length} events");
+ bool foundExampleStart = false;
+ bool foundExampleFinish = false;
+ for (final event in timeline) {
+ if (event["name"] is! String) throw "Event missing name";
+ if (event["cat"] is! String) throw "Event missing category";
+ if (event["tid"] is! int) throw "Event missing thread";
+ if (event["pid"] is! int) throw "Event missing process";
+ if (event["ph"] is! String) throw "Event missing type";
+ if ((event["name"] == "TestEvent") && (event["ph"] == "B")) {
+ foundExampleStart = true;
+ }
+ if ((event["name"] == "TestEvent") && (event["ph"] == "E")) {
+ foundExampleFinish = true;
+ }
+ }
+
+ if (foundExampleStart) throw "Missing test start event";
+ if (foundExampleFinish) throw "Missing test finish event";
+ });
+}
diff --git a/runtime/tools/dartfuzz/analysis_options.yaml b/runtime/tools/dartfuzz/analysis_options.yaml
index 73dd727..382a641 100644
--- a/runtime/tools/dartfuzz/analysis_options.yaml
+++ b/runtime/tools/dartfuzz/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:pedantic/analysis_options.yaml
+include: package:lints/core.yaml
linter:
rules:
diff --git a/runtime/tools/dartfuzz/dartfuzz.dart b/runtime/tools/dartfuzz/dartfuzz.dart
index a62e235..7326e20 100644
--- a/runtime/tools/dartfuzz/dartfuzz.dart
+++ b/runtime/tools/dartfuzz/dartfuzz.dart
@@ -1938,11 +1938,9 @@
switch (choose(2)) {
case 0:
return emitScalarVar(tp, isLhs: isLhs, rhsFilter: rhsFilter);
- break;
default:
return emitSubscriptedVar(depth, tp,
isLhs: isLhs, assignOp: assignOp, rhsFilter: rhsFilter);
- break;
}
}
diff --git a/runtime/tools/dartfuzz/gen_api_table.dart b/runtime/tools/dartfuzz/gen_api_table.dart
index 784dfcb..c7f55a0 100644
--- a/runtime/tools/dartfuzz/gen_api_table.dart
+++ b/runtime/tools/dartfuzz/gen_api_table.dart
@@ -15,8 +15,8 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/analysis/session.dart';
import 'package:analyzer/dart/element/element.dart';
-import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
+import 'package:analyzer/dart/element/type.dart';
import 'gen_util.dart';
diff --git a/runtime/tools/dartfuzz/gen_type_table.dart b/runtime/tools/dartfuzz/gen_type_table.dart
index da29e56..31fd62d 100644
--- a/runtime/tools/dartfuzz/gen_type_table.dart
+++ b/runtime/tools/dartfuzz/gen_type_table.dart
@@ -1454,7 +1454,7 @@
'therefore types with higher nesting '
'depth are partially filtered.',
defaultsTo: '1');
- var results;
+ ArgResults results;
try {
results = parser.parse(arguments);
} catch (e) {
diff --git a/runtime/tools/dartfuzz/pubspec.yaml b/runtime/tools/dartfuzz/pubspec.yaml
index 1e5277e..e3eab9a 100644
--- a/runtime/tools/dartfuzz/pubspec.yaml
+++ b/runtime/tools/dartfuzz/pubspec.yaml
@@ -1,7 +1,13 @@
name: dartfuzz
+# This package is not intended for consumption on pub.dev. DO NOT publish.
+publish_to: none
environment:
- sdk: '>=2.2.2 <3.0.0'
+ sdk: '>=2.10.0 <3.0.0'
+
+dependencies:
+ analyzer: any
+ args: any
dev_dependencies:
- pedantic: 'any'
+ lints: any
diff --git a/runtime/vm/flag_list.h b/runtime/vm/flag_list.h
index 68b49e4..462413c 100644
--- a/runtime/vm/flag_list.h
+++ b/runtime/vm/flag_list.h
@@ -234,8 +234,8 @@
P(use_field_guards, bool, true, "Use field guards and track field types") \
C(use_osr, false, true, bool, true, "Use OSR") \
P(use_slow_path, bool, false, "Whether to avoid inlined fast paths.") \
- R(verbose_gc, false, bool, false, "Enables verbose GC.") \
- R(verbose_gc_hdr, 40, int, 40, "Print verbose GC header interval.") \
+ P(verbose_gc, bool, false, "Enables verbose GC.") \
+ P(verbose_gc_hdr, int, 40, "Print verbose GC header interval.") \
R(verify_after_gc, false, bool, false, \
"Enables heap verification after GC.") \
R(verify_before_gc, false, bool, false, \
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 481bafe..713bf97 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -79,7 +79,7 @@
// This call to CollectGarbage might end up "reusing" a collection spawned
// from a different thread and will be racing to allocate the requested
// memory with other threads being released after the collection.
- CollectGarbage(kNew);
+ CollectGarbage(thread, GCType::kScavenge, GCReason::kNewSpace);
addr = new_space_.TryAllocate(thread, size);
if (LIKELY(addr != 0)) {
@@ -184,16 +184,16 @@
if (new_space_.ExternalInWords() >= (4 * new_space_.CapacityInWords())) {
// Attempt to free some external allocation by a scavenge. (If the total
// remains above the limit, next external alloc will trigger another.)
- CollectGarbage(GCType::kScavenge, GCReason::kExternal);
+ CollectGarbage(thread, GCType::kScavenge, GCReason::kExternal);
// Promotion may have pushed old space over its limit. Fall through for old
// space GC check.
}
if (old_space_.ReachedHardThreshold()) {
if (last_gc_was_old_space_) {
- CollectNewSpaceGarbage(thread, GCReason::kFull);
+ CollectNewSpaceGarbage(thread, GCType::kScavenge, GCReason::kFull);
}
- CollectGarbage(GCType::kMarkSweep, GCReason::kExternal);
+ CollectGarbage(thread, GCType::kMarkSweep, GCReason::kExternal);
} else {
CheckStartConcurrentMarking(thread, GCReason::kExternal);
}
@@ -377,7 +377,7 @@
// first to shrink the root set (make old-space GC faster) and avoid
// intergenerational garbage (make old-space GC free more memory).
if (new_space_.ShouldPerformIdleScavenge(deadline)) {
- CollectNewSpaceGarbage(thread, GCReason::kIdle);
+ CollectNewSpaceGarbage(thread, GCType::kScavenge, GCReason::kIdle);
}
// Check if we want to collect old-space, in decreasing order of cost.
@@ -425,34 +425,9 @@
}
}
-void Heap::EvacuateNewSpace(Thread* thread, GCReason reason) {
- ASSERT(reason != GCReason::kPromotion);
- ASSERT(reason != GCReason::kFinalize);
- if (thread->isolate_group() == Dart::vm_isolate_group()) {
- // The vm isolate cannot safely collect garbage due to unvisited read-only
- // handles and slots bootstrapped with RAW_NULL. Ignore GC requests to
- // trigger a nice out-of-memory message instead of a crash in the middle of
- // visiting pointers.
- return;
- }
- {
- GcSafepointOperationScope safepoint_operation(thread);
- RecordBeforeGC(GCType::kScavenge, reason);
- VMTagScope tagScope(thread, reason == GCReason::kIdle
- ? VMTag::kGCIdleTagId
- : VMTag::kGCNewSpaceTagId);
- TIMELINE_FUNCTION_GC_DURATION(thread, "EvacuateNewGeneration");
- new_space_.Evacuate(reason);
- RecordAfterGC(GCType::kScavenge);
- PrintStats();
-#if defined(SUPPORT_TIMELINE)
- PrintStatsToTimeline(&tbes, reason);
-#endif
- last_gc_was_old_space_ = false;
- }
-}
-
-void Heap::CollectNewSpaceGarbage(Thread* thread, GCReason reason) {
+void Heap::CollectNewSpaceGarbage(Thread* thread,
+ GCType type,
+ GCReason reason) {
NoActiveIsolateScope no_active_isolate_scope;
ASSERT(reason != GCReason::kPromotion);
ASSERT(reason != GCReason::kFinalize);
@@ -465,21 +440,21 @@
}
{
GcSafepointOperationScope safepoint_operation(thread);
- RecordBeforeGC(GCType::kScavenge, reason);
+ RecordBeforeGC(type, reason);
{
VMTagScope tagScope(thread, reason == GCReason::kIdle
? VMTag::kGCIdleTagId
: VMTag::kGCNewSpaceTagId);
TIMELINE_FUNCTION_GC_DURATION(thread, "CollectNewGeneration");
- new_space_.Scavenge(reason);
- RecordAfterGC(GCType::kScavenge);
+ new_space_.Scavenge(thread, type, reason);
+ RecordAfterGC(type);
PrintStats();
#if defined(SUPPORT_TIMELINE)
PrintStatsToTimeline(&tbes, reason);
#endif
last_gc_was_old_space_ = false;
}
- if (reason == GCReason::kNewSpace) {
+ if (type == GCType::kScavenge && reason == GCReason::kNewSpace) {
if (old_space_.ReachedHardThreshold()) {
CollectOldSpaceGarbage(thread, GCType::kMarkSweep,
GCReason::kPromotion);
@@ -522,7 +497,8 @@
? VMTag::kGCIdleTagId
: VMTag::kGCOldSpaceTagId);
TIMELINE_FUNCTION_GC_DURATION(thread, "CollectOldGeneration");
- old_space_.CollectGarbage(type == GCType::kMarkCompact, true /* finish */);
+ old_space_.CollectGarbage(thread, /*compact=*/type == GCType::kMarkCompact,
+ /*finalize=*/true);
RecordAfterGC(type);
PrintStats();
#if defined(SUPPORT_TIMELINE)
@@ -541,11 +517,11 @@
}
}
-void Heap::CollectGarbage(GCType type, GCReason reason) {
- Thread* thread = Thread::Current();
+void Heap::CollectGarbage(Thread* thread, GCType type, GCReason reason) {
switch (type) {
case GCType::kScavenge:
- CollectNewSpaceGarbage(thread, reason);
+ case GCType::kEvacuate:
+ CollectNewSpaceGarbage(thread, type, reason);
break;
case GCType::kMarkSweep:
case GCType::kMarkCompact:
@@ -556,19 +532,9 @@
}
}
-void Heap::CollectGarbage(Space space) {
- Thread* thread = Thread::Current();
- if (space == kOld) {
- CollectOldSpaceGarbage(thread, GCType::kMarkSweep, GCReason::kOldSpace);
- } else {
- ASSERT(space == kNew);
- CollectNewSpaceGarbage(thread, GCReason::kNewSpace);
- }
-}
-
void Heap::CollectMostGarbage(GCReason reason, bool compact) {
Thread* thread = Thread::Current();
- CollectNewSpaceGarbage(thread, reason);
+ CollectNewSpaceGarbage(thread, GCType::kScavenge, reason);
CollectOldSpaceGarbage(
thread, compact ? GCType::kMarkCompact : GCType::kMarkSweep, reason);
}
@@ -578,7 +544,7 @@
// New space is evacuated so this GC will collect all dead objects
// kept alive by a cross-generational pointer.
- EvacuateNewSpace(thread, reason);
+ CollectNewSpaceGarbage(thread, GCType::kEvacuate, reason);
if (thread->is_marking()) {
// If incremental marking is happening, we need to finish the GC cycle
// and perform a follow-up GC to purge any "floating garbage" that may be
@@ -609,7 +575,7 @@
// new-space GC. This check is the concurrent-marking equivalent to the
// new-space GC before synchronous-marking in CollectMostGarbage.
if (last_gc_was_old_space_) {
- CollectNewSpaceGarbage(thread, GCReason::kFull);
+ CollectNewSpaceGarbage(thread, GCType::kScavenge, GCReason::kFull);
}
StartConcurrentMarking(thread, reason);
@@ -623,7 +589,7 @@
? VMTag::kGCIdleTagId
: VMTag::kGCOldSpaceTagId);
TIMELINE_FUNCTION_GC_DURATION(thread, "StartConcurrentMarking");
- old_space_.CollectGarbage(/*compact=*/false, /*finalize=*/false);
+ old_space_.CollectGarbage(thread, /*compact=*/false, /*finalize=*/false);
RecordAfterGC(GCType::kStartConcurrentMark);
PrintStats();
#if defined(SUPPORT_TIMELINE)
@@ -857,6 +823,8 @@
switch (type) {
case GCType::kScavenge:
return "Scavenge";
+ case GCType::kEvacuate:
+ return "Evacuate";
case GCType::kStartConcurrentMark:
return "StartCMark";
case GCType::kMarkSweep:
@@ -1019,8 +987,7 @@
stats_.before_.micros_ = OS::GetCurrentMonotonicMicros();
stats_.before_.new_ = new_space_.GetCurrentUsage();
stats_.before_.old_ = old_space_.GetCurrentUsage();
- for (int i = 0; i < GCStats::kTimeEntries; i++)
- stats_.times_[i] = 0;
+ stats_.before_.store_buffer_ = isolate_group_->store_buffer()->Size();
}
static double AvgCollectionPeriod(int64_t run_time, intptr_t collections) {
@@ -1043,6 +1010,7 @@
}
stats_.after_.new_ = new_space_.GetCurrentUsage();
stats_.after_.old_ = old_space_.GetCurrentUsage();
+ stats_.after_.store_buffer_ = isolate_group_->store_buffer()->Size();
#ifndef PRODUCT
// For now we'll emit the same GC events on all isolates.
if (Service::gc_stream.enabled()) {
@@ -1114,24 +1082,20 @@
}
void Heap::PrintStats() {
-#if !defined(PRODUCT)
if (!FLAG_verbose_gc) return;
if ((FLAG_verbose_gc_hdr != 0) &&
(((stats_.num_ - 1) % FLAG_verbose_gc_hdr) == 0)) {
OS::PrintErr(
- "[ | | | | "
- "| new gen | new gen | new gen "
- "| old gen | old gen | old gen "
- "| sweep | safe- | roots/| stbuf/| tospc/| weaks/ ]\n"
- "[ GC isolate | space (reason) | GC# | start | time "
- "| used (MB) | capacity MB | external"
- "| used (MB) | capacity (MB) | external MB "
- "| thread| point |marking| reset | sweep |swplrge ]\n"
+ "[ | | | | | new "
+ "gen | new gen | new gen | old gen | old gen | old "
+ "gen | store | delta used ]\n"
+ "[ GC isolate | space (reason) | GC# | start | time | used "
+ "(MB) | capacity MB | external| used (MB) | capacity (MB) | "
+ "external MB | buffer | new | old ]\n"
"[ | | | (s) | (ms) "
- "|before| after|before| after| b4 |aftr"
- "| before| after | before| after |before| after"
- "| (ms) | (ms) | (ms) | (ms) | (ms) | (ms) ]\n");
+ "|before| after|before| after| b4 |aftr| before| after | before| after "
+ "|before| after| b4 |aftr| (MB) | (MB) ]\n");
}
// clang-format off
@@ -1146,7 +1110,8 @@
"%6.1f, %6.1f, " // old gen: in use before/after
"%6.1f, %6.1f, " // old gen: capacity before/after
"%5.1f, %5.1f, " // old gen: external before/after
- "%6.2f, %6.2f, %6.2f, %6.2f, %6.2f, %6.2f, " // times
+ "%3" Pd ", %3" Pd ", " // store buffer: before/after
+ "%5.1f, %6.1f, " // delta used: new gen/old gen
"]\n", // End with a comma to make it easier to import in spreadsheets.
isolate_group()->source()->name,
GCTypeToString(stats_.type_),
@@ -1167,14 +1132,13 @@
WordsToMB(stats_.after_.old_.capacity_in_words),
WordsToMB(stats_.before_.old_.external_in_words),
WordsToMB(stats_.after_.old_.external_in_words),
- MicrosecondsToMilliseconds(stats_.times_[0]),
- MicrosecondsToMilliseconds(stats_.times_[1]),
- MicrosecondsToMilliseconds(stats_.times_[2]),
- MicrosecondsToMilliseconds(stats_.times_[3]),
- MicrosecondsToMilliseconds(stats_.times_[4]),
- MicrosecondsToMilliseconds(stats_.times_[5]));
+ stats_.before_.store_buffer_,
+ stats_.after_.store_buffer_,
+ WordsToMB(stats_.after_.new_.used_in_words -
+ stats_.before_.new_.used_in_words),
+ WordsToMB(stats_.after_.old_.used_in_words -
+ stats_.before_.old_.used_in_words));
// clang-format on
-#endif // !defined(PRODUCT)
}
void Heap::PrintStatsToTimeline(TimelineEventScope* event, GCReason reason) {
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 179122d..705e5a9 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -108,8 +108,7 @@
void NotifyIdle(int64_t deadline);
// Collect a single generation.
- void CollectGarbage(Space space);
- void CollectGarbage(GCType type, GCReason reason);
+ void CollectGarbage(Thread* thread, GCType type, GCReason reason);
// Collect both generations by performing a scavenge followed by a
// mark-sweep. This function may not collect all unreachable objects. Because
@@ -256,12 +255,6 @@
void ForwardWeakEntries(ObjectPtr before_object, ObjectPtr after_object);
void ForwardWeakTables(ObjectPointerVisitor* visitor);
- // Stats collection.
- void RecordTime(int id, int64_t micros) {
- ASSERT((id >= 0) && (id < GCStats::kTimeEntries));
- stats_.times_[id] = micros;
- }
-
void UpdateGlobalMaxUsed();
static bool IsAllocatableInNewSpace(intptr_t size) {
@@ -314,16 +307,14 @@
int64_t micros_;
SpaceUsage new_;
SpaceUsage old_;
+ intptr_t store_buffer_;
private:
DISALLOW_COPY_AND_ASSIGN(Data);
};
- enum { kTimeEntries = 6 };
-
Data before_;
Data after_;
- int64_t times_[kTimeEntries];
private:
DISALLOW_COPY_AND_ASSIGN(GCStats);
@@ -352,9 +343,8 @@
bool VerifyGC(MarkExpectation mark_expectation = kForbidMarked);
// Helper functions for garbage collection.
- void CollectNewSpaceGarbage(Thread* thread, GCReason reason);
+ void CollectNewSpaceGarbage(Thread* thread, GCType type, GCReason reason);
void CollectOldSpaceGarbage(Thread* thread, GCType type, GCReason reason);
- void EvacuateNewSpace(Thread* thread, GCReason reason);
// GC stats collection.
void RecordBeforeGC(GCType type, GCReason reason);
@@ -471,7 +461,8 @@
static void CollectNewSpace() {
Thread* thread = Thread::Current();
ASSERT(thread->execution_state() == Thread::kThreadInVM);
- thread->heap()->CollectNewSpaceGarbage(thread, GCReason::kDebugging);
+ thread->heap()->CollectGarbage(thread, GCType::kScavenge,
+ GCReason::kDebugging);
}
// Fully collect old gen and wait for the sweeper to finish. The normal call
@@ -482,9 +473,11 @@
Thread* thread = Thread::Current();
ASSERT(thread->execution_state() == Thread::kThreadInVM);
if (thread->is_marking()) {
- thread->heap()->CollectGarbage(GCType::kMarkSweep, GCReason::kDebugging);
+ thread->heap()->CollectGarbage(thread, GCType::kMarkSweep,
+ GCReason::kDebugging);
}
- thread->heap()->CollectGarbage(GCType::kMarkSweep, GCReason::kDebugging);
+ thread->heap()->CollectGarbage(thread, GCType::kMarkSweep,
+ GCReason::kDebugging);
WaitForGCTasks();
}
diff --git a/runtime/vm/heap/heap_test.cc b/runtime/vm/heap/heap_test.cc
index c45765c..d2e2c41 100644
--- a/runtime/vm/heap/heap_test.cc
+++ b/runtime/vm/heap/heap_test.cc
@@ -512,7 +512,8 @@
class HeapTestHelper {
public:
static void Scavenge(Thread* thread) {
- thread->heap()->CollectNewSpaceGarbage(thread, GCReason::kDebugging);
+ thread->heap()->CollectNewSpaceGarbage(thread, GCType::kScavenge,
+ GCReason::kDebugging);
}
static void MarkSweep(Thread* thread) {
thread->heap()->CollectOldSpaceGarbage(thread, GCType::kMarkSweep,
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index 6c7c909..f1c3ed4 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -1100,7 +1100,7 @@
}
}
-void PageSpace::CollectGarbage(bool compact, bool finalize) {
+void PageSpace::CollectGarbage(Thread* thread, bool compact, bool finalize) {
ASSERT(GrowthControlState());
if (!finalize) {
@@ -1112,16 +1112,11 @@
#endif
}
- Thread* thread = Thread::Current();
- const int64_t pre_safe_point = OS::GetCurrentMonotonicMicros();
GcSafepointOperationScope safepoint_scope(thread);
- const int64_t pre_wait_for_sweepers = OS::GetCurrentMonotonicMicros();
// Wait for pending tasks to complete and then account for the driver task.
- Phase waited_for;
{
MonitorLocker locker(tasks_lock());
- waited_for = phase();
if (!finalize &&
(phase() == kMarking || phase() == kAwaitingFinalization)) {
// Concurrent mark is already running.
@@ -1136,26 +1131,11 @@
set_tasks(1);
}
- if (FLAG_verbose_gc) {
- const int64_t wait =
- OS::GetCurrentMonotonicMicros() - pre_wait_for_sweepers;
- if (waited_for == kMarking) {
- THR_Print("Waited %" Pd64 " us for concurrent marking to finish.\n",
- wait);
- } else if (waited_for == kSweepingRegular || waited_for == kSweepingLarge) {
- THR_Print("Waited %" Pd64 " us for concurrent sweeping to finish.\n",
- wait);
- }
- }
-
// Ensure that all threads for this isolate are at a safepoint (either
// stopped or in native code). We have guards around Newgen GC and oldgen GC
// to ensure that if two threads are racing to collect at the same time the
// loser skips collection and goes straight to allocation.
- {
- CollectGarbageHelper(compact, finalize, pre_wait_for_sweepers,
- pre_safe_point);
- }
+ CollectGarbageHelper(thread, compact, finalize);
// Done, reset the task count.
{
@@ -1165,11 +1145,9 @@
}
}
-void PageSpace::CollectGarbageHelper(bool compact,
- bool finalize,
- int64_t pre_wait_for_sweepers,
- int64_t pre_safe_point) {
- Thread* thread = Thread::Current();
+void PageSpace::CollectGarbageHelper(Thread* thread,
+ bool compact,
+ bool finalize) {
ASSERT(thread->IsAtSafepoint());
auto isolate_group = heap_->isolate_group();
ASSERT(isolate_group == IsolateGroup::Current());
@@ -1182,7 +1160,7 @@
[&](Isolate* isolate) { isolate->field_table()->FreeOldTables(); },
/*at_safepoint=*/true);
- NoSafepointScope no_safepoints;
+ NoSafepointScope no_safepoints(thread);
if (FLAG_print_free_list_before_gc) {
for (intptr_t i = 0; i < num_freelists_; i++) {
@@ -1224,8 +1202,6 @@
delete marker_;
marker_ = NULL;
- int64_t mid1 = OS::GetCurrentMonotonicMicros();
-
// Abandon the remainder of the bump allocation block.
AbandonBumpAllocation();
// Reset the freelists and setup sweeping.
@@ -1233,19 +1209,15 @@
freelists_[i].Reset();
}
- int64_t mid2 = OS::GetCurrentMonotonicMicros();
- int64_t mid3 = 0;
+ if (FLAG_verify_before_gc) {
+ OS::PrintErr("Verifying before sweeping...");
+ heap_->VerifyGC(kAllowMarked);
+ OS::PrintErr(" done.\n");
+ }
{
- if (FLAG_verify_before_gc) {
- OS::PrintErr("Verifying before sweeping...");
- heap_->VerifyGC(kAllowMarked);
- OS::PrintErr(" done.\n");
- }
-
// Executable pages are always swept immediately to simplify
// code protection.
-
TIMELINE_FUNCTION_GC_DURATION(thread, "SweepExecutable");
GCSweeper sweeper;
OldPage* prev_page = NULL;
@@ -1263,8 +1235,6 @@
// Advance to the next page.
page = next_page;
}
-
- mid3 = OS::GetCurrentMonotonicMicros();
}
bool has_reservation = MarkReservation();
@@ -1315,13 +1285,6 @@
page_space_controller_.EvaluateGarbageCollection(
usage_before, GetCurrentUsage(), start, end);
- heap_->RecordTime(kConcurrentSweep, pre_safe_point - pre_wait_for_sweepers);
- heap_->RecordTime(kSafePoint, start - pre_safe_point);
- heap_->RecordTime(kMarkObjects, mid1 - start);
- heap_->RecordTime(kResetFreeLists, mid2 - mid1);
- heap_->RecordTime(kSweepPages, mid3 - mid2);
- heap_->RecordTime(kSweepLargePages, end - mid3);
-
if (FLAG_print_free_list_after_gc) {
for (intptr_t i = 0; i < num_freelists_; i++) {
OS::PrintErr("After GC: Freelist %" Pd "\n", i);
@@ -1748,8 +1711,8 @@
if (FLAG_log_growth || FLAG_verbose_gc) {
THR_Print("%s: threshold=%" Pd "kB, idle_threshold=%" Pd "kB, reason=%s\n",
heap_->isolate_group()->source()->name,
- hard_gc_threshold_in_words_ / KBInWords,
- idle_gc_threshold_in_words_ / KBInWords, reason);
+ RoundWordsToKB(hard_gc_threshold_in_words_),
+ RoundWordsToKB(idle_gc_threshold_in_words_), reason);
}
}
diff --git a/runtime/vm/heap/pages.h b/runtime/vm/heap/pages.h
index 5a3c5c0..fca17a2 100644
--- a/runtime/vm/heap/pages.h
+++ b/runtime/vm/heap/pages.h
@@ -408,7 +408,7 @@
OldPage::PageType type) const;
// Collect the garbage in the page space using mark-sweep or mark-compact.
- void CollectGarbage(bool compact, bool finalize);
+ void CollectGarbage(Thread* thread, bool compact, bool finalize);
void AddRegionsToObjectSet(ObjectSet* set) const;
@@ -573,10 +573,7 @@
void FreeLargePage(OldPage* page, OldPage* previous_page);
void FreePages(OldPage* pages);
- void CollectGarbageHelper(bool compact,
- bool finalize,
- int64_t pre_wait_for_sweepers,
- int64_t pre_safe_point);
+ void CollectGarbageHelper(Thread* thread, bool compact, bool finalize);
void SweepLarge();
void Sweep(bool exclusive);
void ConcurrentSweep(IsolateGroup* isolate_group);
diff --git a/runtime/vm/heap/pointer_block.cc b/runtime/vm/heap/pointer_block.cc
index daedab8..7d361d4 100644
--- a/runtime/vm/heap/pointer_block.cc
+++ b/runtime/vm/heap/pointer_block.cc
@@ -228,6 +228,11 @@
return (full_.length() + partial_.length()) > kMaxNonEmpty;
}
+intptr_t StoreBuffer::Size() {
+ ASSERT(Thread::Current()->IsAtSafepoint()); // No lock needed.
+ return full_.length() + partial_.length();
+}
+
void StoreBuffer::VisitObjectPointers(ObjectPointerVisitor* visitor) {
for (Block* block = full_.Peek(); block != NULL; block = block->next()) {
block->VisitObjectPointers(visitor);
diff --git a/runtime/vm/heap/pointer_block.h b/runtime/vm/heap/pointer_block.h
index fb10fa6..1b6fd5a 100644
--- a/runtime/vm/heap/pointer_block.h
+++ b/runtime/vm/heap/pointer_block.h
@@ -268,6 +268,7 @@
// Check whether non-empty blocks have exceeded kMaxNonEmpty (but takes no
// action).
bool Overflowed();
+ intptr_t Size();
void VisitObjectPointers(ObjectPointerVisitor* visitor);
};
diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc
index c2e8d9a..09e0c04 100644
--- a/runtime/vm/heap/safepoint.cc
+++ b/runtime/vm/heap/safepoint.cc
@@ -63,7 +63,7 @@
ASSERT(T->CanCollectGarbage());
// Check if we passed the growth limit during the scope.
if (heap->old_space()->ReachedHardThreshold()) {
- heap->CollectGarbage(GCType::kMarkSweep, GCReason::kOldSpace);
+ heap->CollectGarbage(T, GCType::kMarkSweep, GCReason::kOldSpace);
} else {
heap->CheckStartConcurrentMarking(T, GCReason::kOldSpace);
}
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index d2f7de4..0648d60 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1731,24 +1731,20 @@
return tail_->TryAllocateGC(size);
}
-void Scavenger::Scavenge(GCReason reason) {
+void Scavenger::Scavenge(Thread* thread, GCType type, GCReason reason) {
int64_t start = OS::GetCurrentMonotonicMicros();
- // Ensure that all threads for this isolate are at a safepoint (either stopped
- // or in native code). If two threads are racing at this point, the loser
- // will continue with its scavenge after waiting for the winner to complete.
- // TODO(koda): Consider moving SafepointThreads into allocation failure/retry
- // logic to avoid needless collections.
- Thread* thread = Thread::Current();
- GcSafepointOperationScope safepoint_scope(thread);
-
- int64_t safe_point = OS::GetCurrentMonotonicMicros();
- heap_->RecordTime(kSafePoint, safe_point - start);
+ ASSERT(thread->IsAtSafepoint());
// Scavenging is not reentrant. Make sure that is the case.
ASSERT(!scavenging_);
scavenging_ = true;
+ if (type == GCType::kEvacuate) {
+ // Forces the next scavenge to promote all the objects in the new space.
+ early_tenure_ = true;
+ }
+
if (FLAG_verify_before_gc) {
OS::PrintErr("Verifying before Scavenge...");
heap_->WaitForSweeperTasksAtSafepoint(thread);
@@ -1810,6 +1806,11 @@
// Done scavenging. Reset the marker.
ASSERT(scavenging_);
scavenging_ = false;
+
+ // It is possible for objects to stay in the new space
+ // if the VM cannot create more pages for these objects.
+ ASSERT((type != GCType::kEvacuate) || (UsedInWords() == 0) ||
+ failed_to_promote_);
}
intptr_t Scavenger::SerialScavenge(SemiSpace* from) {
@@ -1973,23 +1974,4 @@
}
#endif // !PRODUCT
-void Scavenger::Evacuate(GCReason reason) {
- // We need a safepoint here to prevent allocation right before or right after
- // the scavenge.
- // The former can introduce an object that we might fail to collect.
- // The latter means even if the scavenge promotes every object in the new
- // space, the new allocation means the space is not empty,
- // causing the assertion below to fail.
- GcSafepointOperationScope scope(Thread::Current());
-
- // Forces the next scavenge to promote all the objects in the new space.
- early_tenure_ = true;
-
- Scavenge(reason);
-
- // It is possible for objects to stay in the new space
- // if the VM cannot create more pages for these objects.
- ASSERT((UsedInWords() == 0) || failed_to_promote_);
-}
-
} // namespace dart
diff --git a/runtime/vm/heap/scavenger.h b/runtime/vm/heap/scavenger.h
index 5a93aec..229912a 100644
--- a/runtime/vm/heap/scavenger.h
+++ b/runtime/vm/heap/scavenger.h
@@ -277,10 +277,7 @@
void AbandonRemainingTLABForDebugging(Thread* thread);
// Collect the garbage in this scavenger.
- void Scavenge(GCReason reason);
-
- // Promote all live objects.
- void Evacuate(GCReason reason);
+ void Scavenge(Thread* thread, GCType type, GCReason reason);
int64_t UsedInWords() const {
MutexLocker ml(&space_lock_);
diff --git a/runtime/vm/heap/spaces.h b/runtime/vm/heap/spaces.h
index c1c0663..2b9c00f 100644
--- a/runtime/vm/heap/spaces.h
+++ b/runtime/vm/heap/spaces.h
@@ -31,6 +31,7 @@
enum class GCType {
kScavenge,
+ kEvacuate,
kStartConcurrentMark,
kMarkSweep,
kMarkCompact,
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index 1552a72..b4e8514 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -627,6 +627,44 @@
return nullptr;
}
+#if (defined(DART_TARGET_OS_MACOS) || defined(DART_TARGET_OS_MACOS_IOS)) && \
+ defined(TARGET_ARCH_ARM64)
+// When generating ARM64 Mach-O LLVM tends to generate Compact Unwind Info
+// (__unwind_info) rather than traditional DWARF unwinding information
+// (__eh_frame).
+//
+// Unfortunately when generating __unwind_info LLVM seems to only apply CFI
+// rules to the region between two non-local symbols that contains these CFI
+// directives. In other words given:
+//
+// Abc:
+// .cfi_startproc
+// .cfi_def_cfa x29, 16
+// .cfi_offset x29, -16
+// .cfi_offset x30, -8
+// ;; ...
+// Xyz:
+// ;; ...
+// .cfi_endproc
+//
+// __unwind_info would specify proper unwinding information only for the region
+// between Abc and Xyz symbols. And the region Xyz onwards will have no
+// unwinding information.
+//
+// There also seems to be a difference in how unwinding information is
+// canonicalized and compressed: when building __unwind_info from CFI directives
+// LLVM will fold together similar entries, the same does not happen for
+// __eh_frame. This means that emitting CFI directives for each function would
+// baloon the size of __eh_frame.
+//
+// Hence to work around the problem of incorrect __unwind_info without balooning
+// snapshot size when __eh_frame is generated we choose to emit CFI directives
+// per function specifically on ARM64 Mac OS X and iOS.
+//
+// See also |useCompactUnwind| method in LLVM (https://github.com/llvm/llvm-project/blob/b27430f9f46b88bcd54d992debc8d72e131e1bd0/llvm/lib/MC/MCObjectFileInfo.cpp#L28-L50)
+#define EMIT_UNWIND_DIRECTIVES_PER_FUNCTION 1
+#endif
+
void ImageWriter::WriteText(bool vm) {
const bool bare_instruction_payloads = FLAG_precompiled_mode;
@@ -728,7 +766,9 @@
}
#endif
+#if !defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
FrameUnwindPrologue();
+#endif
#if defined(DART_PRECOMPILER)
PcDescriptors& descriptors = PcDescriptors::Handle(zone_);
@@ -795,6 +835,10 @@
AddCodeSymbol(code, object_name, text_offset);
#endif
+#if defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
+ FrameUnwindPrologue();
+#endif
+
{
NoSafepointScope no_safepoint;
@@ -847,6 +891,9 @@
text_offset += AlignWithBreakInstructions(alignment, text_offset);
ASSERT_EQUAL(text_offset - instr_start, SizeInSnapshot(insns.ptr()));
+#if defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
+ FrameUnwindEpilogue();
+#endif
}
// Should be a no-op unless writing bare instruction payloads, in which case
@@ -860,7 +907,9 @@
ASSERT_EQUAL(text_offset, image_size);
+#if !defined(EMIT_UNWIND_DIRECTIVES_PER_FUNCTION)
FrameUnwindEpilogue();
+#endif
ExitSection(ProgramSection::Text, vm, text_offset);
}
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 5b7eaad..a71c5f5 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -1807,9 +1807,9 @@
// an individual isolate.
class NoActiveIsolateScope : public StackResource {
public:
- NoActiveIsolateScope()
- : StackResource(Thread::Current()),
- thread_(static_cast<Thread*>(thread())) {
+ NoActiveIsolateScope() : NoActiveIsolateScope(Thread::Current()) {}
+ explicit NoActiveIsolateScope(Thread* thread)
+ : StackResource(thread), thread_(thread) {
saved_isolate_ = thread_->isolate_;
thread_->isolate_ = nullptr;
}
diff --git a/runtime/vm/json_stream.h b/runtime/vm/json_stream.h
index 5b37379..3a6b523 100644
--- a/runtime/vm/json_stream.h
+++ b/runtime/vm/json_stream.h
@@ -173,6 +173,7 @@
}
void PrintCommaIfNeeded() { writer_.PrintCommaIfNeeded(); }
+ JSONWriter* writer() { return &writer_; }
private:
void Clear() { writer_.Clear(); }
diff --git a/runtime/vm/metrics_test.cc b/runtime/vm/metrics_test.cc
index 5a9ebf7..f87ba5d 100644
--- a/runtime/vm/metrics_test.cc
+++ b/runtime/vm/metrics_test.cc
@@ -78,7 +78,7 @@
ISOLATE_UNIT_TEST_CASE(Metric_EmbedderAPI) {
{
- TransitionVMToNative transition(Thread::Current());
+ TransitionVMToNative transition(thread);
const char* kScript = "void main() {}";
Dart_Handle api_lib = TestCase::LoadTestScript(
@@ -88,15 +88,16 @@
// Ensure we've done new/old GCs to ensure max metrics are initialized.
String::New("<land-in-new-space>", Heap::kNew);
- IsolateGroup::Current()->heap()->new_space()->Scavenge(GCReason::kDebugging);
- IsolateGroup::Current()->heap()->CollectAllGarbage(GCReason::kDebugging,
- /*compact=*/ true);
+ thread->heap()->CollectGarbage(thread, GCType::kScavenge,
+ GCReason::kDebugging);
+ thread->heap()->CollectGarbage(thread, GCType::kMarkCompact,
+ GCReason::kDebugging);
// Ensure we've something live in new space.
String::New("<land-in-new-space2>", Heap::kNew);
{
- TransitionVMToNative transition(Thread::Current());
+ TransitionVMToNative transition(thread);
Dart_Isolate isolate = Dart_CurrentIsolate();
#if !defined(PRODUCT)
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 72b9021..4d3fa4d 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -4473,13 +4473,13 @@
auto isolate_group = thread->isolate_group();
if (js->HasParam("gc")) {
if (js->ParamIs("gc", "scavenge")) {
- isolate_group->heap()->CollectGarbage(GCType::kScavenge,
+ isolate_group->heap()->CollectGarbage(thread, GCType::kScavenge,
GCReason::kDebugging);
} else if (js->ParamIs("gc", "mark-sweep")) {
- isolate_group->heap()->CollectGarbage(GCType::kMarkSweep,
+ isolate_group->heap()->CollectGarbage(thread, GCType::kMarkSweep,
GCReason::kDebugging);
} else if (js->ParamIs("gc", "mark-compact")) {
- isolate_group->heap()->CollectGarbage(GCType::kMarkCompact,
+ isolate_group->heap()->CollectGarbage(thread, GCType::kMarkCompact,
GCReason::kDebugging);
} else {
PrintInvalidParamError(js, "gc");
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index e21a66c..9f3a5bf 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -437,7 +437,7 @@
if ((interrupt_bits & kVMInterrupt) != 0) {
CheckForSafepoint();
if (isolate_group()->store_buffer()->Overflowed()) {
- heap()->CollectGarbage(GCType::kScavenge, GCReason::kStoreBuffer);
+ heap()->CollectGarbage(this, GCType::kScavenge, GCReason::kStoreBuffer);
}
#if !defined(PRODUCT)
diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
index c242cda..fc57084 100644
--- a/runtime/vm/timeline.cc
+++ b/runtime/vm/timeline.cc
@@ -105,10 +105,6 @@
if (use_systrace_recorder || (flag != NULL)) {
if (use_systrace_recorder || (strcmp("systrace", flag) == 0)) {
- if (FLAG_trace_timeline) {
- THR_Print("Using the Systrace timeline recorder.\n");
- }
-
#if defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID)
return new TimelineEventSystraceRecorder();
#elif defined(DART_HOST_OS_MACOS)
@@ -128,24 +124,22 @@
if (use_endless_recorder || (flag != NULL)) {
if (use_endless_recorder || (strcmp("endless", flag) == 0)) {
- if (FLAG_trace_timeline) {
- THR_Print("Using the endless timeline recorder.\n");
- }
return new TimelineEventEndlessRecorder();
}
}
if (use_startup_recorder || (flag != NULL)) {
if (use_startup_recorder || (strcmp("startup", flag) == 0)) {
- if (FLAG_trace_timeline) {
- THR_Print("Using the startup recorder.\n");
- }
return new TimelineEventStartupRecorder();
}
}
- if (FLAG_trace_timeline) {
- THR_Print("Using the ring timeline recorder.\n");
+ if (strcmp("file", flag) == 0) {
+ return new TimelineEventFileRecorder("dart-timeline.json");
+ }
+ if (Utils::StrStartsWith(flag, "file:") ||
+ Utils::StrStartsWith(flag, "file=")) {
+ return new TimelineEventFileRecorder(&flag[5]);
}
// Always fall back to the ring recorder.
@@ -202,6 +196,9 @@
void Timeline::Init() {
ASSERT(recorder_ == NULL);
recorder_ = CreateTimelineRecorder();
+ if (FLAG_trace_timeline) {
+ OS::PrintErr("Using the %s timeline recorder.\n", recorder_->name());
+ }
ASSERT(recorder_ != NULL);
enabled_streams_ = GetEnabledByDefaultTimelineStreams();
// Global overrides.
@@ -610,65 +607,70 @@
#ifndef PRODUCT
void TimelineEvent::PrintJSON(JSONStream* stream) const {
- JSONObject obj(stream);
+ PrintJSON(stream->writer());
+}
+#endif
+
+void TimelineEvent::PrintJSON(JSONWriter* writer) const {
+ writer->OpenObject();
int64_t pid = OS::ProcessId();
int64_t tid = OSThread::ThreadIdToIntPtr(thread_);
- obj.AddProperty("name", label_);
- obj.AddProperty("cat", stream_ != NULL ? stream_->name() : NULL);
- obj.AddProperty64("tid", tid);
- obj.AddProperty64("pid", pid);
- obj.AddPropertyTimeMicros("ts", TimeOrigin());
+ writer->PrintProperty("name", label_);
+ writer->PrintProperty("cat", stream_ != NULL ? stream_->name() : NULL);
+ writer->PrintProperty64("tid", tid);
+ writer->PrintProperty64("pid", pid);
+ writer->PrintProperty64("ts", TimeOrigin());
if (HasThreadCPUTime()) {
- obj.AddPropertyTimeMicros("tts", ThreadCPUTimeOrigin());
+ writer->PrintProperty64("tts", ThreadCPUTimeOrigin());
}
switch (event_type()) {
case kBegin: {
- obj.AddProperty("ph", "B");
+ writer->PrintProperty("ph", "B");
} break;
case kEnd: {
- obj.AddProperty("ph", "E");
+ writer->PrintProperty("ph", "E");
} break;
case kDuration: {
- obj.AddProperty("ph", "X");
- obj.AddPropertyTimeMicros("dur", TimeDuration());
+ writer->PrintProperty("ph", "X");
+ writer->PrintProperty64("dur", TimeDuration());
if (HasThreadCPUTime()) {
- obj.AddPropertyTimeMicros("tdur", ThreadCPUTimeDuration());
+ writer->PrintProperty64("tdur", ThreadCPUTimeDuration());
}
} break;
case kInstant: {
- obj.AddProperty("ph", "i");
- obj.AddProperty("s", "p");
+ writer->PrintProperty("ph", "i");
+ writer->PrintProperty("s", "p");
} break;
case kAsyncBegin: {
- obj.AddProperty("ph", "b");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "b");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kAsyncInstant: {
- obj.AddProperty("ph", "n");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "n");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kAsyncEnd: {
- obj.AddProperty("ph", "e");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "e");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kCounter: {
- obj.AddProperty("ph", "C");
+ writer->PrintProperty("ph", "C");
} break;
case kFlowBegin: {
- obj.AddProperty("ph", "s");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "s");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kFlowStep: {
- obj.AddProperty("ph", "t");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "t");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kFlowEnd: {
- obj.AddProperty("ph", "f");
- obj.AddProperty("bp", "e");
- obj.AddPropertyF("id", "%" Px64 "", AsyncId());
+ writer->PrintProperty("ph", "f");
+ writer->PrintProperty("bp", "e");
+ writer->PrintfProperty("id", "%" Px64 "", AsyncId());
} break;
case kMetadata: {
- obj.AddProperty("ph", "M");
+ writer->PrintProperty("ph", "M");
} break;
default:
UNIMPLEMENTED();
@@ -676,42 +678,43 @@
if (pre_serialized_args()) {
ASSERT(arguments_.length() == 1);
- stream->AppendSerializedObject("args", arguments_[0].value);
+ writer->AppendSerializedObject("args", arguments_[0].value);
if (isolate_id_ != ILLEGAL_PORT) {
- stream->UncloseObject();
- stream->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
+ writer->UncloseObject();
+ writer->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
static_cast<int64_t>(isolate_id_));
- stream->CloseObject();
+ writer->CloseObject();
}
if (isolate_group_id_ != 0) {
- stream->UncloseObject();
- stream->PrintfProperty("isolateGroupId",
+ writer->UncloseObject();
+ writer->PrintfProperty("isolateGroupId",
ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
isolate_group_id_);
- stream->CloseObject();
+ writer->CloseObject();
} else {
ASSERT(isolate_group_id_ == ILLEGAL_PORT);
}
} else {
- JSONObject args(&obj, "args");
+ writer->OpenObject("args");
for (intptr_t i = 0; i < arguments_.length(); i++) {
const TimelineEventArgument& arg = arguments_[i];
- args.AddProperty(arg.name, arg.value);
+ writer->PrintProperty(arg.name, arg.value);
}
if (isolate_id_ != ILLEGAL_PORT) {
- args.AddPropertyF("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
- static_cast<int64_t>(isolate_id_));
+ writer->PrintfProperty("isolateId", ISOLATE_SERVICE_ID_FORMAT_STRING,
+ static_cast<int64_t>(isolate_id_));
}
if (isolate_group_id_ != 0) {
- args.AddPropertyF("isolateGroupId",
- ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
- isolate_group_id_);
+ writer->PrintfProperty("isolateGroupId",
+ ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING,
+ isolate_group_id_);
} else {
ASSERT(isolate_group_id_ == ILLEGAL_PORT);
}
+ writer->CloseObject();
}
+ writer->CloseObject();
}
-#endif
int64_t TimelineEvent::TimeOrigin() const {
return timestamp0_;
@@ -1347,6 +1350,132 @@
delete event;
}
+static void TimelineEventFileRecorderStart(uword parameter) {
+ reinterpret_cast<TimelineEventFileRecorder*>(parameter)->Drain();
+}
+
+TimelineEventFileRecorder::TimelineEventFileRecorder(const char* path)
+ : TimelineEventPlatformRecorder(),
+ monitor_(),
+ head_(nullptr),
+ tail_(nullptr),
+ file_(nullptr),
+ first_(true),
+ shutting_down_(false),
+ thread_id_(OSThread::kInvalidThreadJoinId) {
+ Dart_FileOpenCallback file_open = Dart::file_open_callback();
+ Dart_FileWriteCallback file_write = Dart::file_write_callback();
+ Dart_FileCloseCallback file_close = Dart::file_close_callback();
+ if ((file_open == nullptr) || (file_write == nullptr) ||
+ (file_close == nullptr)) {
+ OS::PrintErr("warning: Could not access file callbacks.");
+ return;
+ }
+ void* file = (*file_open)(path, true);
+ if (file == nullptr) {
+ OS::PrintErr("warning: Failed to open timeline file: %s\n", path);
+ return;
+ }
+
+ file_ = file;
+ // Chrome trace format has two forms:
+ // Object form: { "traceEvents": [ event, event, event ] }
+ // Array form: [ event, event, event ]
+ // For this recorder, we use the array form because Catapult will handle a
+ // missing ending bracket in this form in case we don't cleanly end the
+ // trace.
+ Write("[\n");
+ OSThread::Start("TimelineEventFileRecorder", TimelineEventFileRecorderStart,
+ reinterpret_cast<uword>(this));
+}
+
+TimelineEventFileRecorder::~TimelineEventFileRecorder() {
+ if (file_ == nullptr) return;
+
+ {
+ MonitorLocker ml(&monitor_);
+ shutting_down_ = true;
+ ml.Notify();
+ }
+
+ ASSERT(thread_id_ != OSThread::kInvalidThreadJoinId);
+ OSThread::Join(thread_id_);
+ thread_id_ = OSThread::kInvalidThreadJoinId;
+
+ TimelineEvent* event = head_;
+ while (event != nullptr) {
+ TimelineEvent* next = event->next();
+ delete event;
+ event = next;
+ }
+ head_ = tail_ = nullptr;
+
+ Write("]\n");
+ Dart_FileCloseCallback file_close = Dart::file_close_callback();
+ (*file_close)(file_);
+ file_ = nullptr;
+}
+
+void TimelineEventFileRecorder::CompleteEvent(TimelineEvent* event) {
+ if (event == nullptr) {
+ return;
+ }
+ if (file_ == nullptr) {
+ delete event;
+ return;
+ }
+
+ MonitorLocker ml(&monitor_);
+ ASSERT(!shutting_down_);
+ event->set_next(nullptr);
+ if (tail_ == nullptr) {
+ head_ = tail_ = event;
+ } else {
+ tail_->set_next(event);
+ tail_ = event;
+ }
+ ml.Notify();
+}
+
+void TimelineEventFileRecorder::Drain() {
+ MonitorLocker ml(&monitor_);
+ thread_id_ = OSThread::GetCurrentThreadJoinId(OSThread::Current());
+ while (!shutting_down_) {
+ if (head_ == nullptr) {
+ ml.Wait();
+ continue; // Recheck empty and shutting down.
+ }
+ TimelineEvent* event = head_;
+ TimelineEvent* next = event->next();
+ head_ = next;
+ if (next == nullptr) {
+ tail_ = nullptr;
+ }
+ ml.Exit();
+ {
+ JSONWriter writer;
+ if (first_) {
+ first_ = false;
+ } else {
+ writer.buffer()->AddChar(',');
+ }
+ event->PrintJSON(&writer);
+ char* output = NULL;
+ intptr_t output_length = 0;
+ writer.Steal(&output, &output_length);
+ Write(output, output_length);
+ free(output);
+ delete event;
+ }
+ ml.Enter();
+ }
+}
+
+void TimelineEventFileRecorder::Write(const char* buffer, intptr_t len) {
+ Dart_FileWriteCallback file_write = Dart::file_write_callback();
+ (*file_write)(buffer, len, file_);
+}
+
TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
: head_(nullptr), tail_(nullptr), block_index_(0) {}
diff --git a/runtime/vm/timeline.h b/runtime/vm/timeline.h
index 6d5c062..8f1df56 100644
--- a/runtime/vm/timeline.h
+++ b/runtime/vm/timeline.h
@@ -36,6 +36,7 @@
class JSONArray;
class JSONObject;
class JSONStream;
+class JSONWriter;
class Object;
class ObjectPointerVisitor;
class Isolate;
@@ -49,6 +50,7 @@
#define CALLBACK_RECORDER_NAME "Callback"
#define ENDLESS_RECORDER_NAME "Endless"
+#define FILE_RECORDER_NAME "File"
#define FUCHSIA_RECORDER_NAME "Fuchsia"
#define MACOS_RECORDER_NAME "Macos"
#define RING_RECORDER_NAME "Ring"
@@ -392,6 +394,7 @@
#ifndef PRODUCT
void PrintJSON(JSONStream* stream) const;
#endif
+ void PrintJSON(JSONWriter* writer) const;
ThreadId thread() const { return thread_; }
@@ -456,6 +459,13 @@
intptr_t arguments_length() const { return arguments_.length(); }
+ TimelineEvent* next() const {
+ return next_;
+ }
+ void set_next(TimelineEvent* next) {
+ next_ = next;
+ }
+
private:
void StreamInit(TimelineStream* stream) { stream_ = stream; }
void Init(EventType event_type, const char* label);
@@ -518,6 +528,7 @@
ThreadId thread_;
Dart_Port isolate_id_;
uint64_t isolate_group_id_;
+ TimelineEvent* next_;
friend class TimelineEventRecorder;
friend class TimelineEventEndlessRecorder;
@@ -1006,6 +1017,31 @@
};
#endif // defined(DART_HOST_OS_MACOS)
+class TimelineEventFileRecorder : public TimelineEventPlatformRecorder {
+ public:
+ explicit TimelineEventFileRecorder(const char* path);
+ virtual ~TimelineEventFileRecorder();
+
+ const char* name() const { return FILE_RECORDER_NAME; }
+ intptr_t Size() { return 0; }
+
+ void Drain();
+
+ private:
+ void CompleteEvent(TimelineEvent* event);
+ void OnEvent(TimelineEvent* event) { UNREACHABLE(); }
+ void Write(const char* buffer) { Write(buffer, strlen(buffer)); }
+ void Write(const char* buffer, intptr_t len);
+
+ Monitor monitor_;
+ TimelineEvent* head_;
+ TimelineEvent* tail_;
+ void* file_;
+ bool first_;
+ bool shutting_down_;
+ ThreadJoinId thread_id_;
+};
+
class DartTimelineEventHelpers : public AllStatic {
public:
static void ReportTaskEvent(Thread* thread,
diff --git a/tools/VERSION b/tools/VERSION
index 74dc52d..20084da 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 32
+PRERELEASE 33
PRERELEASE_PATCH 0
\ No newline at end of file