Version 2.18.0-45.0.dev
Merge commit '9846fe2621b25e6c183dbc3a6536a0b1ae39fb86' into 'dev'
diff --git a/.github/ISSUE_TEMPLATE/1_cherry_pick.yml b/.github/ISSUE_TEMPLATE/1_cherry_pick.yml
new file mode 100644
index 0000000..a1c9afd
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/1_cherry_pick.yml
@@ -0,0 +1,69 @@
+name: Request a cherry-pick.
+description: You would like to request that a feature be cherry-picked into a release.
+title: '[CP] <title>'
+labels: ['cherry-pick-review']
+assignees:
+ - mit-mit
+ - whesse
+ - athomas
+ - vsmenon
+ - itsjustkevin
+body:
+ - type: input
+ id: commit_hash
+ attributes:
+ label: Commit(s) to merge
+ description: What are the shortened commit hash(es) that have been merged to main?
+ validations:
+ required: true
+ - type: input
+ id: target
+ attributes:
+ label: Target
+ description: Should this be cherry-picked to beta, stable or both??
+ validations:
+ required: true
+ - type: textarea
+ id: issue_description
+ attributes:
+ label: Issue Description
+ description: Brief description of the issue. What is the issue? What platforms are the problems occuring on?
+ validations:
+ required: true
+ - type: textarea
+ id: fix
+ attributes:
+ label: What is the fix
+ description: Brief description of the fix.
+ validations:
+ required: true
+ - type: textarea
+ id: why
+ attributes:
+ label: Why cherry-pick
+ description: Describe the reasons, impacted users and functional issues to explain why this should be cherry-picked.
+ validations:
+ required: true
+ - type: dropdown
+ id: risk
+ attributes:
+ label: Risk
+ description: What is the risk level of this cherry-pick?
+ options:
+ - low
+ - medium
+ - high
+ validations:
+ required: true
+ - type: input
+ id: original_issue
+ attributes:
+ label: Issue link(s)
+ description: Add links to the original issues fixed by this cp
+ validations:
+ required: true
+ - type: textarea
+ id: extra_info
+ attributes:
+ label: Extra Info
+ description: Is there anything else we need to know about this cherry-pick?
diff --git a/DEPS b/DEPS
index f2884cf..7b77af9 100644
--- a/DEPS
+++ b/DEPS
@@ -77,7 +77,7 @@
# Revisions of /third_party/* dependencies.
"args_rev": "3b3f55766af13d895d2020ec001a28e8dc147f91",
- "async_rev": "3f58c326bd4928fca9ddc10c72b19cb7ac659256",
+ "async_rev": "f3ed5f690e2ec9dbe1bfc5184705575b4f6480e5",
"bazel_worker_rev": "ceeba0982d4ff40d32371c9d35f3d2dc1868de20",
"benchmark_harness_rev": "c546dbd9f639f75cd2f75de8df2eb9f8ea15e8e7",
"boolean_selector_rev": "437e7f06c7e416bed91e16ae1df453555897e945",
@@ -133,7 +133,6 @@
"oauth2_rev": "7cd3284049fe5badbec9f2bea2afc41d14c01057",
"package_config_rev": "8731bf10b5375542792a32a0f7c8a6f370583d96",
"path_rev": "baedce9d2ca11ea2cdf54395a74eb038087777a4",
- "pedantic_rev": "66f2f6c27581c7936482e83be80b27be2719901c",
"platform_rev": "1ffad63428bbd1b3ecaa15926bacfb724023648c",
"ply_rev": "604b32590ffad5cbb82e4afef1d305512d06ae93",
"pool_rev": "7abe634002a1ba8a0928eded086062f1307ccfae",
@@ -160,7 +159,7 @@
"test_descriptor_rev": "ead23c1e7df079ac0f6457a35f7a71432892e527",
"test_process_rev": "7c73ec8a8a6e0e63d0ec27d70c21ca4323fb5e8f",
"test_reflective_loader_rev": "fcfce37666672edac849d2af6dffc0f8df236a94",
- "test_rev": "b6aba5544628730b7d6a38eae1aef9117a1bb235",
+ "test_rev": "d54846bc2b5cfa4e1445fda85c5e48a00940aa68",
"typed_data_rev": "29ce5a92b03326d0b8035916ac04f528874994bd",
"usage_rev": "e85d575d6decb921c57a43b9844bba3607479f56",
"vector_math_rev": "0cbed0914d49a6a44555e6d5444c438a4a4c3fc1",
@@ -380,8 +379,6 @@
"@" + Var("package_config_rev"),
Var("dart_root") + "/third_party/pkg/path":
Var("dart_git") + "path.git" + "@" + Var("path_rev"),
- Var("dart_root") + "/third_party/pkg/pedantic":
- Var("dart_git") + "pedantic.git" + "@" + Var("pedantic_rev"),
Var("dart_root") + "/third_party/pkg/platform":
Var("dart_git") + "platform.dart.git" + "@" + Var("platform_rev"),
Var("dart_root") + "/third_party/pkg/pool":
diff --git a/benchmarks/Startup/dart/Startup.dart b/benchmarks/Startup/dart/Startup.dart
index 49889fb..7beaede 100644
--- a/benchmarks/Startup/dart/Startup.dart
+++ b/benchmarks/Startup/dart/Startup.dart
@@ -76,7 +76,7 @@
print(ends.toList());
throw '$name is missing or ambiguous';
}
- print('Startup.$name(RunTime): $micros us.');
+ print('Startup.$name(RunTimeRaw): $micros us.');
}
report('CreateIsolateGroupAndSetupHelper', null);
diff --git a/benchmarks/Startup/dart2/Startup.dart b/benchmarks/Startup/dart2/Startup.dart
index 26fb07b..3220928 100644
--- a/benchmarks/Startup/dart2/Startup.dart
+++ b/benchmarks/Startup/dart2/Startup.dart
@@ -78,7 +78,7 @@
print(ends.toList());
throw '$name is missing or ambiguous';
}
- print('Startup.$name(RunTime): $micros us.');
+ print('Startup.$name(RunTimeRaw): $micros us.');
}
report('CreateIsolateGroupAndSetupHelper', null);
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
index de81796..cd0889c 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/code.dart
@@ -57,16 +57,21 @@
FunctionBodyCode.fromParts(List<Object> parts) : super.fromParts(parts);
}
-/// A piece of code identifying a syntactically valid function parameter.
+/// A piece of code identifying a syntactically valid function or function type
+/// parameter.
///
/// There is no distinction here made between named and positional parameters.
///
+/// There is also no distinction between function type parameters and normal
+/// function parameters, so the [name] is nullable (it is not required for
+/// positional function type parameters).
+///
/// It is the job of the user to construct and combine these together in a way
/// that creates valid parameter lists.
class ParameterCode implements Code {
final Code? defaultValue;
final List<String> keywords;
- final String name;
+ final String? name;
final TypeAnnotationCode? type;
@override
@@ -82,7 +87,7 @@
type!,
' ',
],
- name,
+ if (name != null) name!,
if (defaultValue != null) ...[
' = ',
defaultValue!,
@@ -92,7 +97,7 @@
ParameterCode({
this.defaultValue,
this.keywords = const [],
- required this.name,
+ this.name,
this.type,
});
}
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
index cc9ef15..131c21e 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/api/introspection.dart
@@ -35,10 +35,10 @@
TypeAnnotation get returnType;
/// The positional parameters for this function.
- Iterable<ParameterDeclaration> get positionalParameters;
+ Iterable<FunctionTypeParameter> get positionalParameters;
/// The named parameters for this function.
- Iterable<ParameterDeclaration> get namedParameters;
+ Iterable<FunctionTypeParameter> get namedParameters;
/// The type parameters for this function.
Iterable<TypeParameterDeclaration> get typeParameters;
@@ -197,8 +197,9 @@
abstract class FieldDeclaration
implements VariableDeclaration, ClassMemberDeclaration {}
-/// Parameter introspection information.
-abstract class ParameterDeclaration implements Declaration {
+/// General parameter introspection information, see the subtypes
+/// [FunctionTypeParameter] and [ParameterDeclaration].
+abstract class Parameter {
/// The type of this parameter.
TypeAnnotation get type;
@@ -216,6 +217,16 @@
ParameterCode get code;
}
+/// Parameters of normal functions/methods, which always have an identifier.
+abstract class ParameterDeclaration implements Parameter, Declaration {}
+
+/// Function type parameters don't always have names, and it is never useful to
+/// get an [Identifier] for them, so they do not implement [Declaration] and
+/// instead have an optional name.
+abstract class FunctionTypeParameter implements Parameter {
+ String? get name;
+}
+
/// Type parameter introspection information.
abstract class TypeParameterDeclaration implements Declaration {
/// The bound for this type parameter, if it has any.
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
index 29c443b..4aace85 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/introspection_impls.dart
@@ -94,21 +94,21 @@
typeParam.code,
],
positionalParameters: [
- for (ParameterDeclaration positional in positionalParameters)
+ for (FunctionTypeParameter positional in positionalParameters)
positional.code,
],
namedParameters: [
- for (ParameterDeclaration named in namedParameters) named.code,
+ for (FunctionTypeParameter named in namedParameters) named.code,
],
);
return isNullable ? underlyingType.asNullable : underlyingType;
}
@override
- final List<ParameterDeclarationImpl> namedParameters;
+ final List<FunctionTypeParameterImpl> namedParameters;
@override
- final List<ParameterDeclarationImpl> positionalParameters;
+ final List<FunctionTypeParameterImpl> positionalParameters;
@override
final TypeAnnotationImpl returnType;
@@ -137,13 +137,13 @@
returnType.serialize(serializer);
serializer.startList();
- for (ParameterDeclarationImpl param in positionalParameters) {
+ for (FunctionTypeParameterImpl param in positionalParameters) {
param.serialize(serializer);
}
serializer.endList();
serializer.startList();
- for (ParameterDeclarationImpl param in namedParameters) {
+ for (FunctionTypeParameterImpl param in namedParameters) {
param.serialize(serializer);
}
serializer.endList();
@@ -223,6 +223,50 @@
]);
}
+class FunctionTypeParameterImpl extends RemoteInstance
+ implements FunctionTypeParameter {
+ @override
+ final bool isNamed;
+
+ @override
+ final bool isRequired;
+
+ @override
+ final String? name;
+
+ @override
+ final TypeAnnotationImpl type;
+
+ @override
+ RemoteInstanceKind get kind => RemoteInstanceKind.functionTypeParameter;
+
+ FunctionTypeParameterImpl({
+ required int id,
+ required this.isNamed,
+ required this.isRequired,
+ required this.name,
+ required this.type,
+ }) : super(id);
+
+ @override
+ void serialize(Serializer serializer) {
+ super.serialize(serializer);
+ // Client side we don't encode anything but the ID.
+ if (serializationMode.isClient) return;
+
+ serializer.addBool(isNamed);
+ serializer.addBool(isRequired);
+ serializer.addNullableString(name);
+ type.serialize(serializer);
+ }
+
+ @override
+ ParameterCode get code =>
+ new ParameterCode(name: name, type: type.code, keywords: [
+ if (isNamed && isRequired) 'required',
+ ]);
+}
+
class TypeParameterDeclarationImpl extends DeclarationImpl
implements TypeParameterDeclaration {
@override
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
index 4ec67e4..666ff65 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/remote_instance.dart
@@ -95,6 +95,7 @@
fieldDeclaration,
functionDeclaration,
functionTypeAnnotation,
+ functionTypeParameter,
identifier,
identifierResolver,
namedStaticType,
diff --git a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
index c51175a..9ac4f12 100644
--- a/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/macros/executor/serialization_extensions.dart
@@ -40,6 +40,9 @@
case RemoteInstanceKind.functionTypeAnnotation:
moveNext();
return _expectFunctionTypeAnnotation(id) as T;
+ case RemoteInstanceKind.functionTypeParameter:
+ moveNext();
+ return _expectFunctionTypeParameter(id) as T;
case RemoteInstanceKind.identifier:
moveNext();
return _expectIdentifier(id) as T;
@@ -103,6 +106,15 @@
typeParameters: (this..moveNext())._expectRemoteInstanceList(),
);
+ FunctionTypeParameter _expectFunctionTypeParameter(int id) =>
+ new FunctionTypeParameterImpl(
+ id: id,
+ isNamed: expectBool(),
+ isRequired: (this..moveNext()).expectBool(),
+ name: (this..moveNext()).expectNullableString(),
+ type: RemoteInstance.deserialize(this),
+ );
+
Identifier _expectIdentifier(int id) => new IdentifierImpl(
id: id,
name: expectString(),
@@ -294,7 +306,7 @@
return new ParameterCode(
defaultValue: (this..moveNext()).expectNullableCode(),
keywords: _readStringList(),
- name: (this..moveNext()).expectString(),
+ name: (this..moveNext()).expectNullableString(),
type: (this..moveNext()).expectNullableCode()) as T;
case CodeKind.typeParameter:
return new TypeParameterCode(
@@ -384,7 +396,7 @@
}
serializer
..endList()
- ..addString(self.name);
+ ..addNullableString(self.name);
self.type.serializeNullable(serializer);
return;
case CodeKind.typeParameter:
diff --git a/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart b/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
index eb74a8e..b667364 100644
--- a/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
+++ b/pkg/_fe_analyzer_shared/test/macros/executor/serialization_test.dart
@@ -166,6 +166,12 @@
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'foo'),
type: fooType);
+ final fooNamedFunctionTypeParam = FunctionTypeParameterImpl(
+ id: RemoteInstance.uniqueId,
+ isNamed: true,
+ isRequired: true,
+ name: 'foo',
+ type: fooType);
final barPositionalParam = ParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
@@ -174,6 +180,19 @@
identifier:
IdentifierImpl(id: RemoteInstance.uniqueId, name: 'bar'),
type: barType);
+ final barPositionalFunctionTypeParam = FunctionTypeParameterImpl(
+ id: RemoteInstance.uniqueId,
+ isNamed: true,
+ isRequired: true,
+ name: 'bar',
+ type: fooType);
+
+ final unnamedFunctionTypeParam = FunctionTypeParameterImpl(
+ id: RemoteInstance.uniqueId,
+ isNamed: true,
+ isRequired: true,
+ name: null,
+ type: fooType);
final zapTypeParam = TypeParameterDeclarationImpl(
id: RemoteInstance.uniqueId,
@@ -187,8 +206,11 @@
var functionType = FunctionTypeAnnotationImpl(
id: RemoteInstance.uniqueId,
isNullable: true,
- namedParameters: [fooNamedParam],
- positionalParameters: [barPositionalParam],
+ namedParameters: [
+ fooNamedFunctionTypeParam,
+ unnamedFunctionTypeParam
+ ],
+ positionalParameters: [barPositionalFunctionTypeParam],
returnType: fooType,
typeParameters: [zapTypeParam],
);
diff --git a/pkg/_js_interop_checks/lib/js_interop_checks.dart b/pkg/_js_interop_checks/lib/js_interop_checks.dart
index 351c410..34a6b09 100644
--- a/pkg/_js_interop_checks/lib/js_interop_checks.dart
+++ b/pkg/_js_interop_checks/lib/js_interop_checks.dart
@@ -41,6 +41,7 @@
/// Libraries that use `external` to exclude from checks on external.
static final Iterable<String> _pathsWithAllowedDartExternalUsage = <String>[
'_foreign_helper', // for foreign helpers
+ '_late_helper', // for dart2js late variable utilities
'_interceptors', // for ddc JS string
'_native_typed_data',
'_runtime', // for ddc types at runtime
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 44c74aa..3411254 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -50,6 +50,7 @@
import 'package:analysis_server/src/services/completion/completion_state.dart';
import 'package:analysis_server/src/services/execution/execution_context.dart';
import 'package:analysis_server/src/services/flutter/widget_descriptions.dart';
+import 'package:analysis_server/src/services/refactoring/refactoring.dart';
import 'package:analysis_server/src/utilities/process.dart';
import 'package:analysis_server/src/utilities/request_statistics.dart';
import 'package:analyzer/dart/analysis/results.dart';
@@ -144,6 +145,12 @@
/// The state used by the completion domain handlers.
final CompletionState completionState = CompletionState();
+ /// The workspace for rename refactorings.
+ late RefactoringWorkspace refactoringWorkspace;
+
+ /// The object used to manage uncompleted refactorings.
+ late RefactoringManager? _refactoringManager;
+
/// The context used by the execution domain handlers.
final ExecutionContext executionContext = ExecutionContext();
@@ -266,6 +273,8 @@
CompletionDomainHandler(this),
FlutterDomainHandler(this)
];
+ refactoringWorkspace = RefactoringWorkspace(driverMap.values, searchEngine);
+ _newRefactoringManager();
}
/// The analytics instance; note, this object can be `null`.
@@ -292,6 +301,18 @@
return _onAnalysisStartedController.stream;
}
+ RefactoringManager? get refactoringManager {
+ var refactoringManager = _refactoringManager;
+ if (refactoringManager == null) {
+ return null;
+ }
+ if (refactoringManager.hasPendingRequest) {
+ refactoringManager.cancel();
+ _newRefactoringManager();
+ }
+ return _refactoringManager;
+ }
+
String get sdkPath {
return sdkManager.defaultSdkDirectory;
}
@@ -692,6 +713,11 @@
return flutterServices[service]?.contains(file) ?? false;
}
+ /// Initializes [_refactoringManager] with a new instance.
+ void _newRefactoringManager() {
+ _refactoringManager = RefactoringManager(this, refactoringWorkspace);
+ }
+
Future<void> _scheduleAnalysisImplementedNotification() async {
var files = analysisServices[AnalysisService.IMPLEMENTED];
if (files != null) {
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index a7ea8fd..dc3afe6 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -183,10 +183,8 @@
final Map<Folder, AnalysisDriver> driverMap =
HashMap<Folder, AnalysisDriver>();
- /// Stream subscription we are using to watch each analysis root directory for
- /// changes.
- final Map<Folder, StreamSubscription<WatchEvent>> changeSubscriptions =
- <Folder, StreamSubscription<WatchEvent>>{};
+ /// Subscriptions to watch included resources for changes.
+ final List<StreamSubscription<WatchEvent>> watcherSubscriptions = [];
/// For each folder, stores the subscription to the Bazel workspace so that we
/// can establish watches for the generated files.
@@ -468,10 +466,16 @@
var rootFolder = analysisContext.contextRoot.root;
driverMap[rootFolder] = driver;
- var watcher = rootFolder.watch();
- watchers.add(watcher);
- changeSubscriptions[rootFolder] = watcher.changes
- .listen(_handleWatchEvent, onError: _handleWatchInterruption);
+ for (final included in analysisContext.contextRoot.included) {
+ final watcher = included.watch();
+ watchers.add(watcher);
+ watcherSubscriptions.add(
+ watcher.changes.listen(
+ _handleWatchEvent,
+ onError: _handleWatchInterruption,
+ ),
+ );
+ }
_watchBazelFilesIfNeeded(rootFolder, driver);
@@ -582,7 +586,6 @@
void _destroyAnalysisContext(DriverBasedAnalysisContext context) {
context.driver.dispose();
var rootFolder = context.contextRoot.root;
- changeSubscriptions.remove(rootFolder)?.cancel();
var watched = bazelWatchedPathsPerFolder.remove(rootFolder);
if (watched != null) {
for (var path in watched.paths) {
@@ -597,6 +600,9 @@
void _destroyAnalysisContexts() {
var collection = _collection;
if (collection != null) {
+ for (final subscription in watcherSubscriptions) {
+ subscription.cancel();
+ }
for (var analysisContext in collection.contexts) {
_destroyAnalysisContext(analysisContext);
}
diff --git a/pkg/analysis_server/lib/src/edit/edit_domain.dart b/pkg/analysis_server/lib/src/edit/edit_domain.dart
index dd858f1..87660ab 100644
--- a/pkg/analysis_server/lib/src/edit/edit_domain.dart
+++ b/pkg/analysis_server/lib/src/edit/edit_domain.dart
@@ -12,8 +12,10 @@
import 'package:analysis_server/src/handler/legacy/edit_format.dart';
import 'package:analysis_server/src/handler/legacy/edit_format_if_enabled.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_assists.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_available_refactorings.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_fixes.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_postfix_completion.dart';
+import 'package:analysis_server/src/handler/legacy/edit_get_refactoring.dart';
import 'package:analysis_server/src/handler/legacy/edit_get_statement_completion.dart';
import 'package:analysis_server/src/handler/legacy/edit_import_elements.dart';
import 'package:analysis_server/src/handler/legacy/edit_is_postfix_completion_applicable.dart';
@@ -43,19 +45,9 @@
/// Instances of the class [EditDomainHandler] implement a [RequestHandler]
/// that handles requests in the edit domain.
class EditDomainHandler extends AbstractRequestHandler {
- /// The workspace for rename refactorings.
- RefactoringWorkspace? refactoringWorkspace;
-
- /// The object used to manage uncompleted refactorings.
- _RefactoringManager? refactoringManager;
-
/// Initialize a newly created handler to handle requests for the given
/// [server].
- EditDomainHandler(AnalysisServer server) : super(server) {
- refactoringWorkspace =
- RefactoringWorkspace(server.driverMap.values, server.searchEngine);
- _newRefactoringManager();
- }
+ EditDomainHandler(super.server);
@override
Response? handleRequest(
@@ -72,7 +64,8 @@
EditGetAssistsHandler(server, request, cancellationToken).handle();
return Response.DELAYED_RESPONSE;
} else if (requestName == EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS) {
- _getAvailableRefactorings(request);
+ EditGetAvailableRefactoringsHandler(server, request, cancellationToken)
+ .handle();
return Response.DELAYED_RESPONSE;
} else if (requestName == EDIT_REQUEST_BULK_FIXES) {
EditBulkFixes(server, request, cancellationToken).handle();
@@ -81,7 +74,8 @@
EditGetFixesHandler(server, request, cancellationToken).handle();
return Response.DELAYED_RESPONSE;
} else if (requestName == EDIT_REQUEST_GET_REFACTORING) {
- return _getRefactoring(request, cancellationToken);
+ EditGetRefactoringHandler(server, request, cancellationToken).handle();
+ return Response.DELAYED_RESPONSE;
} else if (requestName == EDIT_REQUEST_IMPORT_ELEMENTS) {
EditImportElementsHandler(server, request, cancellationToken).handle();
return Response.DELAYED_RESPONSE;
@@ -117,95 +111,6 @@
}
return null;
}
-
- Future<void> _getAvailableRefactorings(Request request) async {
- var params = EditGetAvailableRefactoringsParams.fromRequest(request);
- var file = params.file;
- var offset = params.offset;
- var length = params.length;
-
- if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
- return;
- }
-
- // add refactoring kinds
- var kinds = <RefactoringKind>[];
- // Check nodes.
- final searchEngine = server.searchEngine;
- {
- var resolvedUnit = await server.getResolvedUnit(file);
- if (resolvedUnit != null) {
- // Try EXTRACT_LOCAL_VARIABLE.
- if (ExtractLocalRefactoring(resolvedUnit, offset, length)
- .isAvailable()) {
- kinds.add(RefactoringKind.EXTRACT_LOCAL_VARIABLE);
- }
- // Try EXTRACT_METHOD.
- if (ExtractMethodRefactoring(searchEngine, resolvedUnit, offset, length)
- .isAvailable()) {
- kinds.add(RefactoringKind.EXTRACT_METHOD);
- }
- // Try EXTRACT_WIDGETS.
- if (ExtractWidgetRefactoring(searchEngine, resolvedUnit, offset, length)
- .isAvailable()) {
- kinds.add(RefactoringKind.EXTRACT_WIDGET);
- }
- }
- }
- // check elements
- {
- var resolvedUnit = await server.getResolvedUnit(file);
- if (resolvedUnit != null) {
- var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
- var element = server.getElementOfNode(node);
- if (element != null) {
- // try CONVERT_METHOD_TO_GETTER
- if (element is ExecutableElement) {
- Refactoring refactoring = ConvertMethodToGetterRefactoring(
- searchEngine, resolvedUnit.session, element);
- var status = await refactoring.checkInitialConditions();
- if (!status.hasFatalError) {
- kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER);
- }
- }
- // try RENAME
- final refactoringWorkspace = this.refactoringWorkspace;
- if (refactoringWorkspace != null) {
- var renameRefactoring = RenameRefactoring.create(
- refactoringWorkspace, resolvedUnit, element);
- if (renameRefactoring != null) {
- kinds.add(RefactoringKind.RENAME);
- }
- }
- }
- }
- }
- // respond
- var result = EditGetAvailableRefactoringsResult(kinds);
- server.sendResponse(result.toResponse(request.id));
- }
-
- Response _getRefactoring(
- Request request, CancellationToken cancellationToken) {
- final refactoringManager = this.refactoringManager;
- if (refactoringManager == null) {
- return Response.unsupportedFeature(request.id, 'Search is not enabled.');
- }
- if (refactoringManager.hasPendingRequest) {
- refactoringManager.cancel();
- _newRefactoringManager();
- }
- refactoringManager.getRefactoring(request, cancellationToken);
- return Response.DELAYED_RESPONSE;
- }
-
- /// Initializes [refactoringManager] with a new instance.
- void _newRefactoringManager() {
- final refactoringWorkspace = this.refactoringWorkspace;
- if (refactoringWorkspace != null) {
- refactoringManager = _RefactoringManager(server, refactoringWorkspace);
- }
- }
}
/// An object managing a single [Refactoring] instance.
@@ -216,7 +121,7 @@
///
/// Once new set of parameters is received, the previous [Refactoring] instance
/// is invalidated and a new one is created and initialized.
-class _RefactoringManager {
+class RefactoringManager {
static const List<RefactoringProblem> EMPTY_PROBLEM_LIST =
<RefactoringProblem>[];
@@ -238,7 +143,7 @@
Request? request;
EditGetRefactoringResult? result;
- _RefactoringManager(this.server, this.refactoringWorkspace)
+ RefactoringManager(this.server, this.refactoringWorkspace)
: searchEngine = refactoringWorkspace.searchEngine {
_reset();
}
@@ -615,6 +520,6 @@
}
}
-/// [_RefactoringManager] throws instances of this class internally to stop
+/// [RefactoringManager] throws instances of this class internally to stop
/// processing in a manager that was reset.
class _ResetError {}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
new file mode 100644
index 0000000..04b8ac1
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
@@ -0,0 +1,83 @@
+// 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/services/refactoring/refactoring.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer_plugin/protocol/protocol_common.dart';
+
+/// The handler for the `edit.getAvailableRefactorings` request.
+class EditGetAvailableRefactoringsHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ EditGetAvailableRefactoringsHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ var params = EditGetAvailableRefactoringsParams.fromRequest(request);
+ var file = params.file;
+ var offset = params.offset;
+ var length = params.length;
+
+ if (server.sendResponseErrorIfInvalidFilePath(request, file)) {
+ return;
+ }
+
+ // add refactoring kinds
+ var kinds = <RefactoringKind>[];
+ // Check nodes.
+ final searchEngine = server.searchEngine;
+ {
+ var resolvedUnit = await server.getResolvedUnit(file);
+ if (resolvedUnit != null) {
+ // Try EXTRACT_LOCAL_VARIABLE.
+ if (ExtractLocalRefactoring(resolvedUnit, offset, length)
+ .isAvailable()) {
+ kinds.add(RefactoringKind.EXTRACT_LOCAL_VARIABLE);
+ }
+ // Try EXTRACT_METHOD.
+ if (ExtractMethodRefactoring(searchEngine, resolvedUnit, offset, length)
+ .isAvailable()) {
+ kinds.add(RefactoringKind.EXTRACT_METHOD);
+ }
+ // Try EXTRACT_WIDGETS.
+ if (ExtractWidgetRefactoring(searchEngine, resolvedUnit, offset, length)
+ .isAvailable()) {
+ kinds.add(RefactoringKind.EXTRACT_WIDGET);
+ }
+ }
+ }
+ // check elements
+ var resolvedUnit = await server.getResolvedUnit(file);
+ if (resolvedUnit != null) {
+ var node = NodeLocator(offset).searchWithin(resolvedUnit.unit);
+ var element = server.getElementOfNode(node);
+ if (element != null) {
+ // try CONVERT_METHOD_TO_GETTER
+ if (element is ExecutableElement) {
+ Refactoring refactoring = ConvertMethodToGetterRefactoring(
+ searchEngine, resolvedUnit.session, element);
+ var status = await refactoring.checkInitialConditions();
+ if (!status.hasFatalError) {
+ kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER);
+ }
+ }
+ // try RENAME
+ final refactoringWorkspace = server.refactoringWorkspace;
+ var renameRefactoring = RenameRefactoring.create(
+ refactoringWorkspace, resolvedUnit, element);
+ if (renameRefactoring != null) {
+ kinds.add(RefactoringKind.RENAME);
+ }
+ }
+ }
+ // respond
+ sendResult(EditGetAvailableRefactoringsResult(kinds));
+ }
+}
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart
new file mode 100644
index 0000000..f4793ce
--- /dev/null
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_refactoring.dart
@@ -0,0 +1,27 @@
+// 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/src/handler/legacy/legacy_handler.dart';
+
+/// The handler for the `edit.getRefactoring` request.
+class EditGetRefactoringHandler extends LegacyHandler {
+ /// Initialize a newly created handler to be able to service requests for the
+ /// [server].
+ EditGetRefactoringHandler(
+ super.server, super.request, super.cancellationToken);
+
+ @override
+ Future<void> handle() async {
+ final refactoringManager = server.refactoringManager;
+ if (refactoringManager == null) {
+ sendResponse(
+ Response.unsupportedFeature(request.id, 'Search is not enabled.'));
+ return;
+ }
+ refactoringManager.getRefactoring(request, cancellationToken);
+ }
+}
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index c0147af..ae9517c 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -608,6 +608,8 @@
status: needsEvaluation
CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE:
status: needsEvaluation
+CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION:
+ status: needsEvaluation
CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE:
status: needsEvaluation
CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS:
diff --git a/pkg/analysis_server/tool/code_completion/completion_metrics.dart b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
index a8f6982..f2d73e1 100644
--- a/pkg/analysis_server/tool/code_completion/completion_metrics.dart
+++ b/pkg/analysis_server/tool/code_completion/completion_metrics.dart
@@ -52,6 +52,7 @@
import 'package:analyzer_plugin/src/utilities/completion/optype.dart';
import 'package:args/args.dart';
import 'package:cli_util/cli_logging.dart';
+import 'package:collection/collection.dart';
import 'metrics_util.dart';
import 'output_utilities.dart';
@@ -151,8 +152,9 @@
/// Create a parser that can be used to parse the command-line arguments.
ArgParser createArgParser() {
- return ArgParser()
- ..addOption(
+ return ArgParser(
+ usageLineLength: stdout.hasTerminal ? stdout.terminalColumns : 80)
+ ..addFlag(
'help',
abbr: 'h',
help: 'Print this help message.',
@@ -169,6 +171,11 @@
'token can be removed, or the rest of the file can be removed to '
'test code completion with diverse methods. The default mode is to '
'complete at the start of the token without modifying the file.')
+ ..addOption(CompletionMetricsOptions.PREFIX_LENGTH,
+ defaultsTo: '0',
+ help: 'The number of characters to include in the prefix. Each '
+ 'completion will be requested this many characters in from the '
+ 'start of the token being completed.')
..addFlag(CompletionMetricsOptions.PRINT_MISSED_COMPLETION_DETAILS,
defaultsTo: false,
help:
@@ -299,7 +306,7 @@
/// objects for a run of [CompletionMetricsComputer].
class CompletionMetrics {
/// The maximum number of slowest results to collect.
- static const maxSlowestResults = 5;
+ static const maxSlowestResults = 100;
/// The maximum number of worst results to collect.
static const maxWorstResults = 5;
@@ -382,7 +389,10 @@
/// where a shadowed element was suggested rather than the visible one.
Map<String, List<ShadowedCompletion>> shadowedCompletions = {};
- final Map<CompletionGroup, List<CompletionResult>> slowestResults = {};
+ /// A list of the slowest results, sorted from slowest to fastest.
+ ///
+ /// This list contains at most [maxSlowestResults] results.
+ final List<CompletionResult> slowestResults = [];
final Map<CompletionGroup, List<CompletionResult>> worstResults = {};
@@ -441,13 +451,10 @@
in map['missingCompletionLocationTables'] as List<dynamic>) {
metrics.missingCompletionLocationTables.add(element as String);
}
- for (var entry in (map['slowestResults'] as Map<String, dynamic>).entries) {
- var group = CompletionGroup.values[int.parse(entry.key)];
- var results = (entry.value as List<dynamic>)
- .map((map) => CompletionResult.fromJson(map as Map<String, dynamic>))
- .toList();
- metrics.slowestResults[group] = results;
- }
+ metrics.slowestResults.addAll([
+ for (var result in map['slowestResults'] as List<dynamic>)
+ CompletionResult.fromJson(result as Map<String, dynamic>),
+ ]);
for (var entry in (map['worstResults'] as Map<String, dynamic>).entries) {
var group = CompletionGroup.values[int.parse(entry.key)];
var results = (entry.value as List<dynamic>)
@@ -487,10 +494,8 @@
missingCompletionLocations.addAll(metrics.missingCompletionLocations);
missingCompletionLocationTables
.addAll(metrics.missingCompletionLocationTables);
- for (var resultList in metrics.slowestResults.values) {
- for (var result in resultList) {
- _recordSlowestResult(result);
- }
+ for (var result in metrics.slowestResults) {
+ _recordSlowestResult(result);
}
for (var resultList in metrics.worstResults.values) {
for (var result in resultList) {
@@ -572,9 +577,8 @@
'missingCompletionLocations': missingCompletionLocations.toList(),
'missingCompletionLocationTables':
missingCompletionLocationTables.toList(),
- 'slowestResults': slowestResults.map((key, value) => MapEntry(
- key.index.toString(),
- value.map((result) => result.toJson()).toList())),
+ 'slowestResults':
+ slowestResults.map((result) => result.toJson()).toList(),
'worstResults': worstResults.map((key, value) => MapEntry(
key.index.toString(),
value.map((result) => result.toJson()).toList())),
@@ -614,18 +618,17 @@
}
}
- /// If the [result] is took longer than any previously recorded results,
+ /// If the [result] took longer than any previously recorded results,
/// record it.
void _recordSlowestResult(CompletionResult result) {
- var results = slowestResults.putIfAbsent(result.group, () => []);
- if (results.length >= maxSlowestResults) {
- if (result.elapsedMS <= results.last.elapsedMS) {
+ if (slowestResults.length >= maxSlowestResults) {
+ if (result.elapsedMS <= slowestResults.last.elapsedMS) {
return;
}
- results.removeLast();
+ slowestResults.removeLast();
}
- results.add(result);
- results.sort((first, second) => second.elapsedMS - first.elapsedMS);
+ slowestResults.add(result);
+ slowestResults.sort((first, second) => second.elapsedMS - first.elapsedMS);
}
/// Record this elapsed ms count for the average ms count.
@@ -1199,15 +1202,36 @@
}
}
+ /// Prints the results which took the longest amounts of time to compute.
+ ///
+ /// Specifically, only results which are above the 90th percentile, and which
+ /// are in the top [maxSlowestResults] slowest results, are included.
void printSlowestResults(CompletionMetrics metrics) {
- var slowestResults = metrics.slowestResults;
- var entries = slowestResults.entries.toList();
- entries.sort((first, second) => first.key.name.compareTo(second.key.name));
+ var p90ElapsedMs = metrics.percentileCompletionMS.p90;
+ var slowestResults = metrics.slowestResults
+ .where((element) => element.elapsedMS >= p90ElapsedMs)
+ .toList();
print('');
printHeading(2, 'The slowest completion results to compute');
- for (var entry in entries) {
- _printSlowestResults('In ${entry.key.name}', entry.value);
+ for (var result in slowestResults) {
+ var expected = result.expectedCompletion;
+ print('');
+ print('* Elapsed ms: ${result.elapsedMS}');
+ print('* Group: ${result.group.name}');
+ print("* Completion: '${expected.completion}'");
+ print('* Completion kind: ${expected.kind}');
+ print('* Element kind: ${expected.elementKind}');
+ print('* Location: ${expected.location}');
}
+
+ print('');
+ var slowestResultCountByGroup = slowestResults.groupFoldBy(
+ (result) => result.group.name,
+ (int? previous, result) => (previous ?? 0) + 1);
+ slowestResultCountByGroup.forEach((groupName, count) {
+ var countString = count.toString().padLeft(2);
+ print('${groupName.padRight(20)}: $countString result(s)');
+ });
}
void printWorstResults(CompletionMetrics metrics) {
@@ -1240,6 +1264,8 @@
return expected.length;
}
+ /// Computes completion suggestions for [dartRequest], and returns the
+ /// suggestions, sorted by rank and then by completion text.
Future<List<protocol.CompletionSuggestion>> _computeCompletionSuggestions(
MetricsSuggestionListener listener,
OperationPerformanceImpl performance,
@@ -1406,7 +1432,8 @@
var filePath = result.path;
// Use the ExpectedCompletionsVisitor to compute the set of expected
// completions for this CompilationUnit.
- final visitor = ExpectedCompletionsVisitor(result);
+ final visitor =
+ ExpectedCompletionsVisitor(result, caretOffset: options.prefixLength);
_resolvedUnitResult.unit.accept(visitor);
for (var expectedCompletion in visitor.expectedCompletions) {
@@ -1508,9 +1535,14 @@
var length = expectedCompletion.syntacticEntity.length;
assert(offset >= 0);
assert(length > 0);
+ var tokenEndOffset = offset + length;
+ if (length >= options.prefixLength) {
+ // Rather than removing the whole token, remove the characters after
+ // the given prefix length.
+ offset += options.prefixLength;
+ }
if (options.overlay == CompletionMetricsOptions.OVERLAY_REMOVE_TOKEN) {
- return contents.substring(0, offset) +
- contents.substring(offset + length);
+ return contents.substring(0, offset) + contents.substring(tokenEndOffset);
} else if (options.overlay ==
CompletionMetricsOptions.OVERLAY_REMOVE_REST_OF_FILE) {
return contents.substring(0, offset);
@@ -1522,26 +1554,6 @@
}
}
- void _printSlowestResults(
- String title, List<CompletionResult> slowestResults) {
- printHeading(3, title);
- var needsBlankLine = false;
- for (var result in slowestResults) {
- var elapsedMS = result.elapsedMS;
- var expected = result.expectedCompletion;
- if (needsBlankLine) {
- print('');
- } else {
- needsBlankLine = true;
- }
- print(' Elapsed ms: $elapsedMS');
- print(' Completion: ${expected.completion}');
- print(' Completion kind: ${expected.kind}');
- print(' Element kind: ${expected.elementKind}');
- print(' Location: ${expected.location}');
- }
- }
-
void _printWorstResults(String title, List<CompletionResult> worstResults) {
List<String> suggestionRow(int rank, SuggestionData data) {
var suggestion = data.suggestion;
@@ -1636,15 +1648,17 @@
return null;
}
+ /// Returns a [Place] indicating the position of [expectedCompletion] in
+ /// [suggestions].
+ ///
+ /// If [expectedCompletion] is not found, `Place.none()` is returned.
static Place placementInSuggestionList(
List<protocol.CompletionSuggestion> suggestions,
ExpectedCompletion expectedCompletion) {
- var placeCounter = 1;
- for (var completionSuggestion in suggestions) {
- if (expectedCompletion.matches(completionSuggestion)) {
- return Place(placeCounter, suggestions.length);
+ for (var i = 0; i < suggestions.length; i++) {
+ if (expectedCompletion.matches(suggestions[i])) {
+ return Place(i + 1, suggestions.length);
}
- placeCounter++;
}
return Place.none();
}
@@ -1666,6 +1680,12 @@
/// completion offset should be removed.
static const String OVERLAY_REMOVE_TOKEN = 'remove-token';
+ /// An option controlling how long of a prefix should be used.
+ ///
+ /// This affects the offset of the completion request, and how much content is
+ /// removed in each of the overlay modes.
+ static const String PREFIX_LENGTH = 'prefix-length';
+
/// A flag that causes detailed information to be printed every time a
/// completion request fails to produce a suggestions matching the expected
/// suggestion.
@@ -1705,6 +1725,8 @@
/// The overlay mode that should be used.
final String overlay;
+ final int prefixLength;
+
/// A flag indicating whether information should be printed every time a
/// completion request fails to produce a suggestions matching the expected
/// suggestion.
@@ -1740,6 +1762,7 @@
factory CompletionMetricsOptions(results) {
return CompletionMetricsOptions._(
overlay: results[OVERLAY] as String,
+ prefixLength: int.parse(results[PREFIX_LENGTH] as String),
printMissedCompletionDetails:
results[PRINT_MISSED_COMPLETION_DETAILS] as bool,
printMissedCompletionSummary:
@@ -1754,6 +1777,7 @@
CompletionMetricsOptions._(
{required this.overlay,
+ required this.prefixLength,
required this.printMissedCompletionDetails,
required this.printMissedCompletionSummary,
required this.printMissingInformation,
diff --git a/pkg/analysis_server/tool/code_completion/visitors.dart b/pkg/analysis_server/tool/code_completion/visitors.dart
index 5783758..0e36d73 100644
--- a/pkg/analysis_server/tool/code_completion/visitors.dart
+++ b/pkg/analysis_server/tool/code_completion/visitors.dart
@@ -127,6 +127,11 @@
/// The result of resolving the file being visited.
final ResolvedUnitResult result;
+ /// The offset from the location of each entity passed to
+ /// [safelyRecordEntity], to be used as the column for each
+ /// [ExpectedCompletion] created.
+ final int _caretOffset;
+
/// The completions that are expected to be produced in the file being
/// visited.
final List<ExpectedCompletion> expectedCompletions = [];
@@ -144,7 +149,8 @@
/// comment don't yield an error like Dart syntax mistakes would yield.
final bool _doExpectCommentRefs = false;
- ExpectedCompletionsVisitor(this.result);
+ ExpectedCompletionsVisitor(this.result, {required int caretOffset})
+ : _caretOffset = caretOffset;
/// Return the path of the file that is being visited.
String get filePath => result.path;
@@ -160,6 +166,9 @@
var location = lineInfo.getLocation(entity.offset);
var lineNumber = location.lineNumber;
var columnNumber = location.columnNumber;
+ if (entity.length >= _caretOffset) {
+ columnNumber += _caretOffset;
+ }
bool isKeyword() => kind == protocol.CompletionSuggestionKind.KEYWORD;
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 15c5a05..46810d4 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -285,6 +285,7 @@
CompileTimeErrorCode.LATE_FINAL_FIELD_WITH_CONST_CONSTRUCTOR,
CompileTimeErrorCode.LATE_FINAL_LOCAL_ALREADY_ASSIGNED,
CompileTimeErrorCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE,
+ CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION,
CompileTimeErrorCode.MAIN_FIRST_POSITIONAL_PARAMETER_TYPE,
CompileTimeErrorCode.MAIN_HAS_REQUIRED_NAMED_PARAMETERS,
CompileTimeErrorCode.MAIN_HAS_TOO_MANY_REQUIRED_POSITIONAL_PARAMETERS,
diff --git a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
index e7ff31d..4b4946f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/analysis_context_collection.dart
@@ -16,6 +16,7 @@
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
import 'package:analyzer/src/dart/analysis/performance_logger.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
+import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/util/sdk.dart';
@@ -25,7 +26,10 @@
final ResourceProvider resourceProvider;
/// The instance of macro executor that is used for all macros.
- final macro.MultiMacroExecutor? macroExecutor = macro.MultiMacroExecutor();
+ final macro.MultiMacroExecutor macroExecutor = macro.MultiMacroExecutor();
+
+ /// The instance of the macro kernel builder.
+ final MacroKernelBuilder macroKernelBuilder = MacroKernelBuilder();
/// The list of analysis contexts.
@override
@@ -48,7 +52,6 @@
AnalysisDriverScheduler? scheduler,
FileContentCache? fileContentCache,
void Function(AnalysisOptionsImpl)? updateAnalysisOptions,
- MacroKernelBuilder? macroKernelBuilder,
}) : resourceProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE {
sdkPath ??= getSdkPath();
@@ -119,7 +122,9 @@
for (var analysisContext in contexts) {
analysisContext.driver.dispose();
}
- macroExecutor?.close();
+ macroExecutor.close();
+ // If there are other collections, they will have to start it again.
+ KernelCompilationService.dispose();
}
/// Check every element with [_throwIfNotAbsoluteNormalizedPath].
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_state.dart b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
index e2a6383..618627c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_state.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_state.dart
@@ -331,7 +331,7 @@
String get transitiveSignature {
var librarySignatureBuilder = ApiSignature()
..addString(uriStr)
- ..addString(libraryCycle.transitiveSignature);
+ ..addString(libraryCycle.apiSignature);
return librarySignatureBuilder.toHex();
}
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_context.dart b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
index f19c23c..3d44aa2 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_context.dart
@@ -160,7 +160,7 @@
}
}
- var resolutionKey = '${cycle.transitiveSignature}.linked_bundle';
+ var resolutionKey = '${cycle.apiSignature}.linked_bundle';
var resolutionBytes = byteStore.get(resolutionKey);
if (resolutionBytes == null) {
@@ -244,7 +244,7 @@
final macroKernelBuilder = this.macroKernelBuilder;
if (macroKernelBuilder != null && macroLibraries.isNotEmpty) {
- var macroKernelKey = '${cycle.transitiveSignature}.macro_kernel';
+ var macroKernelKey = '${cycle.implSignature}.macro_kernel';
var macroKernelBytes = byteStore.get(macroKernelKey);
if (macroKernelBytes == null) {
macroKernelBytes = await macroKernelBuilder.build(
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
index 1a4ed51..1457c6b 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_graph.dart
@@ -28,18 +28,46 @@
/// The cycles that use this cycle, used to [invalidate] transitively.
final List<LibraryCycle> _directUsers = [];
- /// The transitive signature of this cycle.
+ /// The transitive API signature of this cycle.
///
/// It is based on the API signatures of all files of the [libraries], and
- /// transitive signatures of the cycles that the [libraries] reference
- /// directly. So, indirectly it is based on the transitive closure of all
- /// files that [libraries] reference (but we don't compute these files).
- String transitiveSignature;
+ /// API signatures of the cycles that the [libraries] reference directly.
+ /// So, indirectly it is based on API signatures of the transitive closure
+ /// of all files that [libraries] reference.
+ String apiSignature;
+
+ /// The transitive implementation signature of this cycle.
+ ///
+ /// It is based on the full code signatures of all files of the [libraries],
+ /// and full code signatures of the cycles that the [libraries] reference
+ /// directly. So, indirectly it is based on full code signatures of the
+ /// transitive closure of all files that [libraries] reference.
+ ///
+ /// Usually, when a library is imported we need its [apiSignature], because
+ /// its API is all we can see from outside. But if the library contains
+ /// a macro, and we use it, we run full code of the macro defining library,
+ /// potentially executing every method body of the transitive closure of
+ /// the libraries imported by the macro defining library. So, the resulting
+ /// library (that imports a macro defining library) API signature must
+ /// include [implSignature] of the macro defining library.
+ String implSignature;
+
+ late final bool hasMacroClass = () {
+ for (final library in libraries) {
+ for (final file in library.libraryFiles) {
+ if (file.unlinked2.macroClasses.isNotEmpty) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }();
LibraryCycle({
required this.libraries,
required this.directDependencies,
- required this.transitiveSignature,
+ required this.apiSignature,
+ required this.implSignature,
}) {
for (var directDependency in directDependencies) {
directDependency._directUsers.add(this);
@@ -99,8 +127,10 @@
@override
void evaluateScc(List<_LibraryNode> scc) {
- var signature = ApiSignature();
- signature.addUint32List(_salt);
+ var apiSignature = ApiSignature();
+ var implSignature = ApiSignature();
+ apiSignature.addUint32List(_salt);
+ implSignature.addUint32List(_salt);
// Sort libraries to produce stable signatures.
scc.sort((first, second) {
@@ -115,7 +145,8 @@
var file = node.file;
_appendDirectlyReferenced(
directDependencies,
- signature,
+ apiSignature,
+ implSignature,
file.directReferencedLibraries.whereNotNull().toList(),
);
}
@@ -125,13 +156,22 @@
for (var node in scc) {
libraries.add(node.file);
- signature.addLanguageVersion(node.file.packageLanguageVersion);
- signature.addString(node.file.uriStr);
+ apiSignature.addLanguageVersion(node.file.packageLanguageVersion);
+ apiSignature.addString(node.file.uriStr);
- signature.addInt(node.file.libraryFiles.length);
+ implSignature.addLanguageVersion(node.file.packageLanguageVersion);
+ implSignature.addString(node.file.uriStr);
+
+ apiSignature.addInt(node.file.libraryFiles.length);
for (var file in node.file.libraryFiles) {
- signature.addBool(file.exists);
- signature.addBytes(file.apiSignature);
+ apiSignature.addBool(file.exists);
+ apiSignature.addBytes(file.apiSignature);
+ }
+
+ implSignature.addInt(node.file.libraryFiles.length);
+ for (var file in node.file.libraryFiles) {
+ implSignature.addBool(file.exists);
+ implSignature.addString(file.contentHash);
}
}
@@ -139,7 +179,8 @@
var cycle = LibraryCycle(
libraries: libraries,
directDependencies: directDependencies,
- transitiveSignature: signature.toHex(),
+ apiSignature: apiSignature.toHex(),
+ implSignature: implSignature.toHex(),
);
// Set the instance into the libraries.
@@ -154,10 +195,12 @@
void _appendDirectlyReferenced(
Set<LibraryCycle> directDependencies,
- ApiSignature signature,
+ ApiSignature apiSignature,
+ ApiSignature implSignature,
List<FileState> directlyReferenced,
) {
- signature.addInt(directlyReferenced.length);
+ apiSignature.addInt(directlyReferenced.length);
+ implSignature.addInt(directlyReferenced.length);
for (var referencedLibrary in directlyReferenced) {
var referencedCycle = referencedLibrary.internal_libraryCycle;
@@ -165,7 +208,12 @@
if (referencedCycle == null) continue;
if (directDependencies.add(referencedCycle)) {
- signature.addString(referencedCycle.transitiveSignature);
+ apiSignature.addString(
+ referencedCycle.hasMacroClass
+ ? referencedCycle.implSignature
+ : referencedCycle.apiSignature,
+ );
+ implSignature.addString(referencedCycle.implSignature);
}
}
}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 34789a9..a230ed1 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -9018,6 +9018,18 @@
);
/**
+ * Parameters:
+ * 0: the message of the exception
+ * 1: the stack trace
+ */
+ static const CompileTimeErrorCode MACRO_EXECUTION_EXCEPTION =
+ CompileTimeErrorCode(
+ 'MACRO_EXECUTION_EXCEPTION',
+ "Exception during macro execution: {0}\n{1}",
+ correctionMessage: "Re-install the Dart or Flutter SDK.",
+ );
+
+ /**
* No parameters.
*/
// #### Description
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index b93727e..d5a1aac 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -46,6 +46,7 @@
import 'package:analyzer/src/generated/error_detection_helpers.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/this_access_tracker.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:collection/collection.dart';
@@ -445,6 +446,10 @@
_checkForBadFunctionUse(node);
_checkForWrongTypeParameterVarianceInSuperinterfaces();
_checkForMainFunction(node.name);
+ _reportMacroApplicationErrors(
+ annotations: node.metadata,
+ macroErrors: element.macroApplicationErrors,
+ );
GetterSetterTypesVerifier(
typeSystem: typeSystem,
@@ -5173,6 +5178,30 @@
return null;
}
+ void _reportMacroApplicationErrors({
+ required List<Annotation> annotations,
+ required List<MacroApplicationError> macroErrors,
+ }) {
+ for (final macroError in macroErrors) {
+ if (macroError.annotationIndex < annotations.length) {
+ final applicationNode = annotations[macroError.annotationIndex];
+ if (macroError is UnknownMacroApplicationError) {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION,
+ applicationNode,
+ [
+ macroError.message,
+ macroError.stackTrace,
+ ],
+ );
+ } else {
+ // TODO(scheglov) Other implementations.
+ throw UnimplementedError('(${macroError.runtimeType}) $macroError');
+ }
+ }
+ }
+ }
+
void _withEnclosingExecutable(
ExecutableElement element,
void Function() operation,
diff --git a/pkg/analyzer/lib/src/summary2/macro.dart b/pkg/analyzer/lib/src/summary2/macro.dart
index 27130c3..bf4756d 100644
--- a/pkg/analyzer/lib/src/summary2/macro.dart
+++ b/pkg/analyzer/lib/src/summary2/macro.dart
@@ -68,34 +68,6 @@
}
}
-/// Implementation of [MacroKernelBuilder] using `frontend_server`.
-class FrontEndServerMacroKernelBuilder implements MacroKernelBuilder {
- @override
- Future<Uint8List> build({
- required MacroFileSystem fileSystem,
- required List<MacroLibrary> libraries,
- }) async {
- final macroMainContent = macro.bootstrapMacroIsolate(
- {
- for (final library in libraries)
- library.uri.toString(): {
- for (final c in library.classes) c.name: c.constructors
- },
- },
- macro.SerializationMode.byteDataClient,
- );
-
- final macroMainPath = '${libraries.first.path}.macro';
- final overlayFileSystem = _OverlayMacroFileSystem(fileSystem);
- overlayFileSystem.overlays[macroMainPath] = macroMainContent;
-
- return KernelCompilationService.compile(
- fileSystem: overlayFileSystem,
- path: macroMainPath,
- );
- }
-}
-
class MacroClass {
final String name;
final List<String> constructors;
@@ -140,11 +112,32 @@
MacroFileEntry getFile(String path);
}
-abstract class MacroKernelBuilder {
+class MacroKernelBuilder {
+ const MacroKernelBuilder();
+
Future<Uint8List> build({
required MacroFileSystem fileSystem,
required List<MacroLibrary> libraries,
- });
+ }) async {
+ final macroMainContent = macro.bootstrapMacroIsolate(
+ {
+ for (final library in libraries)
+ library.uri.toString(): {
+ for (final c in library.classes) c.name: c.constructors
+ },
+ },
+ macro.SerializationMode.byteDataClient,
+ );
+
+ final macroMainPath = '${libraries.first.path}.macro';
+ final overlayFileSystem = _OverlayMacroFileSystem(fileSystem);
+ overlayFileSystem.overlays[macroMainPath] = macroMainContent;
+
+ return KernelCompilationService.compile(
+ fileSystem: overlayFileSystem,
+ path: macroMainPath,
+ );
+ }
}
class MacroLibrary {
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 1931e3b..d9719b2 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -6,6 +6,7 @@
import 'package:_fe_analyzer_shared/src/macros/executor.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart'
as macro;
+import 'package:_fe_analyzer_shared/src/macros/executor/protocol.dart' as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
as macro;
import 'package:analyzer/dart/ast/ast.dart';
@@ -69,12 +70,20 @@
}
} on MacroApplicationError catch (e) {
classElement.macroApplicationErrors.add(e);
+ } on macro.RemoteException catch (e) {
+ classElement.macroApplicationErrors.add(
+ UnknownMacroApplicationError(
+ annotationIndex: i,
+ message: e.error,
+ stackTrace: e.stackTrace ?? '<null>',
+ ),
+ );
} catch (e, stackTrace) {
classElement.macroApplicationErrors.add(
UnknownMacroApplicationError(
annotationIndex: i,
- stackTrace: stackTrace.toString(),
message: e.toString(),
+ stackTrace: stackTrace.toString(),
),
);
}
@@ -172,7 +181,7 @@
);
}
- static macro.ParameterDeclarationImpl _buildFormalParameter(
+ static macro.FunctionTypeParameterImpl _buildFormalParameter(
FormalParameter node,
) {
if (node is DefaultFormalParameter) {
@@ -186,12 +195,11 @@
throw UnimplementedError('(${node.runtimeType}) $node');
}
- return macro.ParameterDeclarationImpl(
+ return macro.FunctionTypeParameterImpl(
id: macro.RemoteInstance.uniqueId,
- identifier:
- _buildIdentifier(node.identifier!), // TODO(scheglov) might be null
isNamed: node.isNamed,
isRequired: node.isRequired,
+ name: node.identifier?.name,
type: typeAnnotation,
);
}
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index f38485c..4d58c07 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -7917,6 +7917,13 @@
```dart
List<num> x = [1, 2.5, 3];
```
+ MACRO_EXECUTION_EXCEPTION:
+ problemMessage: "Exception during macro execution: {0}\n{1}"
+ correctionMessage: Re-install the Dart or Flutter SDK.
+ comment: |-
+ Parameters:
+ 0: the message of the exception
+ 1: the stack trace
MAIN_FIRST_POSITIONAL_PARAMETER_TYPE:
problemMessage: "The type of the first positional parameter of the 'main' function must be a supertype of 'List<String>'."
correctionMessage: Try changing the type of the parameter.
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
index 28a1626..de7d1bc 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_caching_test.dart
@@ -5,11 +5,13 @@
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
+import '../../summary/macros_environment.dart';
import '../resolution/context_collection_resolution.dart';
main() {
@@ -27,6 +29,16 @@
return driver.test.libraryContextTestView.linkedCycles;
}
+ @override
+ void setUp() {
+ super.setUp();
+
+ writeTestPackageConfig(
+ PackageConfigFileBuilder(),
+ macrosEnvironment: MacrosEnvironment.instance,
+ );
+ }
+
test_analysisOptions_strictCasts() async {
useEmptyByteStore();
@@ -340,6 +352,180 @@
_assertNoLinkedCycles();
}
+ test_macro_libraryElement_changeMacroCode() async {
+ File newFileWithFixedNameMacro(String className) {
+ return newFile('$testPackageLibPath/my_macro.dart', '''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ const MyMacro();
+
+ FutureOr<void> buildTypesForClass(clazz, builder) {
+ builder.declareType(
+ '$className',
+ DeclarationCode.fromString('class $className {}'),
+ );
+ }
+}
+''');
+ }
+
+ final macroFile = newFileWithFixedNameMacro('MacroA');
+
+ var a = newFile('$testPackageLibPath/a.dart', r'''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+''');
+
+ var b = newFile('$testPackageLibPath/b.dart', r'''
+export 'a.dart';
+''');
+
+ final analysisContext = contextFor(macroFile.path);
+
+ Future<LibraryElement> getLibrary(String uriStr) async {
+ final result = await analysisContext.currentSession
+ .getLibraryByUri(uriStr) as LibraryElementResult;
+ return result.element;
+ }
+
+ // This macro generates `MacroA`, but not `MacroB`.
+ {
+ final libraryA = await getLibrary('package:test/a.dart');
+ expect(libraryA.getType('MacroA'), isNotNull);
+ expect(libraryA.getType('MacroB'), isNull);
+ // This propagates transitively.
+ final libraryB = await getLibrary('package:test/b.dart');
+ expect(libraryB.exportNamespace.get('MacroA'), isNotNull);
+ expect(libraryB.exportNamespace.get('MacroB'), isNull);
+ }
+
+ _assertContainsLinkedCycle({a.path});
+ _assertContainsLinkedCycle({b.path}, andClear: true);
+
+ // The macro will generate `MacroB`.
+ newFileWithFixedNameMacro('MacroB');
+
+ // Notify about changes.
+ analysisContext.changeFile(macroFile.path);
+ await analysisContext.applyPendingFileChanges();
+
+ // This macro generates `MacroB`, but not `MacroA`.
+ {
+ final libraryA = await getLibrary('package:test/a.dart');
+ expect(libraryA.getType('MacroA'), isNull);
+ expect(libraryA.getType('MacroB'), isNotNull);
+ // This propagates transitively.
+ final libraryB = await getLibrary('package:test/b.dart');
+ expect(libraryB.exportNamespace.get('MacroA'), isNull);
+ expect(libraryB.exportNamespace.get('MacroB'), isNotNull);
+ }
+
+ _assertContainsLinkedCycle({a.path});
+ _assertContainsLinkedCycle({b.path}, andClear: true);
+ }
+
+ test_macro_resolvedUnit_changeCodeUsedByMacro() async {
+ final a = newFile('$testPackageLibPath/a.dart', r'''
+String getClassName() => 'MacroA';
+''');
+
+ newFile('$testPackageLibPath/my_macro.dart', r'''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+import 'a.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ const MyMacro();
+
+ FutureOr<void> buildTypesForClass(clazz, builder) {
+ final className = getClassName();
+ builder.declareType(
+ '$className',
+ DeclarationCode.fromString('class $className {}'),
+ );
+ }
+}
+''');
+
+ // The macro will generate `MacroA`, so no errors.
+ await assertNoErrorsInCode('''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+
+void f(MacroA a) {}
+''');
+
+ // The macro will generate `MacroB`.
+ newFile(a.path, r'''
+String getClassName() => 'MacroB';
+''');
+
+ // Notify about changes.
+ var analysisContext = contextFor(a.path);
+ analysisContext.changeFile(a.path);
+ await analysisContext.applyPendingFileChanges();
+
+ // Resolve the test file, it still references `MacroA`, but the macro
+ // generates `MacroB` now, so we have an error.
+ await resolveTestFile();
+ assertErrorsInResult([
+ error(CompileTimeErrorCode.UNDEFINED_CLASS, 55, 6),
+ ]);
+ }
+
+ test_macro_resolvedUnit_changeMacroCode() async {
+ File newFileWithFixedNameMacro(String className) {
+ return newFile('$testPackageLibPath/my_macro.dart', '''
+import 'dart:async';
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ const MyMacro();
+
+ FutureOr<void> buildTypesForClass(clazz, builder) {
+ builder.declareType(
+ '$className',
+ DeclarationCode.fromString('class $className {}'),
+ );
+ }
+}
+''');
+ }
+
+ var macroFile = newFileWithFixedNameMacro('MacroA');
+
+ // The macro will generate `MacroA`, so no errors.
+ await assertNoErrorsInCode('''
+import 'my_macro.dart';
+
+@MyMacro()
+class A {}
+
+void f(MacroA a) {}
+''');
+
+ // The macro will generate `MacroB`.
+ newFileWithFixedNameMacro('MacroB');
+
+ // Notify about changes.
+ var analysisContext = contextFor(macroFile.path);
+ analysisContext.changeFile(macroFile.path);
+ await analysisContext.applyPendingFileChanges();
+
+ // Resolve the test file, it still references `MacroA`, but the updated
+ // macro generates `MacroB` now, so we have an error.
+ await resolveTestFile();
+ assertErrorsInResult([
+ error(CompileTimeErrorCode.UNDEFINED_CLASS, 55, 6),
+ ]);
+ }
+
void _assertContainsLinkedCycle(Set<String> expectedPosix,
{bool andClear = false}) {
var expected = expectedPosix.map(convertPath).toSet();
diff --git a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
index d3fffbd..40822da 100644
--- a/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
+++ b/pkg/analyzer/test/src/dart/resolution/context_collection_resolution.dart
@@ -12,7 +12,6 @@
import 'package:analyzer/src/dart/analysis/driver_based_analysis_context.dart';
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
-import 'package:analyzer/src/summary2/macro.dart';
import 'package:analyzer/src/test_utilities/mock_packages.dart';
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
@@ -141,8 +140,6 @@
_declaredVariables = map;
}
- MacroKernelBuilder? get macroKernelBuilder => null;
-
bool get retainDataForTesting => false;
Folder get sdkRoot => newFolder('/sdk');
@@ -257,7 +254,6 @@
retainDataForTesting: retainDataForTesting,
sdkPath: sdkRoot.path,
updateAnalysisOptions: updateAnalysisOptions,
- macroKernelBuilder: macroKernelBuilder,
);
verifyCreatedCollection();
diff --git a/pkg/analyzer/test/src/dart/resolution/macro_test.dart b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
index 0178ec0..1ed52c5 100644
--- a/pkg/analyzer/test/src/dart/resolution/macro_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/macro_test.dart
@@ -2,8 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
-import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../summary/macros_environment.dart';
@@ -25,31 +24,15 @@
@reflectiveTest
class MacroResolutionTest extends PubPackageResolutionTest {
@override
- MacroKernelBuilder? get macroKernelBuilder {
- return FrontEndServerMacroKernelBuilder();
- }
-
- @override
void setUp() {
super.setUp();
- // TODO(scheglov) Dependency tracking for macros is not right yet.
- useEmptyByteStore();
-
writeTestPackageConfig(
PackageConfigFileBuilder(),
macrosEnvironment: MacrosEnvironment.instance,
);
}
- @override
- Future<void> tearDown() async {
- await super.tearDown();
- KernelCompilationService.disposeDelayed(
- const Duration(milliseconds: 100),
- );
- }
-
test_0() async {
newFile('$testPackageLibPath/a.dart', r'''
import 'dart:async';
@@ -77,4 +60,46 @@
void f(A_Macro a) {}
''');
}
+
+ test_macroExecutionException_compileTimeError() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ const MyMacro();
+
+ buildTypesForClass(clazz, builder) {
+ unresolved;
+ }
+}
+''');
+
+ await assertErrorsInCode('''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', [error(CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION, 18, 10)]);
+ }
+
+ test_macroExecutionException_throwsException() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ const MyMacro();
+
+ buildTypesForClass(clazz, builder) {
+ throw 42;
+ }
+}
+''');
+
+ await assertErrorsInCode('''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', [error(CompileTimeErrorCode.MACRO_EXECUTION_EXCEPTION, 18, 10)]);
+ }
}
diff --git a/pkg/analyzer/test/src/summary/elements_base.dart b/pkg/analyzer/test/src/summary/elements_base.dart
index 0c30670..0254431 100644
--- a/pkg/analyzer/test/src/summary/elements_base.dart
+++ b/pkg/analyzer/test/src/summary/elements_base.dart
@@ -23,6 +23,7 @@
import 'package:analyzer/src/source/package_map_resolver.dart';
import 'package:analyzer/src/summary2/bundle_reader.dart';
import 'package:analyzer/src/summary2/informative_data.dart';
+import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linked_element_factory.dart';
import 'package:analyzer/src/summary2/macro.dart';
@@ -30,6 +31,7 @@
import 'package:analyzer/src/test_utilities/mock_sdk.dart';
import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
import 'package:analyzer/src/util/uri.dart';
+import 'package:meta/meta.dart';
import 'package:path/path.dart' as package_path;
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -42,8 +44,8 @@
/// The shared SDK bundle, computed once and shared among test invocations.
static _SdkBundle? _sdkBundle;
- MacroKernelBuilder? macroKernelBuilder;
- macro.MultiMacroExecutor? macroExecutor;
+ /// The instance of macro executor that is used for all macros.
+ final macro.MultiMacroExecutor _macroExecutor = macro.MultiMacroExecutor();
/// The set of features enabled in this test.
FeatureSet featureSet = FeatureSets.latestWithExperiments;
@@ -180,7 +182,7 @@
var linkResult = await link(
elementFactory,
inputLibraries,
- macroExecutor: macroExecutor,
+ macroExecutor: _macroExecutor,
);
for (var macroUnit in linkResult.macroGeneratedUnits) {
@@ -204,6 +206,14 @@
return elementFactory.libraryOfUri2('$testUri');
}
+ @mustCallSuper
+ Future<void> tearDown() async {
+ await _macroExecutor.close();
+ KernelCompilationService.disposeDelayed(
+ const Duration(milliseconds: 100),
+ );
+ }
+
void _addLibraryUnits(
Source definingSource,
CompilationUnit definingUnit,
@@ -315,23 +325,14 @@
return;
}
- final macroKernelBuilder = this.macroKernelBuilder;
- if (macroKernelBuilder == null) {
- return;
- }
-
- final macroExecutor = this.macroExecutor;
- if (macroExecutor == null) {
- return;
- }
-
+ final macroKernelBuilder = const MacroKernelBuilder();
var macroKernelBytes = await macroKernelBuilder.build(
fileSystem: _MacroFileSystem(resourceProvider),
libraries: macroLibraries,
);
var bundleMacroExecutor = BundleMacroExecutor(
- macroExecutor: macroExecutor,
+ macroExecutor: _macroExecutor,
kernelBytes: macroKernelBytes,
libraries: macroLibraries.map((e) => e.uri).toSet(),
);
@@ -368,7 +369,7 @@
await link(
elementFactory,
cycleInputLibraries,
- macroExecutor: macroExecutor,
+ macroExecutor: _macroExecutor,
);
await _buildMacroLibraries(elementFactory, macroLibraries);
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
index 85ec1ed..f438eec 100644
--- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart
+++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -145,7 +145,7 @@
}
}
- void _writeFormalParameter(ParameterDeclaration node) {
+ void _writeFormalParameter(FunctionTypeParameter node) {
final String closeSeparator;
if (node.isNamed) {
_sink.write('{');
@@ -161,8 +161,10 @@
}
write(node.type);
- _sink.write(' ');
- _sink.write(node.identifier.name);
+ if (node.name != null) {
+ _sink.write(' ');
+ _sink.write(node.name);
+ }
_sink.write(closeSeparator);
}
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index f21ac8a..e2dd9f2 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -2,13 +2,10 @@
// 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:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
- as macro;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/element.dart';
-import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
-import 'package:analyzer/src/summary2/macro.dart';
+import 'package:analyzer/src/summary2/macro_application_error.dart';
import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -64,16 +61,6 @@
PackageConfigFileBuilder(),
macrosEnvironment: MacrosEnvironment.instance,
);
-
- macroKernelBuilder = FrontEndServerMacroKernelBuilder();
- macroExecutor = macro.MultiMacroExecutor();
- }
-
- Future<void> tearDown() async {
- await macroExecutor?.close();
- KernelCompilationService.disposeDelayed(
- const Duration(milliseconds: 100),
- );
}
test_arguments_error() async {
@@ -482,6 +469,64 @@
''');
}
+ test_macroApplicationErrors_compileTimeError() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ buildTypesForClass(clazz, builder) {
+ unresolved;
+ }
+}
+''');
+
+ final library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', preBuildSequence: [
+ {'package:test/a.dart'}
+ ]);
+
+ final A = library.getType('A') as ClassElementImpl;
+ final error = A.macroApplicationErrors.single;
+ error as UnknownMacroApplicationError;
+
+ expect(error.annotationIndex, 0);
+ expect(error.message, contains('unresolved'));
+ expect(error.stackTrace, contains('executeTypesMacro'));
+ }
+
+ test_macroApplicationErrors_throwsException() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+import 'package:_fe_analyzer_shared/src/macros/api.dart';
+
+macro class MyMacro implements ClassTypesMacro {
+ buildTypesForClass(clazz, builder) {
+ throw 'foo bar';
+ }
+}
+''');
+
+ final library = await buildLibrary(r'''
+import 'a.dart';
+
+@MyMacro()
+class A {}
+''', preBuildSequence: [
+ {'package:test/a.dart'}
+ ]);
+
+ final A = library.getType('A') as ClassElementImpl;
+ final error = A.macroApplicationErrors.single;
+ error as UnknownMacroApplicationError;
+
+ expect(error.annotationIndex, 0);
+ expect(error.message, 'foo bar');
+ expect(error.stackTrace, contains('MyMacro.buildTypesForClass'));
+ }
+
test_macroFlag_class() async {
var library = await buildLibrary(r'''
macro class A {}
@@ -577,7 +622,7 @@
import 'package:_fe_analyzer_shared/src/macros/api.dart';
macro class ArgumentsTextMacro implements ClassTypesMacro {
-${fields.entries.map((e) => ' final${e.value} ${e.key}').join('\n')}
+${fields.entries.map((e) => ' final ${e.value} ${e.key};').join('\n')}
const ArgumentsTextMacro${constructorParametersCode.trim()};
diff --git a/pkg/compiler/lib/src/common/elements.dart b/pkg/compiler/lib/src/common/elements.dart
index 2919892..c190bfc 100644
--- a/pkg/compiler/lib/src/common/elements.dart
+++ b/pkg/compiler/lib/src/common/elements.dart
@@ -247,7 +247,7 @@
LibraryEntity get foreignLibrary =>
_foreignLibrary ??= _env.lookupLibrary(Uris.dart__foreign_helper);
- /// The dart:_internal library.
+ /// The dart:_rti library.
LibraryEntity get rtiLibrary =>
_rtiLibrary ??= _env.lookupLibrary(Uris.dart__rti, required: true);
@@ -926,9 +926,24 @@
FunctionEntity get defineProperty => _findHelperFunction('defineProperty');
+ FunctionEntity get throwLateFieldNI =>
+ _findLateHelperFunction('throwLateFieldNI');
+
+ FunctionEntity get throwLateFieldAI =>
+ _findLateHelperFunction('throwLateFieldAI');
+
FunctionEntity get throwLateFieldADI =>
_findLateHelperFunction('throwLateFieldADI');
+ FunctionEntity get throwUnnamedLateFieldNI =>
+ _findLateHelperFunction('throwUnnamedLateFieldNI');
+
+ FunctionEntity get throwUnnamedLateFieldAI =>
+ _findLateHelperFunction('throwUnnamedLateFieldAI');
+
+ FunctionEntity get throwUnnamedLateFieldADI =>
+ _findLateHelperFunction('throwUnnamedLateFieldADI');
+
bool isExtractTypeArguments(FunctionEntity member) {
return member.name == 'extractTypeArguments' &&
member.library == internalLibrary;
@@ -1120,6 +1135,9 @@
/// Most foreign helpers are located in the `dart:_foreign_helper` library.
bool isForeignHelper(MemberEntity member) {
return member.library == foreignLibrary ||
+ isLateReadCheck(member) ||
+ isLateWriteOnceCheck(member) ||
+ isLateInitializeOnceCheck(member) ||
isCreateInvocationMirrorHelper(member);
}
@@ -1139,17 +1157,35 @@
_isTopLevelFunctionNamed('isJsSentinel', member);
/// Returns `true` if [member] is the `_lateReadCheck` function defined in
- /// dart:_internal.
+ /// dart:_late_helper.
bool isLateReadCheck(MemberEntity member) =>
member.library == lateHelperLibrary &&
_isTopLevelFunctionNamed('_lateReadCheck', member);
+ /// Returns `true` if [member] is the `_lateWriteOnceCheck` function defined
+ /// in dart:_late_helper.
+ bool isLateWriteOnceCheck(MemberEntity member) =>
+ member.library == lateHelperLibrary &&
+ _isTopLevelFunctionNamed('_lateWriteOnceCheck', member);
+
+ /// Returns `true` if [member] is the `_lateInitializeOnceCheck` function
+ /// defined in dart:_late_helper.
+ bool isLateInitializeOnceCheck(MemberEntity member) =>
+ member.library == lateHelperLibrary &&
+ _isTopLevelFunctionNamed('_lateInitializeOnceCheck', member);
+
/// Returns `true` if [member] is the `createSentinel` function defined in
/// dart:_internal.
bool isCreateSentinel(MemberEntity member) =>
member.library == internalLibrary &&
_isTopLevelFunctionNamed('createSentinel', member);
+ /// Returns `true` if [member] is the `isSentinel` function defined in
+ /// dart:_internal.
+ bool isIsSentinel(MemberEntity member) =>
+ member.library == internalLibrary &&
+ _isTopLevelFunctionNamed('isSentinel', member);
+
ClassEntity getDefaultSuperclass(
ClassEntity cls, NativeBasicData nativeBasicData) {
if (nativeBasicData.isJsInteropClass(cls)) {
diff --git a/pkg/compiler/lib/src/inferrer/builder_kernel.dart b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
index d1f6982..a1c9da3 100644
--- a/pkg/compiler/lib/src/inferrer/builder_kernel.dart
+++ b/pkg/compiler/lib/src/inferrer/builder_kernel.dart
@@ -279,7 +279,7 @@
Local local = _localsMap.getLocalVariable(node);
DartType type = _localsMap.getLocalType(_elementMap, local);
_state.updateLocal(_inferrer, _capturedAndBoxed, local,
- _inferrer.typeOfParameter(local), node, type);
+ _inferrer.typeOfParameter(local), type);
if (isOptional) {
TypeInformation type;
if (node.initializer != null) {
@@ -816,10 +816,10 @@
DartType type = _localsMap.getLocalType(_elementMap, local);
if (node.initializer == null) {
_state.updateLocal(
- _inferrer, _capturedAndBoxed, local, _types.nullType, node, type);
+ _inferrer, _capturedAndBoxed, local, _types.nullType, type);
} else {
- _state.updateLocal(_inferrer, _capturedAndBoxed, local,
- visit(node.initializer), node, type);
+ _state.updateLocal(
+ _inferrer, _capturedAndBoxed, local, visit(node.initializer), type);
}
if (node.initializer is ir.ThisExpression) {
_state.markThisAsExposed();
@@ -844,8 +844,7 @@
}
Local local = _localsMap.getLocalVariable(node.variable);
DartType type = _localsMap.getLocalType(_elementMap, local);
- _state.updateLocal(
- _inferrer, _capturedAndBoxed, local, rhsType, node, type);
+ _state.updateLocal(_inferrer, _capturedAndBoxed, local, rhsType, type);
return rhsType;
}
@@ -938,7 +937,7 @@
// receiver of the call to `==`, which doesn't happen in this case. Remove
// this when the ssa builder recognizes `== null` directly.
_typeOfReceiver(node, node.expression);
- _potentiallyAddNullCheck(node, node.expression);
+ _potentiallyAddNullCheck(node.expression);
return _types.boolType;
}
@@ -996,11 +995,11 @@
bool rightIsNull = _types.isNull(rightType);
if (leftIsNull) {
// [right] is `null` if [node] evaluates to `true`.
- _potentiallyAddNullCheck(node, right);
+ _potentiallyAddNullCheck(right);
}
if (rightIsNull) {
// [left] is `null` if [node] evaluates to `true`.
- _potentiallyAddNullCheck(node, left);
+ _potentiallyAddNullCheck(left);
}
if (leftIsNull || rightIsNull) {
// `left == right` where `left` and/or `right` is known to have type
@@ -1088,7 +1087,7 @@
// Receiver strengthening to non-null.
DartType type = _localsMap.getLocalType(_elementMap, local);
_state.updateLocal(
- _inferrer, _capturedAndBoxed, local, receiverType, node, type,
+ _inferrer, _capturedAndBoxed, local, receiverType, type,
excludeNull: !selector.appliesToNullWithoutThrow());
}
}
@@ -1191,8 +1190,8 @@
Local variable = _localsMap.getLocalVariable(node.variable);
DartType variableType = _localsMap.getLocalType(_elementMap, variable);
- _state.updateLocal(_inferrer, _capturedAndBoxed, variable, currentType,
- node.variable, variableType);
+ _state.updateLocal(
+ _inferrer, _capturedAndBoxed, variable, currentType, variableType);
JumpTarget target = _localsMap.getJumpTargetForForIn(node);
return handleLoop(node, target, () {
@@ -1550,6 +1549,32 @@
} else if (_closedWorld.commonElements.isCreateSentinel(member)) {
handleStaticInvoke(node, selector, member, arguments);
return _types.lateSentinelType;
+ } else if (_closedWorld.commonElements.isIsSentinel(member)) {
+ handleStaticInvoke(node, selector, member, arguments);
+
+ // Calls to `isSentinel` can only come from the late lowering kernel
+ // transformation.
+ final value = node.arguments.positional.single as ir.VariableGet;
+
+ Local local = _localsMap.getLocalVariable(value.variable);
+ DartType localType = _localsMap.getLocalType(_elementMap, local);
+ LocalState stateWhenSentinel = LocalState.childPath(_state);
+ LocalState stateWhenNotSentinel = LocalState.childPath(_state);
+
+ // Narrow tested variable to late sentinel on true branch.
+ stateWhenSentinel.updateLocal(_inferrer, _capturedAndBoxed, local,
+ _types.lateSentinelType, localType);
+
+ // Narrow tested variable to not late sentinel on false branch.
+ TypeInformation currentTypeInformation =
+ stateWhenNotSentinel.readLocal(_inferrer, _capturedAndBoxed, local);
+ stateWhenNotSentinel.updateLocal(_inferrer, _capturedAndBoxed, local,
+ currentTypeInformation, localType,
+ excludeLateSentinel: true);
+
+ _setStateAfter(_state, stateWhenSentinel, stateWhenNotSentinel);
+
+ return _types.boolType;
} else if (member.isConstructor) {
return handleConstructorInvoke(
node, node.arguments, selector, member, arguments);
@@ -1721,13 +1746,13 @@
TypeInformation currentTypeInformation = stateAfterCheckWhenTrue
.readLocal(_inferrer, _capturedAndBoxed, local);
stateAfterCheckWhenTrue.updateLocal(_inferrer, _capturedAndBoxed, local,
- currentTypeInformation, node, localType,
+ currentTypeInformation, localType,
isCast: false);
_setStateAfter(_state, stateAfterCheckWhenTrue, stateAfterCheckWhenFalse);
}
}
- void _potentiallyAddNullCheck(ir.Expression node, ir.Expression receiver) {
+ void _potentiallyAddNullCheck(ir.Expression receiver) {
if (!_accumulateIsChecks) return;
if (receiver is ir.VariableGet) {
Local local = _localsMap.getLocalVariable(receiver.variable);
@@ -1736,19 +1761,14 @@
LocalState stateAfterCheckWhenNotNull = LocalState.childPath(_state);
// Narrow tested variable to 'Null' on true branch.
- stateAfterCheckWhenNull.updateLocal(_inferrer, _capturedAndBoxed, local,
- _types.nullType, node, localType);
+ stateAfterCheckWhenNull.updateLocal(
+ _inferrer, _capturedAndBoxed, local, _types.nullType, localType);
// Narrow tested variable to 'not null' on false branch.
TypeInformation currentTypeInformation = stateAfterCheckWhenNotNull
.readLocal(_inferrer, _capturedAndBoxed, local);
- stateAfterCheckWhenNotNull.updateLocal(
- _inferrer,
- _capturedAndBoxed,
- local,
- currentTypeInformation,
- node,
- _closedWorld.commonElements.objectType,
+ stateAfterCheckWhenNotNull.updateLocal(_inferrer, _capturedAndBoxed,
+ local, currentTypeInformation, _closedWorld.commonElements.objectType,
excludeNull: true);
_setStateAfter(
_state, stateAfterCheckWhenNull, stateAfterCheckWhenNotNull);
@@ -1904,7 +1924,7 @@
Local local = _localsMap.getLocalVariable(variable);
DartType type = _localsMap.getLocalType(_elementMap, local);
_state.updateLocal(
- _inferrer, _capturedAndBoxed, local, localFunctionType, node, type,
+ _inferrer, _capturedAndBoxed, local, localFunctionType, type,
excludeNull: true);
}
@@ -2059,8 +2079,8 @@
mask = _types.dynamicType;
}
Local local = _localsMap.getLocalVariable(exception);
- _state.updateLocal(_inferrer, _capturedAndBoxed, local, mask, node,
- _dartTypes.dynamicType(),
+ _state.updateLocal(
+ _inferrer, _capturedAndBoxed, local, mask, _dartTypes.dynamicType(),
excludeNull: true /* `throw null` produces a NullThrownError */);
}
ir.VariableDeclaration stackTrace = node.stackTrace;
@@ -2070,7 +2090,7 @@
// Note: stack trace may be null if users omit a stack in
// `completer.completeError`.
_state.updateLocal(_inferrer, _capturedAndBoxed, local,
- _types.dynamicType, node, _dartTypes.dynamicType());
+ _types.dynamicType, _dartTypes.dynamicType());
}
visit(node.body);
return null;
@@ -2408,7 +2428,7 @@
if (field != null) {
return inferrer.typeOfMember(field);
} else {
- return _locals.use(inferrer, local);
+ return _locals.use(local);
}
}
@@ -2417,19 +2437,21 @@
Map<Local, FieldEntity> capturedAndBoxed,
Local local,
TypeInformation type,
- ir.Node node,
DartType staticType,
{isCast = true,
- excludeNull = false}) {
+ excludeNull = false,
+ excludeLateSentinel = false}) {
assert(type != null);
type = inferrer.types.narrowType(type, staticType,
- isCast: isCast, excludeNull: excludeNull, excludeLateSentinel: true);
+ isCast: isCast,
+ excludeNull: excludeNull,
+ excludeLateSentinel: excludeLateSentinel);
FieldEntity field = capturedAndBoxed[local];
if (field != null) {
inferrer.recordTypeOfField(field, type);
} else {
- _locals.update(inferrer, local, type, node, staticType, _tryBlock);
+ _locals.update(inferrer, local, type, _tryBlock);
}
}
diff --git a/pkg/compiler/lib/src/inferrer/locals_handler.dart b/pkg/compiler/lib/src/inferrer/locals_handler.dart
index 2545b2b..d32de95 100644
--- a/pkg/compiler/lib/src/inferrer/locals_handler.dart
+++ b/pkg/compiler/lib/src/inferrer/locals_handler.dart
@@ -9,7 +9,6 @@
import 'dart:collection' show IterableMixin;
import 'package:kernel/ast.dart' as ir;
import '../elements/entities.dart';
-import '../elements/types.dart';
import '../ir/util.dart';
import '../util/util.dart';
import 'inferrer_engine.dart';
@@ -342,12 +341,12 @@
LocalsHandler.deepCopyOf(LocalsHandler other)
: _locals = VariableScope.deepCopyOf(other._locals);
- TypeInformation use(InferrerEngine inferrer, Local local) {
+ TypeInformation use(Local local) {
return _locals[local];
}
void update(InferrerEngine inferrer, Local local, TypeInformation type,
- ir.Node node, DartType staticType, LocalsHandler tryBlock) {
+ LocalsHandler tryBlock) {
if (tryBlock != null) {
// We don't know if an assignment in a try block
// will be executed, so all assignments in that block are
diff --git a/pkg/compiler/lib/src/inferrer/type_system.dart b/pkg/compiler/lib/src/inferrer/type_system.dart
index d28d06f..9a90a88 100644
--- a/pkg/compiler/lib/src/inferrer/type_system.dart
+++ b/pkg/compiler/lib/src/inferrer/type_system.dart
@@ -9,7 +9,6 @@
import '../constants/values.dart' show BoolConstantValue;
import '../elements/entities.dart';
import '../elements/types.dart';
-import '../ir/static_type.dart' show ClassRelation;
import '../world.dart';
import 'abstract_value_domain.dart';
import 'type_graph_nodes.dart';
@@ -350,34 +349,49 @@
{bool isCast = true,
bool excludeNull = false,
bool excludeLateSentinel = false}) {
- // Avoid refining an input with an exact type. It we are almost always
+ AbstractValue inferredType = type.type;
+
+ TypeInformation _excludeLateSentinel() {
+ if (!excludeLateSentinel) return type;
+ final newType = NarrowTypeInformation(
+ _abstractValueDomain, type, _abstractValueDomain.dynamicType);
+ allocatedTypes.add(newType);
+ return newType;
+ }
+
+ // Avoid refining an input with an exact type, since we are almost always
// adding a narrowing to a subtype of the same class or a superclass.
- if (_abstractValueDomain.isExact(type.type).isDefinitelyTrue) return type;
+ if (_abstractValueDomain.isExact(inferredType).isDefinitelyTrue) {
+ return _excludeLateSentinel();
+ }
- AbstractValueWithPrecision narrowing =
- _abstractValueDomain.createFromStaticType(annotation,
- classRelation: ClassRelation.subtype, nullable: isCast);
+ AbstractValue narrowing = _abstractValueDomain
+ .createFromStaticType(annotation, nullable: isCast)
+ .abstractValue;
- AbstractValue abstractValue = narrowing.abstractValue;
if (excludeNull) {
- abstractValue = _abstractValueDomain.excludeNull(abstractValue);
- }
- if (!excludeLateSentinel) {
- abstractValue = _abstractValueDomain.includeLateSentinel(abstractValue);
+ narrowing = _abstractValueDomain.excludeNull(narrowing);
}
- if (_abstractValueDomain.containsAll(abstractValue).isPotentiallyTrue) {
+ if (!excludeLateSentinel) {
+ // Narrowing is an intersection of [AbstractValue]s. Unless
+ // [excludeLateSentinel] is `true`, we include the late sentinel here so
+ // that it is preserved by the intersection.
+ narrowing = _abstractValueDomain.includeLateSentinel(narrowing);
+ }
+
+ if (_abstractValueDomain.containsAll(narrowing).isPotentiallyTrue) {
// Top, or non-nullable Top.
- if (_abstractValueDomain.isNull(abstractValue).isPotentiallyTrue) {
- return type;
+ if (_abstractValueDomain.isNull(narrowing).isPotentiallyTrue) {
+ return _excludeLateSentinel();
}
- // If the input is already narrowed to be not-null, there is no value
- // in adding another narrowing node.
- if (_isNonNullNarrow(type)) return type;
+ // If the input is already narrowed to be non-null, there is no value in
+ // adding another non-null narrowing node.
+ if (_isNonNullNarrow(type)) return _excludeLateSentinel();
}
TypeInformation newType =
- NarrowTypeInformation(_abstractValueDomain, type, abstractValue);
+ NarrowTypeInformation(_abstractValueDomain, type, narrowing);
allocatedTypes.add(newType);
return newType;
}
diff --git a/pkg/compiler/lib/src/js_backend/backend_impact.dart b/pkg/compiler/lib/src/js_backend/backend_impact.dart
index 37855c0..f6dfa17 100644
--- a/pkg/compiler/lib/src/js_backend/backend_impact.dart
+++ b/pkg/compiler/lib/src/js_backend/backend_impact.dart
@@ -779,4 +779,28 @@
], otherImpacts: [
_needsString('Needed to encode the new RTI ruleset.')
]);
+
+ BackendImpact _lateFieldReadCheck;
+
+ BackendImpact get lateFieldReadCheck =>
+ _lateFieldReadCheck ??= BackendImpact(globalUses: [
+ _commonElements.throwUnnamedLateFieldNI,
+ _commonElements.throwLateFieldNI,
+ ]);
+
+ BackendImpact _lateFieldWriteOnceCheck;
+
+ BackendImpact get lateFieldWriteOnceCheck =>
+ _lateFieldWriteOnceCheck ??= BackendImpact(globalUses: [
+ _commonElements.throwUnnamedLateFieldAI,
+ _commonElements.throwLateFieldAI,
+ ]);
+
+ BackendImpact _lateFieldInitializeOnceCheck;
+
+ BackendImpact get lateFieldInitializeOnceCheck =>
+ _lateFieldInitializeOnceCheck ??= BackendImpact(globalUses: [
+ _commonElements.throwUnnamedLateFieldADI,
+ _commonElements.throwLateFieldADI,
+ ]);
}
diff --git a/pkg/compiler/lib/src/js_backend/resolution_listener.dart b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
index 88e96d3..c6b8b41 100644
--- a/pkg/compiler/lib/src/js_backend/resolution_listener.dart
+++ b/pkg/compiler/lib/src/js_backend/resolution_listener.dart
@@ -292,6 +292,19 @@
_registerBackendImpact(worldImpact, _impacts.noSuchMethodSupport);
}
+ if (_commonElements.isLateReadCheck(member)) {
+ _registerBackendImpact(worldImpact, _impacts.lateFieldReadCheck);
+ }
+
+ if (_commonElements.isLateWriteOnceCheck(member)) {
+ _registerBackendImpact(worldImpact, _impacts.lateFieldWriteOnceCheck);
+ }
+
+ if (_commonElements.isLateInitializeOnceCheck(member)) {
+ _registerBackendImpact(
+ worldImpact, _impacts.lateFieldInitializeOnceCheck);
+ }
+
if (member.isGetter && member.name == Identifiers.runtimeType_) {
// Enable runtime type support if we discover a getter called
// runtimeType. We have to enable runtime type before hitting the
diff --git a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
index bdec099..7711128 100644
--- a/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
+++ b/pkg/compiler/lib/src/kernel/transformations/late_lowering.dart
@@ -462,6 +462,16 @@
// }
// return value;
// }
+ //
+ // The following lowering is also possible but currently worse:
+ //
+ // T get field {
+ // var value = this._#field;
+ // return isSentinel(value) ? this._#field = e : value;
+ // }
+ //
+ // This lowering avoids generating an extra narrowing node in inference,
+ // but the generated code is worse due to poor register allocation.
VariableDeclaration value =
VariableDeclaration('value', initializer: fieldRead(), type: type)
..fileOffset = fileOffset;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index c34f02f..02d707c 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -4212,6 +4212,12 @@
_handleForeignCreateJsSentinel(invocation);
} else if (name == 'isJsSentinel') {
_handleForeignIsJsSentinel(invocation);
+ } else if (name == '_lateReadCheck') {
+ _handleLateReadCheck(invocation);
+ } else if (name == '_lateWriteOnceCheck') {
+ _handleLateWriteOnceCheck(invocation);
+ } else if (name == '_lateInitializeOnceCheck') {
+ _handleLateInitializeOnceCheck(invocation);
} else {
reporter.internalError(
_elementMap.getSpannable(targetElement, invocation),
@@ -4901,6 +4907,14 @@
}
void _handleForeignCreateJsSentinel(ir.StaticInvocation invocation) {
+ if (_unexpectedForeignArguments(invocation,
+ minPositional: 0, maxPositional: 0, typeArgumentCount: 1)) {
+ stack.add(
+ // Result expected on stack.
+ graph.addConstantNull(closedWorld));
+ return;
+ }
+
SourceInformation sourceInformation =
_sourceInformationBuilder.buildCall(invocation, invocation);
stack.add(graph.addConstantLateSentinel(closedWorld,
@@ -4908,6 +4922,14 @@
}
void _handleForeignIsJsSentinel(ir.StaticInvocation invocation) {
+ if (_unexpectedForeignArguments(invocation,
+ minPositional: 1, maxPositional: 1)) {
+ stack.add(
+ // Result expected on stack.
+ graph.addConstantNull(closedWorld));
+ return;
+ }
+
SourceInformation sourceInformation =
_sourceInformationBuilder.buildCall(invocation, invocation);
HInstruction checkedExpression =
@@ -4916,6 +4938,72 @@
..sourceInformation = sourceInformation);
}
+ // TODO(fishythefish): Support specialization of late sentinels based on type.
+
+ void _handleLateReadCheck(ir.StaticInvocation invocation) {
+ if (_unexpectedForeignArguments(invocation,
+ minPositional: 2, maxPositional: 2, typeArgumentCount: 1)) {
+ stack.add(
+ // Result expected on stack.
+ graph.addConstantNull(closedWorld));
+ return;
+ }
+
+ SourceInformation sourceInformation =
+ _sourceInformationBuilder.buildCall(invocation, invocation);
+
+ List<HInstruction> arguments =
+ _visitPositionalArguments(invocation.arguments);
+ HInstruction value = arguments[0];
+ HInstruction name = options.omitLateNames ? null : arguments[1];
+
+ push(HLateReadCheck(value, name,
+ _abstractValueDomain.excludeLateSentinel(value.instructionType))
+ ..sourceInformation = sourceInformation);
+ }
+
+ void _handleLateWriteOnceCheck(ir.StaticInvocation invocation) {
+ if (_unexpectedForeignArguments(invocation,
+ minPositional: 2, maxPositional: 2)) {
+ stack.add(
+ // Result expected on stack.
+ graph.addConstantNull(closedWorld));
+ return;
+ }
+
+ SourceInformation sourceInformation =
+ _sourceInformationBuilder.buildCall(invocation, invocation);
+
+ List<HInstruction> arguments =
+ _visitPositionalArguments(invocation.arguments);
+ HInstruction value = arguments[0];
+ HInstruction name = options.omitLateNames ? null : arguments[1];
+
+ push(HLateWriteOnceCheck(value, name, _abstractValueDomain.dynamicType)
+ ..sourceInformation = sourceInformation);
+ }
+
+ void _handleLateInitializeOnceCheck(ir.StaticInvocation invocation) {
+ if (_unexpectedForeignArguments(invocation,
+ minPositional: 2, maxPositional: 2)) {
+ stack.add(
+ // Result expected on stack.
+ graph.addConstantNull(closedWorld));
+ return;
+ }
+
+ SourceInformation sourceInformation =
+ _sourceInformationBuilder.buildCall(invocation, invocation);
+
+ List<HInstruction> arguments =
+ _visitPositionalArguments(invocation.arguments);
+ HInstruction value = arguments[0];
+ HInstruction name = options.omitLateNames ? null : arguments[1];
+
+ push(HLateInitializeOnceCheck(value, name, _abstractValueDomain.dynamicType)
+ ..sourceInformation = sourceInformation);
+ }
+
void _pushStaticInvocation(MemberEntity target, List<HInstruction> arguments,
AbstractValue typeMask, List<DartType> typeArguments,
{SourceInformation sourceInformation, InterfaceType instanceType}) {
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index dbdf302..c394f07 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -729,7 +729,8 @@
instruction is HAsCheck ||
instruction is HAsCheckSimple ||
instruction is HBoolConversion ||
- instruction is HNullCheck) {
+ instruction is HNullCheck ||
+ instruction is HLateReadCheck) {
String inputName = variableNames.getName(instruction.checkedInput);
if (variableNames.getName(instruction) == inputName) {
needsAssignment = false;
@@ -3017,6 +3018,94 @@
}
@override
+ void visitLateReadCheck(HLateReadCheck node) {
+ // We generate code roughly equivalent to invoking:
+ //
+ // T _lateReadCheck<T>(T value, String name) {
+ // if (isSentinel(value)) throw LateError.fieldNI(name);
+ // return value;
+ // }
+
+ assert(!node.isRedundant(_closedWorld));
+
+ final sourceInformation = node.sourceInformation;
+
+ _emitIsLateSentinel(node.checkedInput, sourceInformation);
+ final condition = pop();
+
+ if (node.hasName) {
+ use(node.name);
+ _pushCallStatic(
+ _commonElements.throwLateFieldNI, [pop()], sourceInformation);
+ } else {
+ _pushCallStatic(
+ _commonElements.throwUnnamedLateFieldNI, const [], sourceInformation);
+ }
+
+ final lateError =
+ pop().toStatement().withSourceInformation(sourceInformation);
+ pushStatement(js.If.noElse(condition, lateError)
+ .withSourceInformation(sourceInformation));
+ }
+
+ @override
+ void visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+ // We generate code roughly equivalent to invoking:
+ //
+ // void _lateWriteOnceCheck(Object? value, String name) {
+ // if (!isSentinel(value)) throw LateError.fieldAI(name);
+ // }
+
+ assert(!node.isRedundant(_closedWorld));
+
+ final sourceInformation = node.sourceInformation;
+ _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
+ final condition = pop();
+
+ if (node.hasName) {
+ use(node.name);
+ _pushCallStatic(
+ _commonElements.throwLateFieldAI, [pop()], sourceInformation);
+ } else {
+ _pushCallStatic(
+ _commonElements.throwUnnamedLateFieldAI, [], sourceInformation);
+ }
+ final lateError =
+ pop().toStatement().withSourceInformation(sourceInformation);
+ pushStatement(js.If.noElse(condition, lateError)
+ .withSourceInformation(sourceInformation));
+ }
+
+ @override
+ void visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+ // We generate code roughly equivalent to invoking:
+ //
+ // void _lateInitializeOnceCheck(Object? value, String name) {
+ // if (!isSentinel(value)) throw LateError.fieldADI(name);
+ // }
+
+ assert(!node.isRedundant(_closedWorld));
+
+ final sourceInformation = node.sourceInformation;
+ _emitIsLateSentinel(node.checkedInput, sourceInformation, inverse: true);
+ final condition = pop();
+
+ if (node.hasName) {
+ use(node.name);
+ _pushCallStatic(
+ _commonElements.throwLateFieldADI, [pop()], sourceInformation);
+ } else {
+ _pushCallStatic(
+ _commonElements.throwUnnamedLateFieldADI, [], sourceInformation);
+ }
+
+ final lateError =
+ pop().toStatement().withSourceInformation(sourceInformation);
+ pushStatement(js.If.noElse(condition, lateError)
+ .withSourceInformation(sourceInformation));
+ }
+
+ @override
void visitTypeKnown(HTypeKnown node) {
// [HTypeKnown] instructions are removed before generating code.
assert(false);
@@ -3299,9 +3388,9 @@
StaticUse.directInvoke(method, selector.callStructure, null));
}
- _emitIsLateSentinel(HIsLateSentinel node, SourceInformation sourceInformation,
+ _emitIsLateSentinel(HInstruction input, SourceInformation sourceInformation,
{inverse = false}) {
- use(node.inputs[0]);
+ use(input);
js.Expression value = pop();
js.Expression sentinel =
_emitter.constantReference(LateSentinelConstantValue());
@@ -3311,5 +3400,5 @@
@override
visitIsLateSentinel(HIsLateSentinel node) =>
- _emitIsLateSentinel(node, node.sourceInformation);
+ _emitIsLateSentinel(node.inputs.single, node.sourceInformation);
}
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 600cbac..ca162ab 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -778,6 +778,19 @@
}
@override
+ void visitLateReadCheck(HLateReadCheck instruction) {
+ // If the checked value is used, the input might still have one use
+ // (i.e. this HLateReadCheck), but it cannot be generated at use, since we
+ // will rely on non-generate-at-use to assign the value to a variable.
+ //
+ // However, if the checked value is unused then the input may be generated
+ // at use in the check.
+ if (instruction.usedBy.isEmpty) {
+ visitInstruction(instruction);
+ }
+ }
+
+ @override
void visitTypeKnown(HTypeKnown instruction) {
// [HTypeKnown] instructions are removed before code generation.
assert(false);
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 16ac75c..6e8e464 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -105,6 +105,9 @@
R visitPrimitiveCheck(HPrimitiveCheck node);
R visitBoolConversion(HBoolConversion node);
R visitNullCheck(HNullCheck node);
+ R visitLateReadCheck(HLateReadCheck node);
+ R visitLateWriteOnceCheck(HLateWriteOnceCheck node);
+ R visitLateInitializeOnceCheck(HLateInitializeOnceCheck node);
R visitTypeKnown(HTypeKnown node);
R visitYield(HYield node);
@@ -591,6 +594,13 @@
@override
visitNullCheck(HNullCheck node) => visitCheck(node);
@override
+ visitLateReadCheck(HLateReadCheck node) => visitCheck(node);
+ @override
+ visitLateWriteOnceCheck(HLateWriteOnceCheck node) => visitCheck(node);
+ @override
+ visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) =>
+ visitCheck(node);
+ @override
visitPrimitiveCheck(HPrimitiveCheck node) => visitCheck(node);
@override
visitTypeKnown(HTypeKnown node) => visitCheck(node);
@@ -1101,6 +1111,10 @@
static const int STRING_CONCAT_TYPECODE = 59;
static const int STRINGIFY_TYPECODE = 60;
+ static const int LATE_READ_CHECK_TYPECODE = 61;
+ static const int LATE_WRITE_ONCE_CHECK_TYPECODE = 62;
+ static const int LATE_INITIALIZE_ONCE_CHECK_TYPECODE = 63;
+
HInstruction(this.inputs, this.instructionType) {
assert(inputs.every((e) => e != null), "inputs: $inputs");
}
@@ -3677,6 +3691,121 @@
}
}
+/// A check for a late sentinel to determine if a late field may be read from or
+/// written to.
+abstract class HLateCheck extends HCheck {
+ final HInstruction name;
+
+ HLateCheck(HInstruction input, this.name, AbstractValue type)
+ : super([input, if (name != null) name], type);
+
+ bool get hasName => name != null;
+
+ @override
+ bool isControlFlow() => true;
+
+ @override
+ bool isCodeMotionInvariant() => false;
+}
+
+/// A check that a late field has been initialized and can therefore be read.
+class HLateReadCheck extends HLateCheck {
+ HLateReadCheck(HInstruction input, HInstruction name, AbstractValue type)
+ : super(input, name, type);
+
+ @override
+ accept(HVisitor visitor) => visitor.visitLateReadCheck(this);
+
+ @override
+ int typeCode() => HInstruction.LATE_READ_CHECK_TYPECODE;
+
+ @override
+ bool typeEquals(HInstruction other) => other is HLateReadCheck;
+
+ @override
+ bool dataEquals(HLateReadCheck other) => true;
+
+ bool isRedundant(JClosedWorld closedWorld) {
+ AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+ AbstractValue inputType = checkedInput.instructionType;
+ return abstractValueDomain.isLateSentinel(inputType).isDefinitelyFalse;
+ }
+
+ @override
+ String toString() {
+ return 'HLateReadCheck($checkedInput)';
+ }
+}
+
+/// A check that a late final field has not been initialized yet and can
+/// therefore be written to.
+///
+/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
+/// is that the latter occurs on writes performed as part of the initializer
+/// expression.
+class HLateWriteOnceCheck extends HLateCheck {
+ HLateWriteOnceCheck(HInstruction input, HInstruction name, AbstractValue type)
+ : super(input, name, type);
+
+ @override
+ accept(HVisitor visitor) => visitor.visitLateWriteOnceCheck(this);
+
+ @override
+ int typeCode() => HInstruction.LATE_WRITE_ONCE_CHECK_TYPECODE;
+
+ @override
+ bool typeEquals(HInstruction other) => other is HLateWriteOnceCheck;
+
+ @override
+ bool dataEquals(HLateWriteOnceCheck other) => true;
+
+ bool isRedundant(JClosedWorld closedWorld) {
+ AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+ AbstractValue inputType = checkedInput.instructionType;
+ return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
+ }
+
+ @override
+ String toString() {
+ return 'HLateWriteOnceCheck($checkedInput)';
+ }
+}
+
+/// A check that a late final field has not been initialized yet and can
+/// therefore be initialized.
+///
+/// The difference between [HLateWriteOnceCheck] and [HLateInitializeOnceCheck]
+/// is that the latter occurs on writes performed as part of the initializer
+/// expression.
+class HLateInitializeOnceCheck extends HLateCheck {
+ HLateInitializeOnceCheck(
+ HInstruction input, HInstruction name, AbstractValue type)
+ : super(input, name, type);
+
+ @override
+ accept(HVisitor visitor) => visitor.visitLateInitializeOnceCheck(this);
+
+ @override
+ int typeCode() => HInstruction.LATE_INITIALIZE_ONCE_CHECK_TYPECODE;
+
+ @override
+ bool typeEquals(HInstruction other) => other is HLateInitializeOnceCheck;
+
+ @override
+ bool dataEquals(HLateInitializeOnceCheck other) => true;
+
+ bool isRedundant(JClosedWorld closedWorld) {
+ AbstractValueDomain abstractValueDomain = closedWorld.abstractValueDomain;
+ AbstractValue inputType = checkedInput.instructionType;
+ return abstractValueDomain.isLateSentinel(inputType).isDefinitelyTrue;
+ }
+
+ @override
+ String toString() {
+ return 'HLateInitializeOnceCheck($checkedInput)';
+ }
+}
+
/// The [HTypeKnown] instruction marks a value with a refined type.
class HTypeKnown extends HCheck {
AbstractValue knownType;
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 81af4d3..75f9512 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -1399,6 +1399,24 @@
}
@override
+ HInstruction visitLateReadCheck(HLateReadCheck node) {
+ if (node.isRedundant(_closedWorld)) return node.checkedInput;
+ return node;
+ }
+
+ @override
+ HInstruction visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+ if (node.isRedundant(_closedWorld)) return node.checkedInput;
+ return node;
+ }
+
+ @override
+ HInstruction visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+ if (node.isRedundant(_closedWorld)) return node.checkedInput;
+ return node;
+ }
+
+ @override
HInstruction visitTypeKnown(HTypeKnown node) {
return node.isRedundant(_closedWorld) ? node.checkedInput : node;
}
@@ -3771,6 +3789,8 @@
@override
void visitNullCheck(HNullCheck instruction) {}
@override
+ void visitLateReadCheck(HLateReadCheck instruction) {}
+ @override
void visitParameterValue(HParameterValue instruction) {}
@override
void visitRelational(HRelational instruction) {}
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 0db158a..f04fe16 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -668,6 +668,27 @@
}
@override
+ String visitLateReadCheck(HLateReadCheck node) {
+ String checkedInput = temporaryId(node.checkedInput);
+ String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+ return "LateReadCheck$comment: $checkedInput";
+ }
+
+ @override
+ String visitLateWriteOnceCheck(HLateWriteOnceCheck node) {
+ String checkedInput = temporaryId(node.checkedInput);
+ String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+ return "LateWriteOnceCheck$comment: $checkedInput";
+ }
+
+ @override
+ String visitLateInitializeOnceCheck(HLateInitializeOnceCheck node) {
+ String checkedInput = temporaryId(node.checkedInput);
+ String comment = node.hasName ? "(${temporaryId(node.name)})" : "";
+ return "LateInitializeOnceCheck$comment: $checkedInput";
+ }
+
+ @override
String visitTypeKnown(HTypeKnown node) {
assert(node.inputs.length <= 2);
String result =
diff --git a/pkg/compiler/lib/src/ssa/types_propagation.dart b/pkg/compiler/lib/src/ssa/types_propagation.dart
index 082183b..1ddc6f2 100644
--- a/pkg/compiler/lib/src/ssa/types_propagation.dart
+++ b/pkg/compiler/lib/src/ssa/types_propagation.dart
@@ -449,6 +449,20 @@
}
@override
+ AbstractValue visitLateReadCheck(HLateReadCheck instruction) {
+ HInstruction input = instruction.checkedInput;
+ AbstractValue inputType = input.instructionType;
+ AbstractValue outputType =
+ abstractValueDomain.excludeLateSentinel(inputType);
+ if (inputType != outputType) {
+ // Replace dominated uses of input with uses of this check so the uses
+ // benefit from the stronger type.
+ input.replaceAllUsersDominatedBy(instruction.next, instruction);
+ }
+ return outputType;
+ }
+
+ @override
AbstractValue visitAsCheck(HAsCheck instruction) {
return _narrowAsCheck(instruction, instruction.checkedInput,
instruction.checkedType.abstractValue);
diff --git a/pkg/compiler/test/codegen/late_field_redundancy_test.dart b/pkg/compiler/test/codegen/late_field_redundancy_test.dart
new file mode 100644
index 0000000..a0ebf05
--- /dev/null
+++ b/pkg/compiler/test/codegen/late_field_redundancy_test.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.
+
+// @dart = 2.10
+
+import 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST = r"""
+class Foo {
+ late int x;
+}
+
+void entry() {
+ final foo = Foo();
+ foo.x = 42;
+ test(foo);
+}
+
+@pragma('dart2js:noInline')
+void test(Foo foo) {
+ final a = foo.x;
+ final b = foo.x;
+ print([a, b]);
+}
+""";
+
+void main() {
+ asyncTest(() async {
+ await compile(TEST,
+ entry: 'entry',
+ methodName: 'test',
+ disableTypeInference: false,
+ disableInlining: false,
+ soundNullSafety: true, check: (String generated) {
+ RegExp regexp = new RegExp(r'=== \$');
+ Expect.equals(1, regexp.allMatches(generated).length);
+ });
+ });
+}
diff --git a/pkg/compiler/test/codegen/late_field_test.dart b/pkg/compiler/test/codegen/late_field_test.dart
new file mode 100644
index 0000000..fa34ffe
--- /dev/null
+++ b/pkg/compiler/test/codegen/late_field_test.dart
@@ -0,0 +1,60 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.10
+
+import 'dart:async';
+import 'package:async_helper/async_helper.dart';
+import '../helpers/compiler_helper.dart';
+
+const String TEST_DIRECT = r"""
+class Foo {
+ late int x;
+}
+
+int test() {
+ final foo = Foo();
+ foo.x = 40;
+ return foo.x + 2;
+ // present: '42'
+ // absent: '+ 2'
+ // absent: 'add'
+}
+""";
+
+const String TEST_INDIRECT = r"""
+class Foo {
+ late int x;
+}
+
+int entry() {
+ final foo = Foo();
+ foo.x = 40;
+ return test(foo);
+}
+
+@pragma('dart2js:noInline')
+int test(Foo foo) {
+ return foo.x + 2;
+ // present: '+ 2'
+ // absent: 'add'
+}
+""";
+
+Future check(String test, {String entry: 'test'}) {
+ return compile(test,
+ entry: entry,
+ methodName: 'test',
+ check: checkerForAbsentPresent(test),
+ disableTypeInference: false,
+ disableInlining: false,
+ soundNullSafety: true);
+}
+
+void main() {
+ asyncTest(() async {
+ await check(TEST_DIRECT);
+ await check(TEST_INDIRECT, entry: 'entry');
+ });
+}
diff --git a/pkg/compiler/test/codegen/late_test.dart b/pkg/compiler/test/codegen/late_test.dart
deleted file mode 100644
index af820fb..0000000
--- a/pkg/compiler/test/codegen/late_test.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-// @dart = 2.10
-
-import 'dart:async';
-import 'package:async_helper/async_helper.dart';
-import '../helpers/compiler_helper.dart';
-
-const String TEST = r"""
-class Foo {
- late int x;
-}
-
-int foo() {
- final foo = Foo();
- foo.x = 40;
- return foo.x + 2;
- // present: '+ 2'
- // absent: 'add'
-}
-""";
-
-Future check(String test) {
- return compile(test,
- entry: 'foo',
- check: checkerForAbsentPresent(test),
- disableTypeInference: false,
- disableInlining: false,
- soundNullSafety: true);
-}
-
-void main() {
- asyncTest(() async {
- await check(TEST);
- });
-}
diff --git a/pkg/dartdev/test/core_test.dart b/pkg/dartdev/test/core_test.dart
index e06feef..dc74efa 100644
--- a/pkg/dartdev/test/core_test.dart
+++ b/pkg/dartdev/test/core_test.dart
@@ -105,7 +105,7 @@
test('hasDependency', () {
PackageConfig packageConfig = PackageConfig(jsonDecode(_packageData));
expect(packageConfig.hasDependency('test'), isFalse);
- expect(packageConfig.hasDependency('pedantic'), isTrue);
+ expect(packageConfig.hasDependency('lints'), isTrue);
});
}
@@ -149,8 +149,8 @@
"configVersion": 2,
"packages": [
{
- "name": "pedantic",
- "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/pedantic-1.9.0",
+ "name": "lints",
+ "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/lints-1.0.1",
"packageUri": "lib/",
"languageVersion": "2.1"
},
diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart
index 642adbe..a25734d 100644
--- a/pkg/dartdev/test/utils_test.dart
+++ b/pkg/dartdev/test/utils_test.dart
@@ -163,8 +163,8 @@
"configVersion": 2,
"packages": [
{
- "name": "pedantic",
- "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/pedantic-1.9.0",
+ "name": "lints",
+ "rootUri": "file:///Users/.../.pub-cache/hosted/pub.dartlang.org/lints-1.0.1",
"packageUri": "lib/",
"languageVersion": "2.1"
},
diff --git a/pkg/front_end/lib/src/fasta/incremental_compiler.dart b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
index b646c44..e193567 100644
--- a/pkg/front_end/lib/src/fasta/incremental_compiler.dart
+++ b/pkg/front_end/lib/src/fasta/incremental_compiler.dart
@@ -319,7 +319,8 @@
_benchmarker
?.enterPhase(BenchmarkPhases.incremental_experimentalInvalidation);
ExperimentalInvalidation? experimentalInvalidation =
- await _initializeExperimentalInvalidation(reusedResult, c);
+ await _initializeExperimentalInvalidation(
+ reusedResult, c, uriTranslator);
recorderForTesting?.recordRebuildBodiesCount(
experimentalInvalidation?.missingSources.length ?? 0);
@@ -1165,7 +1166,9 @@
/// Note that - when doing experimental invalidation - [reusedResult] is
/// updated.
Future<ExperimentalInvalidation?> _initializeExperimentalInvalidation(
- ReusageResult reusedResult, CompilerContext c) async {
+ ReusageResult reusedResult,
+ CompilerContext c,
+ UriTranslator uriTranslator) async {
Set<LibraryBuilder>? rebuildBodies;
Set<LibraryBuilder> originalNotReusedLibraries;
Set<Uri>? missingSources;
@@ -1208,37 +1211,46 @@
}
}
- List<int>? previousSource =
- CompilerContext.current.uriToSource[builder.fileUri]!.source;
- // ignore: unnecessary_null_comparison
- if (previousSource == null || previousSource.isEmpty) {
- return null;
+ List<Uri> builderUris = [builder.fileUri];
+ for (LibraryPart part in builder.library.parts) {
+ Uri? fileUri =
+ uriTranslator.getPartFileUri(builder.library.fileUri, part);
+ if (fileUri != null) builderUris.add(fileUri);
}
- ScannerConfiguration scannerConfiguration = new ScannerConfiguration(
- enableExtensionMethods: true /* can't be disabled */,
- enableNonNullable: builder
- .isNonNullableByDefault /* depends on language version etc */,
- enableTripleShift:
- /* should this be on the library? */
- /* this is effectively what the constant evaluator does */
- context.options.globalFeatures.tripleShift.isEnabled);
- String? before = textualOutline(previousSource, scannerConfiguration,
- performModelling: true);
- if (before == null) {
- return null;
- }
- String? now;
- FileSystemEntity entity =
- c.options.fileSystem.entityForUri(builder.fileUri);
- if (await entity.exists()) {
- now = textualOutline(await entity.readAsBytes(), scannerConfiguration,
+
+ for (Uri uri in builderUris) {
+ List<int>? previousSource =
+ CompilerContext.current.uriToSource[uri]!.source;
+ // ignore: unnecessary_null_comparison
+ if (previousSource == null || previousSource.isEmpty) {
+ return null;
+ }
+ ScannerConfiguration scannerConfiguration = new ScannerConfiguration(
+ enableExtensionMethods: true /* can't be disabled */,
+ enableNonNullable: builder
+ .isNonNullableByDefault /* depends on language version etc */,
+ enableTripleShift:
+ /* should this be on the library? */
+ /* this is effectively what the constant evaluator does */
+ context.options.globalFeatures.tripleShift.isEnabled);
+ String? before = textualOutline(previousSource, scannerConfiguration,
performModelling: true);
+ if (before == null) {
+ return null;
+ }
+ String? now;
+ FileSystemEntity entity = c.options.fileSystem.entityForUri(uri);
+ if (await entity.exists()) {
+ now = textualOutline(await entity.readAsBytes(), scannerConfiguration,
+ performModelling: true);
+ }
+ if (before != now) {
+ return null;
+ }
+ missingSources ??= new Set<Uri>();
+ missingSources.add(uri);
}
- if (before != now) {
- return null;
- }
- missingSources ??= new Set<Uri>();
- missingSources.add(builder.fileUri);
+
LibraryBuilder? partOfLibrary = builder.partOfLibrary;
rebuildBodies ??= new Set<LibraryBuilder>();
if (partOfLibrary != null) {
@@ -1255,6 +1267,8 @@
if (!skipExperimentalInvalidationChecksForTesting) {
for (LibraryBuilder builder in reusedResult.notReusedLibraries) {
if (missingSources!.contains(builder.fileUri)) {
+ // Missing sources will be rebuild, so mixin usage there doesn't
+ // matter.
continue;
}
Library lib = builder.library;
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 498d457..3d62028 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -1576,6 +1576,7 @@
}
List<Uri> originalUris = List<Uri>.of(fs.data.keys);
+ uriLoop:
for (Uri uri in originalUris) {
print("Work on $uri");
LibraryBuilder? builder = builders[uri];
@@ -1599,14 +1600,21 @@
// Put each chunk into its own file.
StringBuffer headerSb = new StringBuffer();
+ StringBuffer orgFileOnlyHeaderSb = new StringBuffer();
List<FuzzAstVisitorSorterChunk> nonHeaderChunks = [];
+ print("Found ${fuzzAstVisitorSorter.chunks.length} chunks...");
+
for (FuzzAstVisitorSorterChunk chunk in fuzzAstVisitorSorter.chunks) {
- if (chunk.originalType == FuzzOriginalType.Import ||
+ if (chunk.originalType == FuzzOriginalType.PartOf) {
+ print("Skipping part...");
+ continue uriLoop;
+ } else if (chunk.originalType == FuzzOriginalType.Part) {
+ // The part declaration should only be in the "main" file.
+ orgFileOnlyHeaderSb.writeln(chunk.getSource());
+ } else if (chunk.originalType == FuzzOriginalType.Import ||
chunk.originalType == FuzzOriginalType.Export ||
chunk.originalType == FuzzOriginalType.LibraryName ||
- chunk.originalType == FuzzOriginalType.Part ||
- chunk.originalType == FuzzOriginalType.PartOf ||
chunk.originalType == FuzzOriginalType.LanguageVersion) {
headerSb.writeln(chunk.getSource());
} else {
@@ -1625,14 +1633,12 @@
// exports, etc.
StringBuffer sb = new StringBuffer();
sb.writeln(headerSb.toString());
- for (int i = 0; i < totalSubFiles; i++) {
- if (i == currentSubFile) continue;
- sb.writeln("import '${getUriForChunk(i)}';");
- sb.writeln("export '${getUriForChunk(i)}';");
- }
+ sb.writeln("import '${uri.pathSegments.last}';");
sb.writeln(chunk.getSource());
fs.data[getUriForChunk(currentSubFile)] =
utf8.encode(sb.toString()) as Uint8List;
+ print(" => Split into ${getUriForChunk(currentSubFile)}:\n"
+ "${sb.toString()}\n-------------\n");
currentSubFile++;
}
@@ -1640,9 +1646,11 @@
StringBuffer sb = new StringBuffer();
sb.writeln(headerSb.toString());
for (int i = 0; i < totalSubFiles; i++) {
- sb.writeln("import '${getUriForChunk(i)}';");
- sb.writeln("export '${getUriForChunk(i)}';");
+ sb.writeln("import '${getUriForChunk(i).pathSegments.last}';");
+ sb.writeln("export '${getUriForChunk(i).pathSegments.last}';");
}
+ sb.writeln(orgFileOnlyHeaderSb.toString());
+ print(" => Main file becomes:\n${sb.toString()}\n-------------\n");
fs.data[uri] = utf8.encode(sb.toString()) as Uint8List;
}
@@ -1715,6 +1723,13 @@
enableExtensionMethods: true,
enableNonNullable: nnbd);
accept(ast);
+
+ if (metadataStart == null &&
+ ast.token.precedingComments != null &&
+ chunks.isEmpty) {
+ _chunkOutLanguageVersionComment(ast.token);
+ }
+
if (metadataStart != null) {
String metadata = asString.substring(
metadataStart!.charOffset, metadataEndInclusive!.charEnd);
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml
new file mode 100644
index 0000000..d953359
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml
@@ -0,0 +1,59 @@
+# 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.md file.
+
+# Change a part in a non-outline changing way, then a outline-changing way.
+
+type: newworld
+worlds:
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ sources:
+ main.dart: |
+ import 'lib.dart';
+ part 'part.dart';
+ main() {
+ print(baz());
+ }
+ part.dart: |
+ part of 'main.dart';
+ String foo() {
+ return "foo";
+ }
+ lib.dart: |
+ import 'main.dart';
+ String baz() {
+ return foo();
+ }
+ expectedLibraryCount: 2
+
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - part.dart
+ sources:
+ part.dart: |
+ part of 'main.dart';
+ String foo() {
+ return "foo!";
+ }
+ expectedLibraryCount: 2
+ expectsRebuildBodiesOnly: true
+
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ worldType: updated
+ errors: true # lib.dart calls foo, but foo no longer exists.
+ expectInitializeFromDill: false
+ invalidate:
+ - part.dart
+ sources:
+ part.dart: |
+ part of 'main.dart';
+ String bar() {
+ return "bar";
+ }
+ expectedLibraryCount: 2
+ expectsRebuildBodiesOnly: false # part changed its outline.
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect
new file mode 100644
index 0000000..047b356
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.1.expect
@@ -0,0 +1,21 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ import "org-dartlang-test:///main.dart";
+
+ static method baz() → dart.core::String {
+ return main::foo();
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///lib.dart";
+
+ part part.dart;
+ static method main() → dynamic {
+ dart.core::print(lib::baz());
+ }
+ static method /* from org-dartlang-test:///part.dart */ foo() → dart.core::String {
+ return "foo";
+ }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect
new file mode 100644
index 0000000..2a362a4
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.2.expect
@@ -0,0 +1,21 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ import "org-dartlang-test:///main.dart";
+
+ static method baz() → dart.core::String {
+ return main::foo();
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///lib.dart";
+
+ part part.dart;
+ static method main() → dynamic {
+ dart.core::print(lib::baz());
+ }
+ static method /* from org-dartlang-test:///part.dart */ foo() → dart.core::String {
+ return "foo!";
+ }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect
new file mode 100644
index 0000000..aa9d1f5
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_51.yaml.world.3.expect
@@ -0,0 +1,28 @@
+main = main::main;
+library from "org-dartlang-test:///lib.dart" as lib {
+//
+// Problems in library:
+//
+// org-dartlang-test:///lib.dart:3:10: Error: Method not found: 'foo'.
+// return foo();
+// ^^^
+//
+
+ import "org-dartlang-test:///main.dart";
+
+ static method baz() → dart.core::String {
+ return invalid-expression "org-dartlang-test:///lib.dart:3:10: Error: Method not found: 'foo'.\n return foo();\n ^^^";
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///lib.dart";
+
+ part part.dart;
+ static method main() → dynamic {
+ dart.core::print(lib::baz());
+ }
+ static method /* from org-dartlang-test:///part.dart */ bar() → dart.core::String {
+ return "bar";
+ }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml
new file mode 100644
index 0000000..2297147
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml
@@ -0,0 +1,48 @@
+# 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.md file.
+
+# Compile an application, change a file, but don't change the outline.
+# Use it as a mixin, though, so we cannot neccesarily rebuild the bodies only.
+# Have the thing being mixed in be in a part.
+
+type: newworld
+worlds:
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ sources:
+ main.dart: |
+ import 'lib.dart';
+ part 'part.dart';
+ part.dart: |
+ part of 'main.dart';
+ class Foo {
+ method() {
+ print("A");
+ }
+ }
+ lib.dart: |
+ import 'main.dart';
+
+ class Bar extends Object with Foo {
+ method2() {
+ method();
+ }
+ }
+ expectedLibraryCount: 2
+ - entry: main.dart
+ experiments: alternative-invalidation-strategy
+ worldType: updated
+ expectInitializeFromDill: false
+ invalidate:
+ - part.dart
+ sources:
+ part.dart: |
+ part of 'main.dart';
+ class Foo {
+ method() {
+ print("B");
+ }
+ }
+ expectedLibraryCount: 2
+ expectsRebuildBodiesOnly: false
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect
new file mode 100644
index 0000000..3d7bb8a
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.1.expect
@@ -0,0 +1,36 @@
+main = <No Member>;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ import "org-dartlang-test:///main.dart";
+
+ abstract class _Bar&Object&Foo extends dart.core::Object implements main::Foo /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •() → lib::_Bar&Object&Foo
+ : super dart.core::Object::•()
+ ;
+ method /* from org-dartlang-test:///part.dart */ method() → dynamic {
+ dart.core::print("A");
+ }
+ }
+ class Bar extends lib::_Bar&Object&Foo {
+ synthetic constructor •() → lib::Bar
+ : super lib::_Bar&Object&Foo::•()
+ ;
+ method method2() → dynamic {
+ this.{lib::_Bar&Object&Foo::method}(){() → dynamic};
+ }
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///lib.dart";
+
+ part part.dart;
+ class Foo extends dart.core::Object { // from org-dartlang-test:///part.dart
+ synthetic constructor •() → main::Foo
+ : super dart.core::Object::•()
+ ;
+ method method() → dynamic {
+ dart.core::print("A");
+ }
+ }
+}
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect
new file mode 100644
index 0000000..c9aba5f
--- /dev/null
+++ b/pkg/front_end/testcases/incremental/no_outline_change_52.yaml.world.2.expect
@@ -0,0 +1,36 @@
+main = <No Member>;
+library from "org-dartlang-test:///lib.dart" as lib {
+
+ import "org-dartlang-test:///main.dart";
+
+ abstract class _Bar&Object&Foo extends dart.core::Object implements main::Foo /*isAnonymousMixin,isEliminatedMixin,hasConstConstructor*/ {
+ const synthetic constructor •() → lib::_Bar&Object&Foo
+ : super dart.core::Object::•()
+ ;
+ method /* from org-dartlang-test:///part.dart */ method() → dynamic {
+ dart.core::print("B");
+ }
+ }
+ class Bar extends lib::_Bar&Object&Foo {
+ synthetic constructor •() → lib::Bar
+ : super lib::_Bar&Object&Foo::•()
+ ;
+ method method2() → dynamic {
+ this.{lib::_Bar&Object&Foo::method}(){() → dynamic};
+ }
+ }
+}
+library from "org-dartlang-test:///main.dart" as main {
+
+ import "org-dartlang-test:///lib.dart";
+
+ part part.dart;
+ class Foo extends dart.core::Object { // from org-dartlang-test:///part.dart
+ synthetic constructor •() → main::Foo
+ : super dart.core::Object::•()
+ ;
+ method method() → dynamic {
+ dart.core::print("B");
+ }
+ }
+}
diff --git a/pkg/front_end/testcases/strong.status b/pkg/front_end/testcases/strong.status
index cf5467d..762a17d 100644
--- a/pkg/front_end/testcases/strong.status
+++ b/pkg/front_end/testcases/strong.status
@@ -17,14 +17,13 @@
generic_metadata/typedef_generic_types_in_arguments_and_bounds: SemiFuzzCrash
# These tests have "privacy issues" and isn't compatiable with splitting files (fuzzing):
-constructor_tearoffs/lowering/typedef_from: semiFuzzFailureOnForceRebuildBodies # parts not supported
dart2js/mixin_default_values/main: semiFuzzFailureOnForceRebuildBodies # private method
dartdevc/factory_patch/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
dartdevc/private_covariant: semiFuzzFailureOnForceRebuildBodies # private method in class
late_lowering/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
late_lowering/issue41436/issue41436: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
late_lowering/issue41922: semiFuzzFailureOnForceRebuildBodies # private field
-late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # private class
macros/inject_constructor: semiFuzzFailureOnForceRebuildBodies # Macro injection --- think it might introduce a class in a file?
nnbd/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
nnbd/issue42504: semiFuzzFailureOnForceRebuildBodies # private class
diff --git a/pkg/front_end/testcases/weak.status b/pkg/front_end/testcases/weak.status
index def324c..6097d2c 100644
--- a/pkg/front_end/testcases/weak.status
+++ b/pkg/front_end/testcases/weak.status
@@ -28,7 +28,6 @@
static_field_lowering/opt_in: SemiFuzzFailure
# These tests have "privacy issues" and isn't compatiable with splitting files (fuzzing):
-constructor_tearoffs/lowering/typedef_from: semiFuzzFailureOnForceRebuildBodies # parts not supported
dart2js/mixin_default_values/main: semiFuzzFailureOnForceRebuildBodies # private method
dartdevc/factory_patch/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
dartdevc/private_covariant: semiFuzzFailureOnForceRebuildBodies # private method in class
@@ -47,20 +46,17 @@
general/issue47036: semiFuzzFailureOnForceRebuildBodies # private class
general/issue48548: semiFuzzFailureOnForceRebuildBodies # private field
general/nested_lib_spec/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
-general/part_as_entry_point: semiFuzzFailureOnForceRebuildBodies # parts not supported
-general/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+general/private_members: semiFuzzFailureOnForceRebuildBodies # private class (etc).
general/private_method_tearoff: semiFuzzFailureOnForceRebuildBodies # private method in class
general/redirecting_factory_const_inference: semiFuzzFailureOnForceRebuildBodies # private class
-general/redirecting_factory_invocation_metadata: semiFuzzFailureOnForceRebuildBodies # parts not supported
general/store_load: semiFuzzFailureOnForceRebuildBodies # private field in class
-general/top_level_accessors: semiFuzzFailureOnForceRebuildBodies # parts not supported
general/typedef: semiFuzzFailureOnForceRebuildBodies # private typedef
inference/downwards_inference_on_list_literals_infer_if_value_types_match_context: semiFuzzFailureOnForceRebuildBodies # private field
late_lowering/injected_late_field_checks/main: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
late_lowering/issue41436/issue41436: semiFuzzFailureOnForceRebuildBodies # needs custom libraries.json (and platform?) not setup here
late_lowering/issue41922: semiFuzzFailureOnForceRebuildBodies # private field
-late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # parts not supported
+late_lowering/private_members: semiFuzzFailureOnForceRebuildBodies # private class (etc)
macros/inject_constructor: semiFuzzFailureOnForceRebuildBodies # Macro injection --- think it might introduce a class in a file?
nnbd_mixed/issue46518: semiFuzzFailureOnForceRebuildBodies # private method
nnbd_mixed/no_null_shorting_explicit_extension: semiFuzzFailureOnForceRebuildBodies # private field in class
diff --git a/pkg/modular_test/lib/src/create_package_config.dart b/pkg/modular_test/lib/src/create_package_config.dart
index c25ef69..db463ad 100644
--- a/pkg/modular_test/lib/src/create_package_config.dart
+++ b/pkg/modular_test/lib/src/create_package_config.dart
@@ -60,7 +60,7 @@
}
String _packageConfigEntry(String name, Uri root,
- {Uri packageRoot, LanguageVersion version}) {
+ {Uri? packageRoot, LanguageVersion? version}) {
var fields = [
'"name": "${name}"',
'"rootUri": "$root"',
diff --git a/pkg/modular_test/lib/src/find_sdk_root.dart b/pkg/modular_test/lib/src/find_sdk_root.dart
index 2b36550..8988b83 100644
--- a/pkg/modular_test/lib/src/find_sdk_root.dart
+++ b/pkg/modular_test/lib/src/find_sdk_root.dart
@@ -15,9 +15,8 @@
var segments = current.pathSegments;
var index = segments.lastIndexOf('sdk');
if (index == -1) {
- print("error: cannot find the root of the Dart SDK");
exitCode = 1;
- return null;
+ throw "error: cannot find the root of the Dart SDK";
}
current = current.resolve("../" * (segments.length - index - 1));
if (await File.fromUri(current.resolve("sdk/DEPS")).exists()) {
diff --git a/pkg/modular_test/lib/src/generic_runner.dart b/pkg/modular_test/lib/src/generic_runner.dart
index e7c3030..fe2a85f 100644
--- a/pkg/modular_test/lib/src/generic_runner.dart
+++ b/pkg/modular_test/lib/src/generic_runner.dart
@@ -24,29 +24,29 @@
class RunnerOptions {
/// Name of the test suite being run.
- String suiteName;
+ final String suiteName;
/// Configuration name to use when writing result logs.
- String configurationName;
+ final String? configurationName;
/// Filter used to only run tests that match the filter name.
- String filter;
+ final String? filter;
/// Where log files are emitted.
///
/// Note that all shards currently emit the same filenames, so two shards
/// shouldn't be given the same [logDir] otherwise they will overwrite each
/// other's log files.
- Uri logDir;
+ final Uri? logDir;
/// Of [shards], which shard is currently being executed.
- int shard;
+ final int shard;
/// How many shards will be used to run a suite.
- int shards;
+ final int shards;
/// Whether to print verbose information.
- bool verbose;
+ final bool verbose;
/// Template used to help developers reproduce the issue.
///
@@ -54,23 +54,34 @@
/// * %executable is replaced with `Platform.executable`
/// * %script is replaced with the current `Platform.script`
/// * %name is replaced with the test name.
- String reproTemplate;
+ final String reproTemplate;
+
+ RunnerOptions(
+ {required this.suiteName,
+ this.configurationName,
+ this.filter,
+ this.logDir,
+ required this.shard,
+ required this.shards,
+ required this.verbose,
+ required this.reproTemplate});
}
class _TestOutcome {
/// Unique test name.
- String name;
+ final String name;
- /// Whether, after running the test, the test matches its expectations. Null
- /// before the test is executed.
- bool matchedExpectations;
+ /// Whether, after running the test, the test matches its expectations.
+ late bool matchedExpectations;
/// Additional output emitted by the test, only used when expectations don't
/// match and more details need to be provided.
- String output;
+ String? output;
/// Time used to run the test.
- Duration elapsedTime;
+ late Duration elapsedTime;
+
+ _TestOutcome(this.name);
}
Future<void> runSuite<T>(List<Test> tests, RunnerOptions options) async {
@@ -91,13 +102,13 @@
var test = sortedTests[i];
var name = test.name;
if (options.verbose) stdout.write('$name: ');
- if (options.filter != null && !name.contains(options.filter)) {
+ if (options.filter != null && !name.contains(options.filter!)) {
if (options.verbose) stdout.write('skipped\n');
continue;
}
var watch = new Stopwatch()..start();
- var outcome = new _TestOutcome()..name = test.name;
+ var outcome = new _TestOutcome(test.name);
try {
await test.run();
if (options.verbose) stdout.write('pass\n');
@@ -153,7 +164,7 @@
}
// Ensure the directory URI ends with a path separator.
- var logDir = Directory.fromUri(options.logDir).uri;
+ var logDir = Directory.fromUri(options.logDir!).uri;
var resultJsonUri = logDir.resolve('results.json');
var logsJsonUri = logDir.resolve('logs.json');
File.fromUri(resultJsonUri)
diff --git a/pkg/modular_test/lib/src/io_pipeline.dart b/pkg/modular_test/lib/src/io_pipeline.dart
index 21c57b6..5a1066e 100644
--- a/pkg/modular_test/lib/src/io_pipeline.dart
+++ b/pkg/modular_test/lib/src/io_pipeline.dart
@@ -41,17 +41,17 @@
/// libraries), and not modules that are test specific. File names will be
/// specific enough so that we can keep separate the artifacts created from
/// running tools under different configurations (with different flags).
- Uri _resultsFolderUri;
- Uri get resultFolderUriForTesting => _resultsFolderUri;
+ Uri? _resultsFolderUri;
+ Uri? get resultFolderUriForTesting => _resultsFolderUri;
/// A unique number to denote the current modular test configuration.
///
/// When using [cacheSharedModules], a test can resuse the output of a
/// previous run of this pipeline if that output was generated with the same
/// configuration.
- int _currentConfiguration;
+ int? _currentConfiguration;
- final ConfigurationRegistry _registry;
+ final ConfigurationRegistry? _registry;
/// Whether to keep alive the temporary folder used to store intermediate
/// results in order to inspect it later in test.
@@ -65,13 +65,13 @@
@override
Future<void> run(ModularTest test) async {
- var resultsDir = null;
+ Directory? resultsDir;
if (_resultsFolderUri == null) {
resultsDir = await Directory.systemTemp.createTemp('modular_test_res-');
_resultsFolderUri = resultsDir.uri;
}
if (cacheSharedModules) {
- _currentConfiguration = _registry.computeConfigurationId(test);
+ _currentConfiguration = _registry!.computeConfigurationId(test);
}
await super.run(test);
if (resultsDir != null &&
@@ -90,7 +90,7 @@
Future<void> cleanup() async {
if (_resultsFolderUri == null) return;
if (saveIntermediateResultsForTesting || cacheSharedModules) {
- await Directory.fromUri(_resultsFolderUri).delete(recursive: true);
+ await Directory.fromUri(_resultsFolderUri!).delete(recursive: true);
_resultsFolderUri = null;
}
}
@@ -98,11 +98,12 @@
@override
Future<void> runStep(IOModularStep step, Module module,
Map<Module, Set<DataId>> visibleData, List<String> flags) async {
+ final resultsFolderUri = _resultsFolderUri!;
if (cacheSharedModules && module.isShared) {
// If all expected outputs are already available, skip the step.
bool allCachedResultsFound = true;
for (var dataId in step.resultData) {
- var cachedFile = File.fromUri(_resultsFolderUri
+ var cachedFile = File.fromUri(resultsFolderUri
.resolve(_toFileName(module, dataId, configSpecific: true)));
if (!await cachedFile.exists()) {
allCachedResultsFound = false;
@@ -121,8 +122,8 @@
var stepFolder =
await Directory.systemTemp.createTemp('modular_test_${stepId}-');
for (var module in visibleData.keys) {
- for (var dataId in visibleData[module]) {
- var assetUri = _resultsFolderUri
+ for (var dataId in visibleData[module]!) {
+ var assetUri = resultsFolderUri
.resolve(_toFileName(module, dataId, configSpecific: true));
await File.fromUri(assetUri).copy(
stepFolder.uri.resolve(_toFileName(module, dataId)).toFilePath());
@@ -147,7 +148,7 @@
throw StateError(
"Step '${step.runtimeType}' didn't produce an output file");
}
- await outputFile.copy(_resultsFolderUri
+ await outputFile.copy(resultsFolderUri
.resolve(_toFileName(module, dataId, configSpecific: true))
.toFilePath());
}
diff --git a/pkg/modular_test/lib/src/loader.dart b/pkg/modular_test/lib/src/loader.dart
index b67f011..f846a41 100644
--- a/pkg/modular_test/lib/src/loader.dart
+++ b/pkg/modular_test/lib/src/loader.dart
@@ -32,8 +32,8 @@
Set<String> defaultPackages = defaultTestSpecification.packages.keys.toSet();
Module sdkModule = await _createSdkModule(root);
Map<String, Module> modules = {'sdk': sdkModule};
- String specString;
- Module mainModule;
+ String? specString;
+ Module? mainModule;
var entries = folder.listSync(recursive: false).toList()
// Sort to avoid dependency on file system order.
..sort(_compareFileSystemEntity);
@@ -54,7 +54,7 @@
"that is provided by default.");
}
if (modules.containsKey(moduleName)) {
- return _moduleConflict(fileName, modules[moduleName], testUri);
+ return _moduleConflict(fileName, modules[moduleName]!, testUri);
}
var relativeUri = Uri.parse(fileName);
var isMain = moduleName == 'main';
@@ -82,7 +82,7 @@
"that is provided by default.");
}
if (modules.containsKey(moduleName)) {
- return _moduleConflict(moduleName, modules[moduleName], testUri);
+ return _moduleConflict(moduleName, modules[moduleName]!, testUri);
}
var sources = await _listModuleSources(entryUri);
modules[moduleName] = Module(moduleName, [], testUri, sources,
@@ -133,7 +133,7 @@
void _attachDependencies(
Map<String, List<String>> dependencies, Map<String, Module> modules) {
dependencies.forEach((name, moduleDependencies) {
- var module = modules[name];
+ final module = modules[name];
if (module == null) {
_invalidTest(
"declared dependencies for a non existing module named '$name'");
@@ -142,7 +142,7 @@
_invalidTest("Module dependencies have already been declared on $name.");
}
moduleDependencies.forEach((dependencyName) {
- var moduleDependency = modules[dependencyName];
+ final moduleDependency = modules[dependencyName];
if (moduleDependency == null) {
_invalidTest("'$name' declares a dependency on a non existing module "
"named '$dependencyName'");
@@ -169,7 +169,7 @@
if (module != null) {
module.isPackage = true;
} else {
- var packageLibUri = configRoot.resolve(packages[packageName]);
+ var packageLibUri = configRoot.resolve(packages[packageName]!);
var packageRootUri = Directory.fromUri(packageLibUri).parent.uri;
var sources = await _listModuleSources(packageLibUri);
// TODO(sigmund): validate that we don't use a different alias for a
@@ -254,7 +254,7 @@
var entryType = isFile ? 'file' : 'folder';
var existingIsFile =
- existing.packageBase.path == './' && existing.sources.length == 1;
+ existing.packageBase!.path == './' && existing.sources.length == 1;
var existingEntryType = existingIsFile ? 'file' : 'folder';
var existingName = existingIsFile
@@ -266,7 +266,7 @@
"'$existingName'.");
}
-_invalidTest(String message) {
+Never _invalidTest(String message) {
throw new InvalidTestError(message);
}
diff --git a/pkg/modular_test/lib/src/memory_pipeline.dart b/pkg/modular_test/lib/src/memory_pipeline.dart
index d97586c..13025aa 100644
--- a/pkg/modular_test/lib/src/memory_pipeline.dart
+++ b/pkg/modular_test/lib/src/memory_pipeline.dart
@@ -9,8 +9,8 @@
import 'suite.dart';
/// A hook to fetch data previously computed for a dependency.
-typedef ModuleDataProvider = Object Function(Module, DataId);
-typedef SourceProvider = String Function(Uri);
+typedef ModuleDataProvider = Object? Function(Module, DataId);
+typedef SourceProvider = String? Function(Uri);
abstract class MemoryModularStep extends ModularStep {
Future<Map<DataId, Object>> execute(
@@ -25,12 +25,12 @@
/// Internal state to hold the current results as they are computed by the
/// pipeline. Expected to be null before and after the pipeline runs.
- Map<Module, Map<DataId, Object>> _results;
+ Map<Module, Map<DataId, Object>>? _results;
/// A copy of [_result] at the time the pipeline last finished running.
- Map<Module, Map<DataId, Object>> resultsForTesting;
+ Map<Module, Map<DataId, Object>>? resultsForTesting;
- final ConfigurationRegistry _registry;
+ final ConfigurationRegistry? _registry;
/// Cache of results when [cacheSharedModules] is true
final List<Map<Module, Map<DataId, Object>>> _resultCache;
@@ -38,29 +38,29 @@
MemoryPipeline(this._sources, List<MemoryModularStep> steps,
{bool cacheSharedModules: false})
: _registry = cacheSharedModules ? new ConfigurationRegistry() : null,
- _resultCache = cacheSharedModules ? [] : null,
+ _resultCache = cacheSharedModules ? [] : const [],
super(steps, cacheSharedModules);
@override
Future<void> run(ModularTest test) async {
- _results = {};
- Map<Module, Map<DataId, Object>> cache = null;
+ var results = _results = {};
+ Map<Module, Map<DataId, Object>>? cache = null;
if (cacheSharedModules) {
- int id = _registry.computeConfigurationId(test);
+ int id = _registry!.computeConfigurationId(test);
if (id < _resultCache.length) {
cache = _resultCache[id];
} else {
assert(id == _resultCache.length);
_resultCache.add(cache = {});
}
- _results.addAll(cache);
+ results.addAll(cache);
}
await super.run(test);
- resultsForTesting = _results;
+ resultsForTesting = results;
if (cacheSharedModules) {
- for (var module in _results.keys) {
+ for (var module in results.keys) {
if (module.isShared) {
- cache[module] = _results[module];
+ cache![module] = results[module]!;
}
}
}
@@ -70,10 +70,11 @@
@override
Future<void> runStep(MemoryModularStep step, Module module,
Map<Module, Set<DataId>> visibleData, List<String> flags) async {
+ final results = _results!;
if (cacheSharedModules && module.isShared) {
bool allCachedResultsFound = true;
for (var dataId in step.resultData) {
- if (_results[module] == null || _results[module][dataId] == null) {
+ if (results[module] == null || results[module]![dataId] == null) {
allCachedResultsFound = false;
break;
}
@@ -88,23 +89,23 @@
visibleData.forEach((module, dataIdSet) {
inputData[module] = {};
for (var dataId in dataIdSet) {
- inputData[module][dataId] = _results[module][dataId];
+ inputData[module]![dataId] = results[module]![dataId]!;
}
});
Map<Uri, String> inputSources = {};
if (step.needsSources) {
module.sources.forEach((relativeUri) {
var uri = module.rootUri.resolveUri(relativeUri);
- inputSources[uri] = _sources[uri];
+ inputSources[uri] = _sources[uri]!;
});
}
Map<DataId, Object> result = await step.execute(
module,
(Uri uri) => inputSources[uri],
- (Module m, DataId id) => inputData[m][id],
+ (Module m, DataId id) => inputData[m]![id],
flags);
for (var dataId in step.resultData) {
- (_results[module] ??= {})[dataId] = result[dataId];
+ (results[module] ??= {})[dataId] = result[dataId]!;
}
}
}
diff --git a/pkg/modular_test/lib/src/pipeline.dart b/pkg/modular_test/lib/src/pipeline.dart
index 7e10bac..16377b8 100644
--- a/pkg/modular_test/lib/src/pipeline.dart
+++ b/pkg/modular_test/lib/src/pipeline.dart
@@ -52,7 +52,7 @@
{this.needsSources: true,
this.dependencyDataNeeded: const [],
this.moduleDataNeeded: const [],
- this.resultData,
+ this.resultData: const [],
this.onlyOnMain: false,
this.onlyOnSdk: false,
this.notOnSdk: false});
@@ -94,13 +94,13 @@
// or by the same step on a dependency.
Map<DataId, S> previousKinds = {};
for (var step in steps) {
- if (step.resultData == null || step.resultData.isEmpty) {
+ if (step.resultData.isEmpty) {
_validationError(
"'${step.runtimeType}' needs to declare what data it produces.");
}
for (var resultKind in step.resultData) {
if (previousKinds.containsKey(resultKind) &&
- !areMutuallyExclusive(step, previousKinds[resultKind])) {
+ !areMutuallyExclusive(step, previousKinds[resultKind]!)) {
_validationError("Cannot produce the same data on two modular steps."
" '$resultKind' was previously produced by "
"'${previousKinds[resultKind].runtimeType}' but "
@@ -151,7 +151,7 @@
await _recursiveRun(
step, dependency, computedData, transitiveDependencies, flags);
deps.add(dependency);
- deps.addAll(transitiveDependencies[dependency]);
+ deps.addAll(transitiveDependencies[dependency]!);
}
if ((step.onlyOnMain && !module.isMain) ||
@@ -163,15 +163,15 @@
deps.forEach((dep) {
visibleData[dep] = {};
for (var dataId in step.dependencyDataNeeded) {
- if (computedData[dep].contains(dataId)) {
- visibleData[dep].add(dataId);
+ if (computedData[dep]!.contains(dataId)) {
+ visibleData[dep]!.add(dataId);
}
}
});
visibleData[module] = {};
for (var dataId in step.moduleDataNeeded) {
- if (computedData[module].contains(dataId)) {
- visibleData[module].add(dataId);
+ if (computedData[module]!.contains(dataId)) {
+ visibleData[module]!.add(dataId);
}
}
await runStep(step, module, visibleData, flags);
diff --git a/pkg/modular_test/lib/src/runner.dart b/pkg/modular_test/lib/src/runner.dart
index 13fb65a..916f002 100644
--- a/pkg/modular_test/lib/src/runner.dart
+++ b/pkg/modular_test/lib/src/runner.dart
@@ -26,15 +26,15 @@
await generic.runSuite(
entries,
- new generic.RunnerOptions()
- ..suiteName = suiteName
- ..configurationName = options.configurationName
- ..filter = options.filter
- ..logDir = options.outputDirectory
- ..shard = options.shard
- ..shards = options.shards
- ..verbose = options.verbose
- ..reproTemplate = '%executable %script --verbose --filter %name');
+ new generic.RunnerOptions(
+ suiteName: suiteName,
+ configurationName: options.configurationName,
+ filter: options.filter,
+ logDir: options.outputDirectory,
+ shard: options.shard,
+ shards: options.shards,
+ verbose: options.verbose,
+ reproTemplate: '%executable %script --verbose --filter %name'));
await pipeline.cleanup();
}
@@ -59,11 +59,11 @@
class Options {
bool showSkipped = false;
bool verbose = false;
- String filter = null;
+ String? filter;
int shards = 1;
int shard = 1;
- String configurationName;
- Uri outputDirectory;
+ String? configurationName;
+ Uri? outputDirectory;
bool useSdk = false;
static Options parse(List<String> args) {
@@ -92,7 +92,7 @@
help: 'configuration name to use for emitting jsonl result files.');
ArgResults argResults = parser.parse(args);
int shards = int.tryParse(argResults['shards']) ?? 1;
- int shard;
+ int shard = 1;
if (shards > 1) {
shard = int.tryParse(argResults['shard']) ?? 1;
if (shard <= 0 || shard >= shards) {
@@ -101,7 +101,7 @@
exit(1);
}
}
- Uri toUri(s) => s == null ? null : Uri.base.resolveUri(Uri.file(s));
+ Uri? toUri(s) => s == null ? null : Uri.base.resolveUri(Uri.file(s));
return Options()
..showSkipped = argResults['show-skipped']
..verbose = argResults['verbose']
diff --git a/pkg/modular_test/lib/src/suite.dart b/pkg/modular_test/lib/src/suite.dart
index 281c381..2821b97 100644
--- a/pkg/modular_test/lib/src/suite.dart
+++ b/pkg/modular_test/lib/src/suite.dart
@@ -17,15 +17,7 @@
final List<String> flags;
ModularTest(this.modules, this.mainModule, this.flags) {
- if (mainModule == null) {
- throw ArgumentError("main module was null");
- }
- if (flags == null) {
- throw ArgumentError("flags was null");
- }
- if (modules == null || modules.length == 0) {
- throw ArgumentError("modules cannot be null or empty");
- }
+ if (modules.isEmpty) throw ArgumentError("modules cannot be empty");
for (var module in modules) {
module._validate();
}
@@ -52,7 +44,7 @@
/// The file containing the main entry method, if any. Stored as a relative
/// [Uri] from [rootUri].
- final Uri mainSource;
+ final Uri? mainSource;
/// Whether this module is also available as a package import, where the
/// package name matches the module name.
@@ -63,7 +55,7 @@
/// When [isPackage], the base where all package URIs are resolved against.
/// Stored as a relative [Uri] from [rootUri].
- final Uri packageBase;
+ final Uri? packageBase;
/// Whether this is the main entry module of a test.
bool isMain;
diff --git a/pkg/modular_test/lib/src/test_specification_parser.dart b/pkg/modular_test/lib/src/test_specification_parser.dart
index 85272b0..2253df8 100644
--- a/pkg/modular_test/lib/src/test_specification_parser.dart
+++ b/pkg/modular_test/lib/src/test_specification_parser.dart
@@ -72,15 +72,15 @@
if (key is! String) {
_invalidSpecification("key: '$key' is not a string");
}
- normalizedMap[key] = [];
+ final values = normalizedMap[key] = [];
if (value is String) {
- normalizedMap[key].add(value);
+ values.add(value);
} else if (value is List) {
value.forEach((entry) {
if (entry is! String) {
_invalidSpecification("entry: '$entry' is not a string");
}
- normalizedMap[key].add(entry);
+ values.add(entry);
});
} else {
_invalidSpecification(
diff --git a/pkg/modular_test/pubspec.yaml b/pkg/modular_test/pubspec.yaml
index c1507d6..d6fcca8 100644
--- a/pkg/modular_test/pubspec.yaml
+++ b/pkg/modular_test/pubspec.yaml
@@ -6,7 +6,7 @@
This is used within the Dart SDK to define and validate modular tests, and to
execute them using the modular pipeline of different SDK tools.
environment:
- sdk: ">=2.10.0 <3.0.0"
+ sdk: '>=2.16.0 <3.0.0'
dependencies:
args: any
diff --git a/pkg/modular_test/test/io_pipeline_test.dart b/pkg/modular_test/test/io_pipeline_test.dart
index 8176201..46cbaf9 100644
--- a/pkg/modular_test/test/io_pipeline_test.dart
+++ b/pkg/modular_test/test/io_pipeline_test.dart
@@ -35,7 +35,7 @@
for (var uri in sources.keys) {
var file = new File.fromUri(uri);
await file.create(recursive: true);
- await file.writeAsStringSync(sources[uri]);
+ file.writeAsStringSync(sources[uri]!);
}
return new IOPipeline(steps,
saveIntermediateResultsForTesting: true,
@@ -44,49 +44,49 @@
@override
IOModularStep createSourceOnlyStep(
- {String Function(Map<Uri, String>) action,
- DataId resultId,
+ {required String Function(Map<Uri, String?>) action,
+ required DataId resultId,
bool requestSources: true}) =>
SourceOnlyStep(action, resultId, requestSources);
@override
IOModularStep createModuleDataStep(
- {String Function(String) action,
- DataId inputId,
- DataId resultId,
+ {required String Function(String) action,
+ required DataId inputId,
+ required DataId resultId,
bool requestModuleData: true}) =>
ModuleDataStep(action, inputId, resultId, requestModuleData);
@override
IOModularStep createLinkStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true}) =>
LinkStep(action, inputId, depId, resultId, requestDependenciesData);
@override
IOModularStep createMainOnlyStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true}) =>
MainOnlyStep(action, inputId, depId, resultId, requestDependenciesData);
@override
IOModularStep createTwoOutputStep(
- {String Function(String) action1,
- String Function(String) action2,
- DataId inputId,
- DataId result1Id,
- DataId result2Id}) =>
+ {required String Function(String) action1,
+ required String Function(String) action2,
+ required DataId inputId,
+ required DataId result1Id,
+ required DataId result2Id}) =>
TwoOutputStep(action1, action2, inputId, result1Id, result2Id);
@override
- String getResult(covariant IOPipeline pipeline, Module m, DataId dataId) {
- var folderUri = pipeline.resultFolderUriForTesting;
+ String? getResult(covariant IOPipeline pipeline, Module m, DataId dataId) {
+ var folderUri = pipeline.resultFolderUriForTesting!;
var file = File.fromUri(folderUri
.resolve(pipeline.configSpecificResultFileNameForTesting(m, dataId)));
return file.existsSync() ? file.readAsStringSync() : null;
@@ -100,7 +100,7 @@
}
class SourceOnlyStep implements IOModularStep {
- final String Function(Map<Uri, String>) action;
+ final String Function(Map<Uri, String?>) action;
final DataId resultId;
final bool needsSources;
List<DataId> get dependencyDataNeeded => const [];
@@ -115,12 +115,11 @@
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
- Map<Uri, String> sources = {};
+ Map<Uri, String?> sources = {};
for (var uri in module.sources) {
var file = File.fromUri(root.resolveUri(uri));
- String data = await file.exists() ? await file.readAsString() : null;
- sources[uri] = data;
+ sources[uri] = await file.exists() ? await file.readAsString() : null;
}
await File.fromUri(root.resolveUri(toUri(module, resultId)))
.writeAsString(action(sources));
@@ -199,7 +198,7 @@
final List<DataId> dependencyDataNeeded;
List<DataId> get moduleDataNeeded => [inputId];
List<DataId> get resultData => [resultId];
- final String Function(String, List<String>) action;
+ final String Function(String, List<String?>) action;
final DataId inputId;
final DataId depId;
final DataId resultId;
@@ -214,14 +213,14 @@
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
- List<String> depsData = [];
+ List<String?> depsData = [];
for (var dependency in module.dependencies) {
var depData = await _readHelper(dependency, root, depId, toUri);
depsData.add(depData);
}
var inputData = await _readHelper(module, root, inputId, toUri);
await File.fromUri(root.resolveUri(toUri(module, resultId)))
- .writeAsString(action(inputData, depsData));
+ .writeAsString(action(inputData!, depsData));
}
@override
@@ -233,7 +232,7 @@
final List<DataId> dependencyDataNeeded;
List<DataId> get moduleDataNeeded => [inputId];
List<DataId> get resultData => [resultId];
- final String Function(String, List<String>) action;
+ final String Function(String, List<String?>) action;
final DataId inputId;
final DataId depId;
final DataId resultId;
@@ -248,21 +247,21 @@
@override
Future<void> execute(Module module, Uri root, ModuleDataToRelativeUri toUri,
List<String> flags) async {
- List<String> depsData = [];
+ List<String?> depsData = [];
for (var dependency in computeTransitiveDependencies(module)) {
var depData = await _readHelper(dependency, root, depId, toUri);
depsData.add(depData);
}
var inputData = await _readHelper(module, root, inputId, toUri);
await File.fromUri(root.resolveUri(toUri(module, resultId)))
- .writeAsString(action(inputData, depsData));
+ .writeAsString(action(inputData!, depsData));
}
@override
void notifyCached(Module module) {}
}
-Future<String> _readHelper(Module module, Uri root, DataId dataId,
+Future<String?> _readHelper(Module module, Uri root, DataId dataId,
ModuleDataToRelativeUri toUri) async {
var file = File.fromUri(root.resolveUri(toUri(module, dataId)));
if (await file.exists()) {
diff --git a/pkg/modular_test/test/loader/loader_test.dart b/pkg/modular_test/test/loader/loader_test.dart
index 39b3081..8fadfef 100644
--- a/pkg/modular_test/test/loader/loader_test.dart
+++ b/pkg/modular_test/test/loader/loader_test.dart
@@ -19,7 +19,7 @@
if (entry is Directory) {
var dirName = entry.uri.path.substring(baseDir.path.length);
test(dirName, () => _runTest(entry.uri, dirName, options),
- skip: options.filter != null && !dirName.contains(options.filter));
+ skip: options.filter != null && !dirName.contains(options.filter!));
}
}
}
@@ -46,7 +46,7 @@
" ${Platform.executable} ${Platform.script} "
"--update --show-update --filter $dirName");
}
- expect(expectation, result);
+ expect(result, expectation);
} else if (await file.exists() && (await file.readAsString() == result)) {
print(" expectation matches result and was up to date.");
} else {
@@ -100,7 +100,7 @@
class _Options {
bool updateExpectations = false;
bool showResultOnUpdate = false;
- String filter = null;
+ String? filter;
static _Options parse(List<String> args) {
var parser = new ArgParser()
diff --git a/pkg/modular_test/test/memory_pipeline_test.dart b/pkg/modular_test/test/memory_pipeline_test.dart
index 95ba01d..894ee21 100644
--- a/pkg/modular_test/test/memory_pipeline_test.dart
+++ b/pkg/modular_test/test/memory_pipeline_test.dart
@@ -30,56 +30,57 @@
@override
MemoryModularStep createSourceOnlyStep(
- {String Function(Map<Uri, String>) action,
- DataId resultId,
+ {required String Function(Map<Uri, String?>) action,
+ required DataId resultId,
bool requestSources: true}) =>
SourceOnlyStep(action, resultId, requestSources);
@override
MemoryModularStep createModuleDataStep(
- {String Function(String) action,
- DataId inputId,
- DataId resultId,
+ {required String Function(String) action,
+ required DataId inputId,
+ required DataId resultId,
bool requestModuleData: true}) =>
ModuleDataStep(action, inputId, resultId, requestModuleData);
@override
MemoryModularStep createLinkStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true}) =>
LinkStep(action, inputId, depId, resultId, requestDependenciesData);
@override
MemoryModularStep createMainOnlyStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true}) =>
MainOnlyStep(action, inputId, depId, resultId, requestDependenciesData);
@override
MemoryModularStep createTwoOutputStep(
- {String Function(String) action1,
- String Function(String) action2,
- DataId inputId,
- DataId result1Id,
- DataId result2Id}) =>
+ {required String Function(String) action1,
+ required String Function(String) action2,
+ required DataId inputId,
+ required DataId result1Id,
+ required DataId result2Id}) =>
TwoOutputStep(action1, action2, inputId, result1Id, result2Id);
@override
- String getResult(covariant MemoryPipeline pipeline, Module m, DataId dataId) {
- return pipeline.resultsForTesting[m][dataId];
+ String? getResult(
+ covariant MemoryPipeline pipeline, Module m, DataId dataId) {
+ return pipeline.resultsForTesting![m]![dataId] as String?;
}
FutureOr<void> cleanup(Pipeline<MemoryModularStep> pipeline) => null;
}
class SourceOnlyStep implements MemoryModularStep {
- final String Function(Map<Uri, String>) action;
+ final String Function(Map<Uri, String?>) action;
final DataId resultId;
final bool needsSources;
List<DataId> get dependencyDataNeeded => const [];
@@ -96,7 +97,7 @@
SourceProvider sourceProvider,
ModuleDataProvider dataProvider,
List<String> flags) {
- Map<Uri, String> sources = {};
+ Map<Uri, String?> sources = {};
for (var uri in module.sources) {
sources[uri] = sourceProvider(module.rootUri.resolveUri(uri));
}
@@ -127,9 +128,10 @@
SourceProvider sourceProvider,
ModuleDataProvider dataProvider,
List<String> flags) {
- var inputData = dataProvider(module, inputId) as String;
- if (inputData == null)
+ var inputData = dataProvider(module, inputId) as String?;
+ if (inputData == null) {
return Future.value({resultId: "data for $module was null"});
+ }
return Future.value({resultId: action(inputData)});
}
@@ -159,12 +161,13 @@
SourceProvider sourceProvider,
ModuleDataProvider dataProvider,
List<String> flags) {
- var inputData = dataProvider(module, inputId) as String;
- if (inputData == null)
+ var inputData = dataProvider(module, inputId) as String?;
+ if (inputData == null) {
return Future.value({
- result1Id: "data for $module was null",
- result2Id: "data for $module was null",
+ result1Id: "result for $module was null",
+ result2Id: "result for $module was null",
});
+ }
return Future.value(
{result1Id: action1(inputData), result2Id: action2(inputData)});
}
@@ -177,7 +180,7 @@
bool get needsSources => false;
final List<DataId> dependencyDataNeeded;
List<DataId> get moduleDataNeeded => [inputId];
- final String Function(String, List<String>) action;
+ final String Function(String, List<String?>) action;
final DataId inputId;
final DataId depId;
final DataId resultId;
@@ -196,9 +199,9 @@
ModuleDataProvider dataProvider,
List<String> flags) {
List<String> depsData = module.dependencies
- .map((d) => dataProvider(d, depId) as String)
+ .map((d) => dataProvider(d, depId).toString())
.toList();
- var inputData = dataProvider(module, inputId) as String;
+ var inputData = dataProvider(module, inputId).toString();
return Future.value({resultId: action(inputData, depsData)});
}
@@ -210,7 +213,7 @@
bool get needsSources => false;
final List<DataId> dependencyDataNeeded;
List<DataId> get moduleDataNeeded => [inputId];
- final String Function(String, List<String>) action;
+ final String Function(String, List<String?>) action;
final DataId inputId;
final DataId depId;
final DataId resultId;
@@ -228,11 +231,11 @@
SourceProvider sourceProvider,
ModuleDataProvider dataProvider,
List<String> flags) {
- List<String> depsData = computeTransitiveDependencies(module)
- .map((d) => dataProvider(d, depId) as String)
+ List<String?> depsData = computeTransitiveDependencies(module)
+ .map((d) => dataProvider(d, depId) as String?)
.toList();
- var inputData = dataProvider(module, inputId) as String;
- return Future.value({resultId: action(inputData, depsData)});
+ var inputData = dataProvider(module, inputId) as String?;
+ return Future.value({resultId: action(inputData!, depsData)});
}
@override
diff --git a/pkg/modular_test/test/pipeline_common.dart b/pkg/modular_test/test/pipeline_common.dart
index 254db8c..688d1e4 100644
--- a/pkg/modular_test/test/pipeline_common.dart
+++ b/pkg/modular_test/test/pipeline_common.dart
@@ -34,16 +34,16 @@
/// Create a step that applies [action] on all input files of the module, and
/// emits a result with the given [id]
S createSourceOnlyStep(
- {String Function(Map<Uri, String>) action,
- DataId resultId,
+ {required String Function(Map<Uri, String?>) action,
+ required DataId resultId,
bool requestSources: true});
/// Create a step that applies [action] on the module [inputId] data, and
/// emits a result with the given [resultId].
S createModuleDataStep(
- {String Function(String) action,
- DataId inputId,
- DataId resultId,
+ {required String Function(String) action,
+ required DataId inputId,
+ required DataId resultId,
bool requestModuleData: true});
/// Create a step that applies [action] on the module [inputId] data and the
@@ -52,10 +52,10 @@
///
/// [depId] may be the same as [resultId] or [inputId].
S createLinkStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true});
/// Create a step that applies [action] only on the main module [inputId] data
@@ -65,23 +65,23 @@
/// [depId] may be the same as [inputId] but not [resultId] since this action
/// is only applied on the main module.
S createMainOnlyStep(
- {String Function(String, List<String>) action,
- DataId inputId,
- DataId depId,
- DataId resultId,
+ {required String Function(String, List<String?>) action,
+ required DataId inputId,
+ required DataId depId,
+ required DataId resultId,
bool requestDependenciesData: true});
/// Create a step that applies [action1] and [action2] on the module [inputId]
/// data, and emits two results with the given [result1Id] and [result2Id].
S createTwoOutputStep(
- {String Function(String) action1,
- String Function(String) action2,
- DataId inputId,
- DataId result1Id,
- DataId result2Id});
+ {required String Function(String) action1,
+ required String Function(String) action2,
+ required DataId inputId,
+ required DataId result1Id,
+ required DataId result2Id});
/// Return the result data produced by a modular step.
- String getResult(Pipeline<S> pipeline, Module m, DataId dataId);
+ String? getResult(Pipeline<S> pipeline, Module m, DataId dataId);
/// Do any cleanup work needed after pipeline is completed. Needed because
/// some implementations retain data around to be able to answer [getResult]
@@ -322,7 +322,7 @@
var counterStep = testStrategy.createSourceOnlyStep(
action: (_) => '${i++}', resultId: counterId);
var linkStep = testStrategy.createLinkStep(
- action: (String m, List<String> deps) => "${deps.join(',')},$m",
+ action: (String m, List<String?> deps) => "${deps.join(',')},$m",
inputId: counterId,
depId: counterId,
resultId: linkId,
@@ -352,7 +352,7 @@
var counterStep = testStrategy.createSourceOnlyStep(
action: (_) => '${i++}', resultId: counterId);
var linkStep = testStrategy.createLinkStep(
- action: (String m, List<String> deps) => "${deps.join(',')},$m",
+ action: (String m, List<String?> deps) => "${deps.join(',')},$m",
inputId: counterId,
depId: counterId,
resultId: linkId,
@@ -384,7 +384,7 @@
var counterStep = testStrategy.createSourceOnlyStep(
action: (_) => '${i++}', resultId: counterId);
var linkStep = testStrategy.createLinkStep(
- action: (String m, List<String> deps) => "${deps.join(',')},$m",
+ action: (String m, List<String?> deps) => "${deps.join(',')},$m",
inputId: counterId,
depId: counterId,
resultId: linkId,
@@ -419,7 +419,7 @@
DataId _uppercaseId = const DataId("uppercase");
DataId _joinId = const DataId("join");
-String _concat(Map<Uri, String> sources) {
+String _concat(Map<Uri, String?> sources) {
var buffer = new StringBuffer();
sources.forEach((uri, contents) {
buffer.write("$uri: $contents\n");
@@ -430,7 +430,7 @@
String _lowercase(String contents) => contents.toLowerCase();
String _uppercase(String contents) => contents.toUpperCase();
-String _replaceAndJoin(String moduleData, List<String> depContents) {
+String _replaceAndJoin(String moduleData, List<String?> depContents) {
var buffer = new StringBuffer();
depContents.forEach(buffer.writeln);
buffer.write(moduleData.replaceAll(".dart:", ""));
diff --git a/pkg/modular_test/test/validate_pipeline_test.dart b/pkg/modular_test/test/validate_pipeline_test.dart
index a0b5759..431d91a 100644
--- a/pkg/modular_test/test/validate_pipeline_test.dart
+++ b/pkg/modular_test/test/validate_pipeline_test.dart
@@ -83,5 +83,5 @@
@override
Future<void> runStep(ModularStep step, Module module,
Map<Module, Set<DataId>> visibleData, List<String> flags) =>
- null;
+ Future.value(null);
}
diff --git a/pkg/modular_test/test/validate_suite_test.dart b/pkg/modular_test/test/validate_suite_test.dart
index f335567..5093444 100644
--- a/pkg/modular_test/test/validate_suite_test.dart
+++ b/pkg/modular_test/test/validate_suite_test.dart
@@ -8,19 +8,10 @@
main() {
test('module test is not empty', () {
- expect(
- () => ModularTest([], null, []), throwsA(TypeMatcher<ArgumentError>()));
-
var m = Module("a", [], Uri.parse("app:/"), []);
expect(() => ModularTest([], m, []), throwsA(TypeMatcher<ArgumentError>()));
});
- test('module test must have a main module', () {
- var m = Module("a", [], Uri.parse("app:/"), []);
- expect(() => ModularTest([m], null, []),
- throwsA(TypeMatcher<ArgumentError>()));
- });
-
test('package must depend on package', () {
var m1a = Module("a", const [], Uri.parse("app:/"),
[Uri.parse("a1.dart"), Uri.parse("a2.dart")],
diff --git a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
index 9686fae..0b1972c 100644
--- a/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
+++ b/runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc
@@ -1301,7 +1301,7 @@
Regress216834909_hang_at_exit = false;
}
}
-#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) || \
+#endif // defined(DART_HOST_OS_LINUX) || defined(DART_HOST_OS_ANDROID) ||
// defined(DART_HOST_OS_MACOS)
} // namespace dart
diff --git a/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart b/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
index 7a0f2e6..74ecd60 100644
--- a/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
+++ b/runtime/tests/vm/dart_2/isolates/reload_many_isolates_live_and_die_test.dart
@@ -10,7 +10,7 @@
import 'reload_utils.dart';
-const N = 20;
+final N = runningInSimulator ? 2 : 20;
main() async {
if (!currentVmSupportsReload) return;
diff --git a/runtime/tests/vm/dart_2/isolates/reload_utils.dart b/runtime/tests/vm/dart_2/isolates/reload_utils.dart
index c1c9e96..f41c52f 100644
--- a/runtime/tests/vm/dart_2/isolates/reload_utils.dart
+++ b/runtime/tests/vm/dart_2/isolates/reload_utils.dart
@@ -20,6 +20,11 @@
!executable.contains('dart_precompiled_runtime');
}
+bool get runningInSimulator {
+ final executable = Platform.executable;
+ return executable.contains('SIM');
+}
+
final includeIn = RegExp(r'//\s+@include-in-reload-([0-9]+)([+]?)\s*$');
Future<List<String>> generateDills(String tempDir, String testDartFile) async {
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index e9b73af..7c963b6 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -37,6 +37,7 @@
dart_2/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
dart_2/isolates/concurrency_stress_sanity_test: Pass, Slow # Spawns subprocesses
dart_2/isolates/fast_object_copy_test: Pass, Slow # Slow due to doing a lot of transitive object copies.
+dart_2/isolates/reload_many_isolates_live_and_die_test: Pass, Slow # Launches subprocesses, makes 2 kernel compilations and use reload.
dart_2/minimal_kernel_test: Pass, Slow # Spawns several subprocesses
dart_2/null_safety_autodetection_in_kernel_compiler_test: Pass, Slow # Spawns several subprocesses
dart_2/print_object_layout_test: Pass, Slow # Spawns several subprocesses
@@ -280,6 +281,7 @@
dart/data_uri_spawn_test: Skip # Timeout
dart/kernel_determinism_test: SkipSlow
dart_2/data_uri_spawn_test: Skip # Timeout
+dart_2/isolates/reload_many_isolates_live_and_die_test: SkipSlow # The test itself does reloading of subprocesses.
dart_2/kernel_determinism_test: SkipSlow
[ $compiler != dartk && $compiler != none ]
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.cc b/runtime/vm/compiler/assembler/assembler_arm64.cc
index e2b39f7..8f0e2ee 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm64.cc
@@ -1673,15 +1673,13 @@
}
}
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
if (!FLAG_precompiled_mode) {
- if (restore_pp == kRestoreCallerPP) {
- // Restore and untag PP.
- LoadFromOffset(
- PP, FP,
- target::frame_layout.saved_caller_pp_from_fp * target::kWordSize);
- sub(PP, PP, Operand(kHeapObjectTag));
- }
+ // Restore and untag PP.
+ LoadFromOffset(
+ PP, FP,
+ target::frame_layout.saved_caller_pp_from_fp * target::kWordSize);
+ sub(PP, PP, Operand(kHeapObjectTag));
}
set_constant_pool_allowed(false);
LeaveFrame();
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index 33fa8bb..4690df6e 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -2100,7 +2100,7 @@
void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
void EnterOsrFrame(intptr_t extra_size, Register new_pp = kNoRegister);
- void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+ void LeaveDartFrame();
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
diff --git a/runtime/vm/compiler/assembler/assembler_base.h b/runtime/vm/compiler/assembler/assembler_base.h
index 7faf6fd..f30cfc9 100644
--- a/runtime/vm/compiler/assembler/assembler_base.h
+++ b/runtime/vm/compiler/assembler/assembler_base.h
@@ -591,8 +591,6 @@
friend class AssemblerFixup;
};
-enum RestorePP { kRestoreCallerPP, kKeepCalleePP };
-
class AssemblerBase : public StackResource {
public:
explicit AssemblerBase(ObjectPoolBuilder* object_pool_builder)
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.cc b/runtime/vm/compiler/assembler/assembler_riscv.cc
index 335f0ea..fd70709 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.cc
+++ b/runtime/vm/compiler/assembler/assembler_riscv.cc
@@ -3691,15 +3691,13 @@
}
}
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
// N.B. The ordering here is important. We must never read beyond SP or
// it may have already been clobbered by a signal handler.
if (!FLAG_precompiled_mode) {
- if (restore_pp == kRestoreCallerPP) {
- lx(PP, Address(FP, target::frame_layout.saved_caller_pp_from_fp *
- target::kWordSize));
- subi(PP, PP, kHeapObjectTag);
- }
+ lx(PP, Address(FP, target::frame_layout.saved_caller_pp_from_fp *
+ target::kWordSize));
+ subi(PP, PP, kHeapObjectTag);
}
set_constant_pool_allowed(false);
subi(SP, FP, 2 * target::kWordSize);
diff --git a/runtime/vm/compiler/assembler/assembler_riscv.h b/runtime/vm/compiler/assembler/assembler_riscv.h
index d24ea42..44424ae 100644
--- a/runtime/vm/compiler/assembler/assembler_riscv.h
+++ b/runtime/vm/compiler/assembler/assembler_riscv.h
@@ -1232,7 +1232,7 @@
void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
void EnterOsrFrame(intptr_t extra_size, Register new_pp = kNoRegister);
- void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+ void LeaveDartFrame();
// For non-leaf runtime calls. For leaf runtime calls, use LeafRuntimeScope,
void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
diff --git a/runtime/vm/compiler/assembler/assembler_x64.cc b/runtime/vm/compiler/assembler/assembler_x64.cc
index bfc1a69..9a65cda 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.cc
+++ b/runtime/vm/compiler/assembler/assembler_x64.cc
@@ -2017,13 +2017,11 @@
}
}
-void Assembler::LeaveDartFrame(RestorePP restore_pp) {
+void Assembler::LeaveDartFrame() {
// Restore caller's PP register that was pushed in EnterDartFrame.
if (!FLAG_precompiled_mode) {
- if (restore_pp == kRestoreCallerPP) {
- movq(PP, Address(RBP, (target::frame_layout.saved_caller_pp_from_fp *
- target::kWordSize)));
- }
+ movq(PP, Address(RBP, (target::frame_layout.saved_caller_pp_from_fp *
+ target::kWordSize)));
}
set_constant_pool_allowed(false);
LeaveFrame();
diff --git a/runtime/vm/compiler/assembler/assembler_x64.h b/runtime/vm/compiler/assembler/assembler_x64.h
index 653e97f..395a372 100644
--- a/runtime/vm/compiler/assembler/assembler_x64.h
+++ b/runtime/vm/compiler/assembler/assembler_x64.h
@@ -1142,7 +1142,7 @@
// pushq r15
// .....
void EnterDartFrame(intptr_t frame_size, Register new_pp = kNoRegister);
- void LeaveDartFrame(RestorePP restore_pp = kRestoreCallerPP);
+ void LeaveDartFrame();
// Set up a Dart frame for a function compiled for on-stack replacement.
// The frame layout is a normal Dart frame, but the frame is partially set
diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc
index 88f0e9a..d1a9fe0 100644
--- a/runtime/vm/compiler/backend/il_arm64.cc
+++ b/runtime/vm/compiler/backend/il_arm64.cc
@@ -1360,8 +1360,7 @@
// Restore the pre-aligned SP.
__ mov(SPREG, saved_fp_or_sp);
} else {
- // Although PP is a callee-saved register, it may have been moved by the GC.
- __ LeaveDartFrame(compiler::kRestoreCallerPP);
+ __ LeaveDartFrame();
// Restore the global object pool after returning from runtime (old space is
// moving, so the GOP could have been relocated).
diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc
index ae2531a..b30b025 100644
--- a/runtime/vm/compiler/backend/il_x64.cc
+++ b/runtime/vm/compiler/backend/il_x64.cc
@@ -1301,8 +1301,7 @@
// Restore the pre-aligned SP.
__ movq(SPREG, saved_sp);
} else {
- // Although PP is a callee-saved register, it may have been moved by the GC.
- __ LeaveDartFrame(compiler::kRestoreCallerPP);
+ __ LeaveDartFrame();
// Restore the global object pool after returning from runtime (old space is
// moving, so the GOP could have been relocated).
if (FLAG_precompiled_mode) {
diff --git a/runtime/vm/compiler/ffi/call.cc b/runtime/vm/compiler/ffi/call.cc
index d4cb983..be76d75 100644
--- a/runtime/vm/compiler/ffi/call.cc
+++ b/runtime/vm/compiler/ffi/call.cc
@@ -16,10 +16,13 @@
// TODO(dartbug.com/36607): Cache the trampolines.
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
const FunctionType& c_signature,
- bool is_leaf) {
+ bool is_leaf,
+ const String& function_name) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
- String& name = String::Handle(zone, Symbols::New(thread, "FfiTrampoline"));
+ String& name =
+ String::Handle(zone, Symbols::NewFormatted(thread, "FfiTrampoline_%s",
+ function_name.ToCString()));
const Library& lib = Library::Handle(zone, Library::FfiLibrary());
const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
FunctionType& signature = FunctionType::Handle(zone, FunctionType::New());
diff --git a/runtime/vm/compiler/ffi/call.h b/runtime/vm/compiler/ffi/call.h
index 06bccde..aecd128 100644
--- a/runtime/vm/compiler/ffi/call.h
+++ b/runtime/vm/compiler/ffi/call.h
@@ -21,7 +21,8 @@
FunctionPtr TrampolineFunction(const FunctionType& dart_signature,
const FunctionType& c_signature,
- bool is_leaf);
+ bool is_leaf,
+ const String& function_name);
} // namespace ffi
diff --git a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
index f510129..e8fe9a6 100644
--- a/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
+++ b/runtime/vm/compiler/frontend/base_flow_graph_builder.cc
@@ -1028,8 +1028,10 @@
Report::LongJump(language_error);
}
+ const auto& name =
+ String::Handle(parsed_function_->function().UserVisibleName());
const Function& target = Function::ZoneHandle(
- compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf));
+ compiler::ffi::TrampolineFunction(dart_type, native_type, is_leaf, name));
Fragment code;
// Store the pointer in the context, we cannot load the untagged address
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index fc87889..9162e8b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -15811,7 +15811,7 @@
ASSERT(array.At(sentinel_start + entry_length - 1) ==
smi_illegal_cid().ptr());
ASSERT(ICData::CachedEmptyICDataArray(num_args, is_tracking_exactness()) ==
- entries());
+ array.ptr());
} else {
ASSERT(array.At(sentinel_start + entry_length - 1) == ptr());
}
diff --git a/sdk/lib/_internal/js_runtime/lib/late_helper.dart b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
index ceabcd7..1643555 100644
--- a/sdk/lib/_internal/js_runtime/lib/late_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/late_helper.dart
@@ -6,8 +6,20 @@
import 'dart:_internal' show LateError, createSentinel, isSentinel;
+@pragma('dart2js:tryInline')
+void throwLateFieldNI(String fieldName) => throw LateError.fieldNI(fieldName);
+@pragma('dart2js:tryInline')
+void throwLateFieldAI(String fieldName) => throw LateError.fieldAI(fieldName);
+@pragma('dart2js:tryInline')
void throwLateFieldADI(String fieldName) => throw LateError.fieldADI(fieldName);
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldNI() => throw LateError.fieldNI('');
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldAI() => throw LateError.fieldAI('');
+@pragma('dart2js:tryInline')
+void throwUnnamedLateFieldADI() => throw LateError.fieldADI('');
+
/// A boxed variable used for lowering uninitialized `late` variables when they
/// are locals or statics.
class _Cell {
@@ -113,19 +125,8 @@
// Helpers for lowering late instance fields:
// TODO(fishythefish): Support specialization of sentinels based on type.
-@pragma('dart2js:noInline')
-@pragma('dart2js:as:trust')
-T _lateReadCheck<T>(Object? value, String name) {
- if (isSentinel(value)) throw LateError.fieldNI(name);
- return value as T;
-}
+external T _lateReadCheck<T>(Object? value, String name);
-@pragma('dart2js:noInline')
-void _lateWriteOnceCheck(Object? value, String name) {
- if (!isSentinel(value)) throw LateError.fieldAI(name);
-}
+external void _lateWriteOnceCheck(Object? value, String name);
-@pragma('dart2js:noInline')
-void _lateInitializeOnceCheck(Object? value, String name) {
- if (!isSentinel(value)) throw LateError.fieldADI(name);
-}
+external void _lateInitializeOnceCheck(Object? value, String name);
diff --git a/tools/VERSION b/tools/VERSION
index 5c4a258..bbee9a8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 44
+PRERELEASE 45
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/git_cl_try.sh b/tools/git_cl_try.sh
index 12f56b9..4b76be9 100644
--- a/tools/git_cl_try.sh
+++ b/tools/git_cl_try.sh
@@ -42,19 +42,22 @@
echo "git-cl-try-vm-ffi"
git cl try -B dart/try \
-b vm-ffi-android-debug-arm-try \
- -b vm-ffi-android-debug-arm64-try \
+ -b vm-ffi-android-debug-arm64c-try \
-b vm-ffi-android-product-arm-try \
- -b vm-ffi-android-product-arm64-try \
+ -b vm-ffi-android-product-arm64c-try \
-b vm-ffi-android-release-arm-try \
- -b vm-ffi-android-release-arm64-try
+ -b vm-ffi-android-release-arm64c-try \
}
function git-cl-try-vm-precomp {
echo "git-cl-try-vm-precomp"
git cl try -B dart/try \
- -b vm-kernel-precomp-android-release-arm64-try \
+ -b vm-kernel-precomp-android-release-arm64c-try \
-b vm-kernel-precomp-android-release-arm_x64-try \
+ -b vm-kernel-precomp-dwarf-linux-product-x64-try \
-b vm-kernel-precomp-linux-debug-simarm_x64-try \
+ -b vm-kernel-precomp-linux-debug-simriscv64-try \
-b vm-kernel-precomp-linux-debug-x64-try \
+ -b vm-kernel-precomp-linux-debug-x64c-try \
-b vm-kernel-precomp-linux-product-x64-try \
-b vm-kernel-precomp-linux-release-simarm-try \
-b vm-kernel-precomp-linux-release-simarm64-try \
@@ -74,6 +77,7 @@
-b vm-kernel-precomp-asan-linux-release-x64-try \
-b vm-kernel-precomp-msan-linux-release-x64-try \
-b vm-kernel-precomp-tsan-linux-release-x64-try \
+ -b vm-kernel-ubsan-linux-release-x64-try
}
function git-cl-try-vm-all {
echo "git-cl-try-vm-all"
diff --git a/tools/package_deps/bin/package_deps.dart b/tools/package_deps/bin/package_deps.dart
index 43f2bc1..ce47f51 100644
--- a/tools/package_deps/bin/package_deps.dart
+++ b/tools/package_deps/bin/package_deps.dart
@@ -233,9 +233,9 @@
var extraDevDeclarations = Set<String>.from(_declaredDevDependencies)
..removeAll(devdeps);
- // Remove package:pedantic and package:lints as they are often declared as
- // dev dependencies in order to bring in their analysis_options.yaml files.
- extraDevDeclarations.removeAll(['lints', 'pedantic']);
+ // Remove package:lints - it's often declared as a dev dependency in order
+ // to bring in analysis_options configuration files.
+ extraDevDeclarations.removeAll(['lints']);
if (extraDevDeclarations.isNotEmpty) {
out(' ${_printSet(extraDevDeclarations)} declared in '
"'dev_dependencies:' but not used in dev dirs.");