Version 2.12.0-38.0.dev
Merge commit '54a91f26c47d8bf860ebd78bb8d836b1442bdb21' into 'dev'
diff --git a/DEPS b/DEPS
index 73717bf..cd20336 100644
--- a/DEPS
+++ b/DEPS
@@ -146,7 +146,7 @@
"source_maps-0.9.4_rev": "38524",
"source_maps_rev": "53eb92ccfe6e64924054f83038a534b959b12b3e",
"source_span_rev": "49ff31eabebed0da0ae6634124f8ba5c6fbf57f1",
- "sse_tag": "e5cf68975e8e87171a3dc297577aa073454a91dc",
+ "sse_tag": "814924bac4d3cd56f40d05b3427e69f3e966d139",
"stack_trace_tag": "6788afc61875079b71b3d1c3e65aeaa6a25cbc2f",
"stagehand_tag": "v3.3.11",
"stream_channel_tag": "d7251e61253ec389ee6e045ee1042311bced8f1d",
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index adbfc57..fb9d078 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -15,7 +15,7 @@
hide AnalysisOptions;
import 'package:analysis_server/src/analysis_server_abstract.dart';
import 'package:analysis_server/src/channel/channel.dart';
-import 'package:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
import 'package:analysis_server/src/computer/new_notifications.dart';
import 'package:analysis_server/src/context_manager.dart';
import 'package:analysis_server/src/domain_analysis.dart';
@@ -600,7 +600,6 @@
/// Various IDE options.
class AnalysisServerOptions {
- String fileReadMode = 'as-is';
String newAnalysisDriverLog;
String clientId;
@@ -826,7 +825,7 @@
}
List<HighlightRegion> _computeHighlightRegions(CompilationUnit unit) {
- return DartUnitHighlightsComputer2(unit).compute();
+ return DartUnitHighlightsComputer(unit).compute();
}
server.AnalysisNavigationParams _computeNavigationParams(
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
similarity index 98%
rename from pkg/analysis_server/lib/src/computer/computer_highlights2.dart
rename to pkg/analysis_server/lib/src/computer/computer_highlights.dart
index 70489fe..df56229 100644
--- a/pkg/analysis_server/lib/src/computer/computer_highlights2.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -10,16 +10,16 @@
import 'package:analyzer_plugin/protocol/protocol_common.dart' hide Element;
/// A computer for [HighlightRegion]s in a Dart [CompilationUnit].
-class DartUnitHighlightsComputer2 {
+class DartUnitHighlightsComputer {
final CompilationUnit _unit;
final List<HighlightRegion> _regions = <HighlightRegion>[];
- DartUnitHighlightsComputer2(this._unit);
+ DartUnitHighlightsComputer(this._unit);
/// Returns the computed highlight regions, not `null`.
List<HighlightRegion> compute() {
- _unit.accept(_DartUnitHighlightsComputerVisitor2(this));
+ _unit.accept(_DartUnitHighlightsComputerVisitor(this));
_addCommentRanges();
return _regions;
}
@@ -429,11 +429,11 @@
}
}
-/// An AST visitor for [DartUnitHighlightsComputer2].
-class _DartUnitHighlightsComputerVisitor2 extends RecursiveAstVisitor<void> {
- final DartUnitHighlightsComputer2 computer;
+/// An AST visitor for [DartUnitHighlightsComputer].
+class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
+ final DartUnitHighlightsComputer computer;
- _DartUnitHighlightsComputerVisitor2(this.computer);
+ _DartUnitHighlightsComputerVisitor(this.computer);
@override
void visitAnnotation(Annotation node) {
diff --git a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
index ae00fb8..832f432 100644
--- a/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
+++ b/pkg/analysis_server/lib/src/lsp/lsp_socket_server.dart
@@ -64,18 +64,8 @@
return;
}
- PhysicalResourceProvider resourceProvider;
- if (analysisServerOptions.fileReadMode == 'as-is') {
- resourceProvider = PhysicalResourceProvider(null,
- stateLocation: analysisServerOptions.cacheFolder);
- } else if (analysisServerOptions.fileReadMode == 'normalize-eol-always') {
- resourceProvider = PhysicalResourceProvider(
- PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS,
- stateLocation: analysisServerOptions.cacheFolder);
- } else {
- throw Exception(
- 'File read mode was set to the unknown mode: $analysisServerOptions.fileReadMode');
- }
+ var resourceProvider = PhysicalResourceProvider(
+ stateLocation: analysisServerOptions.cacheFolder);
analysisServer = LspAnalysisServer(
serverChannel,
diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
index 1f2391e..a53a0cf 100644
--- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart
+++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
@@ -5,7 +5,7 @@
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/computer/computer_closingLabels.dart';
import 'package:analysis_server/src/computer/computer_folding.dart';
-import 'package:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
import 'package:analysis_server/src/computer/computer_outline.dart';
import 'package:analysis_server/src/computer/computer_overrides.dart';
import 'package:analysis_server/src/domains/analysis/implemented_dart.dart';
@@ -99,7 +99,7 @@
void sendAnalysisNotificationHighlights(
AnalysisServer server, String file, CompilationUnit dartUnit) {
_sendNotification(server, () {
- var regions = DartUnitHighlightsComputer2(dartUnit).compute();
+ var regions = DartUnitHighlightsComputer(dartUnit).compute();
var params = protocol.AnalysisHighlightsParams(file, regions);
server.sendNotification(params.toNotification());
});
diff --git a/pkg/analysis_server/lib/src/server/driver.dart b/pkg/analysis_server/lib/src/server/driver.dart
index 36cd808..754fb57 100644
--- a/pkg/analysis_server/lib/src/server/driver.dart
+++ b/pkg/analysis_server/lib/src/server/driver.dart
@@ -112,7 +112,8 @@
List<String> allowed,
Map<String, String> allowedHelp,
String defaultsTo,
- void Function(Object) callback}) {
+ void Function(Object) callback,
+ bool hide = false}) {
_knownFlags.add(name);
_parser.addOption(name,
abbr: abbr,
@@ -121,7 +122,8 @@
allowed: allowed,
allowedHelp: allowedHelp,
defaultsTo: defaultsTo,
- callback: callback);
+ callback: callback,
+ hide: hide);
}
/// Generates a string displaying usage information for the defined options.
@@ -229,9 +231,6 @@
static const String DISABLE_SERVER_FEATURE_SEARCH =
'disable-server-feature-search';
- /// The name of the option used to set the file read mode.
- static const String FILE_READ_MODE = 'file-read-mode';
-
/// The name of the option used to print usage information.
static const String HELP_OPTION = 'help';
@@ -305,7 +304,6 @@
var results = parser.parse(arguments, <String, String>{});
var analysisServerOptions = AnalysisServerOptions();
- analysisServerOptions.fileReadMode = results[FILE_READ_MODE];
analysisServerOptions.newAnalysisDriverLog =
results[NEW_ANALYSIS_DRIVER_LOG];
analysisServerOptions.clientId = results[CLIENT_ID];
@@ -763,18 +761,6 @@
' status and performance information');
parser.addOption(SDK_OPTION,
valueHelp: 'path', help: 'Path to the Dart sdk');
- parser.addOption(FILE_READ_MODE,
- help: 'an option for reading files (some clients normalize eol '
- 'characters, which make the file offset and range information '
- 'incorrect)',
- valueHelp: 'mode',
- allowed: ['as-is', 'normalize-eol-always'],
- allowedHelp: {
- 'as-is': 'file contents are read as-is',
- 'normalize-eol-always':
- r"eol characters normalized to the single character new line ('\n')"
- },
- defaultsTo: 'as-is');
parser.addOption(CACHE_FOLDER,
valueHelp: 'path', help: 'Path to the location to write cache data');
parser.addFlag(USE_LSP,
@@ -808,9 +794,11 @@
parser.addMultiOption('enable-experiment', hide: true);
// Removed 9/23/2020.
parser.addFlag('enable-instrumentation', hide: true);
+ // Removed 11/12/2020.
+ parser.addOption('file-read-mode', hide: true);
// Removed 11/8/2020.
parser.addFlag('preview-dart-2', hide: true);
- // Removed 11/11/2020.
+ // Removed 11/12/2020.
parser.addFlag('useAnalysisHighlight2', hide: true);
// Removed 9/23/2020.
parser.addFlag('use-fasta-parser', hide: true);
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
index cd9e6b9..cb8540a 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/transform_set_parser.dart
@@ -16,6 +16,7 @@
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/transform_set_error_code.dart';
import 'package:analysis_server/src/services/correction/fix/data_driven/value_generator.dart';
+import 'package:analysis_server/src/services/correction/fix/data_driven/variable_scope.dart';
import 'package:analysis_server/src/utilities/extensions/yaml.dart';
import 'package:analyzer/error/listener.dart';
import 'package:meta/meta.dart';
@@ -117,8 +118,13 @@
/// The error reporter to which diagnostics will be reported.
final ErrorReporter errorReporter;
+ /// The name of the package from which the data file being translated was
+ /// found.
final String packageName;
+ /// The variable scope defined for the current transform.
+ VariableScope transformVariableScope = VariableScope.empty;
+
/// The parameter modifications associated with the current transform, or
/// `null` if the current transform does not yet have any such modifications.
List<ParameterModification> _parameterModifications;
@@ -141,8 +147,8 @@
/// Convert the given [template] into a list of components. Variable
/// references in the template are looked up in the map of [generators].
- List<TemplateComponent> _extractTemplateComponents(String template,
- Map<String, ValueGenerator> generators, int templateOffset) {
+ List<TemplateComponent> _extractTemplateComponents(
+ String template, VariableScope variableScope, int templateOffset) {
var components = <TemplateComponent>[];
var textStart = 0;
var variableStart = template.indexOf(_openComponent);
@@ -163,7 +169,7 @@
return components;
} else {
var name = template.substring(variableStart + 2, endIndex).trim();
- var generator = generators[name];
+ var generator = variableScope.lookup(name);
if (generator == null) {
errorReporter.reportErrorForOffset(
TransformSetErrorCode.undefinedVariable,
@@ -490,10 +496,11 @@
}
}
// TODO(brianwilkerson) We should report unreferenced variables.
- var generators = _translateTemplateVariables(node.valueAt(_variablesKey),
+ var variableScope = _translateTemplateVariables(
+ node.valueAt(_variablesKey),
ErrorContext(key: _variablesKey, parentNode: node));
var components =
- _extractTemplateComponents(template, generators, templateOffset);
+ _extractTemplateComponents(template, variableScope, templateOffset);
return CodeTemplate(kind, components);
} else if (node == null) {
if (required) {
@@ -766,11 +773,10 @@
}
}
- /// Translate the [node] into a list of template components. Return the
- /// resulting list, or `null` if the [node] does not represent a valid
- /// variables map. If the [node] is not valid, use the [context] to report the
- /// error.
- Map<String, ValueGenerator> _translateTemplateVariables(
+ /// Translate the [node] into a variable scope. Return the resulting scope, or
+ /// the enclosing scope if the [node] does not represent a valid variables
+ /// map. If the [node] is not valid, use the [context] to report the error.
+ VariableScope _translateTemplateVariables(
YamlNode node, ErrorContext context) {
if (node is YamlMap) {
var generators = <String, ValueGenerator>{};
@@ -784,11 +790,12 @@
}
}
}
- return generators;
+ return VariableScope(transformVariableScope, generators);
} else if (node == null) {
- return const {};
+ return transformVariableScope;
} else {
- return _reportInvalidValue(node, context, 'Map');
+ _reportInvalidValue(node, context, 'Map');
+ return transformVariableScope;
}
}
@@ -798,8 +805,14 @@
Transform _translateTransform(YamlNode node, ErrorContext context) {
assert(node != null);
if (node is YamlMap) {
- _reportUnsupportedKeys(node,
- const {_bulkApplyKey, _changesKey, _dateKey, _elementKey, _titleKey});
+ _reportUnsupportedKeys(node, const {
+ _bulkApplyKey,
+ _changesKey,
+ _dateKey,
+ _elementKey,
+ _titleKey,
+ _variablesKey
+ });
var title = _translateString(node.valueAt(_titleKey),
ErrorContext(key: _titleKey, parentNode: node));
var date = _translateDate(node.valueAt(_dateKey),
@@ -810,8 +823,12 @@
true;
var element = _translateElement(node.valueAt(_elementKey),
ErrorContext(key: _elementKey, parentNode: node));
+ transformVariableScope = _translateTemplateVariables(
+ node.valueAt(_variablesKey),
+ ErrorContext(key: _variablesKey, parentNode: node));
var changes = _translateList(node.valueAt(_changesKey),
ErrorContext(key: _changesKey, parentNode: node), _translateChange);
+ transformVariableScope = VariableScope.empty;
if (title == null || date == null || element == null || changes == null) {
// The error has already been reported.
return null;
diff --git a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
index 4e5bd68..0ae0c27 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix/data_driven/variable_scope.dart
@@ -6,6 +6,9 @@
/// A scope in which the generators associated with variables can be looked up.
class VariableScope {
+ /// An empty variable scope.
+ static final empty = VariableScope(null, {});
+
/// The outer scope in which this scope is nested.
final VariableScope outerScope;
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
index 25ad0f77..b86d18d 100644
--- a/pkg/analysis_server/lib/src/socket_server.dart
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -17,7 +17,9 @@
abstract class AbstractSocketServer {
AbstractAnalysisServer get analysisServer;
+
AnalysisServerOptions get analysisServerOptions;
+
DiagnosticServer get diagnosticServer;
}
@@ -66,18 +68,8 @@
return;
}
- PhysicalResourceProvider resourceProvider;
- if (analysisServerOptions.fileReadMode == 'as-is') {
- resourceProvider = PhysicalResourceProvider(null,
- stateLocation: analysisServerOptions.cacheFolder);
- } else if (analysisServerOptions.fileReadMode == 'normalize-eol-always') {
- resourceProvider = PhysicalResourceProvider(
- PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS,
- stateLocation: analysisServerOptions.cacheFolder);
- } else {
- throw Exception(
- 'File read mode was set to the unknown mode: $analysisServerOptions.fileReadMode');
- }
+ var resourceProvider = PhysicalResourceProvider(
+ stateLocation: analysisServerOptions.cacheFolder);
analysisServer = AnalysisServer(
serverChannel,
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index a6b4251..8b50d8f 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -55,14 +55,14 @@
class AbstractContextManagerTest extends ContextManagerTest {
void test_contextsInAnalysisRoot_nestedContext() {
var subProjPath = join(projPath, 'subproj');
- var subProjFolder = resourceProvider.newFolder(subProjPath);
- resourceProvider.newFile(join(subProjPath, 'pubspec.yaml'), 'contents');
+ var subProjFolder = newFolder(subProjPath);
+ newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
var subProjFilePath = join(subProjPath, 'file.dart');
- resourceProvider.newFile(subProjFilePath, 'contents');
+ newFile(subProjFilePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
// Make sure that there really are contexts for both the main project and
// the subproject.
- var projectFolder = resourceProvider.getFolder(projPath);
+ var projectFolder = getFolder(projPath);
var projContextInfo = manager.getContextInfoFor(projectFolder);
expect(projContextInfo, isNotNull);
expect(projContextInfo.folder, projectFolder);
@@ -106,7 +106,7 @@
"dart:typed_data": "../embedder/src/part"
''');
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
// NOTE that this is Not in our package path yet.
@@ -159,8 +159,7 @@
manager.setRoots(<String>[projPath], <String>[]);
await pumpEventQueue();
// Confirm that one context was created.
- var count = manager
- .numberOfContextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+ var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
expect(count, equals(1));
var source = sourceFactory.forUri('dart:foobar');
expect(source, isNotNull);
@@ -178,8 +177,8 @@
var project = convertPath('/project');
var excludedFolder = convertPath('$project/excluded');
// set roots
- resourceProvider.newFolder(project);
- resourceProvider.newFolder(excludedFolder);
+ newFolder(project);
+ newFolder(excludedFolder);
manager.setRoots(<String>[project], <String>[excludedFolder]);
// verify
expect(manager.isInAnalysisRoot(convertPath('$excludedFolder/test.dart')),
@@ -188,10 +187,10 @@
void test_isInAnalysisRoot_inNestedContext() {
var subProjPath = join(projPath, 'subproj');
- var subProjFolder = resourceProvider.newFolder(subProjPath);
- resourceProvider.newFile(join(subProjPath, 'pubspec.yaml'), 'contents');
+ var subProjFolder = newFolder(subProjPath);
+ newFile(join(subProjPath, 'pubspec.yaml'), content: 'contents');
var subProjFilePath = join(subProjPath, 'file.dart');
- resourceProvider.newFile(subProjFilePath, 'contents');
+ newFile(subProjFilePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
// Make sure that there really is a context for the subproject.
var subProjContextInfo = manager.getContextInfoFor(subProjFolder);
@@ -214,14 +213,14 @@
Future<void> test_packagesFolder_areAnalyzed() {
// create a context with a pubspec.yaml file
var pubspecPath = join(projPath, 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'pubspec');
+ newFile(pubspecPath, content: 'pubspec');
// create a file in the "packages" folder
var filePath1 = join(projPath, 'packages', 'file1.dart');
- var file1 = resourceProvider.newFile(filePath1, 'contents');
+ var file1 = newFile(filePath1, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
expect(callbacks.currentFilePaths, unorderedEquals([file1.path]));
var filePath2 = join(projPath, 'packages', 'file2.dart');
- var file2 = resourceProvider.newFile(filePath2, 'contents');
+ var file2 = newFile(filePath2, content: 'contents');
return pumpEventQueue().then((_) {
expect(callbacks.currentFilePaths,
unorderedEquals([file1.path, file2.path]));
@@ -230,7 +229,7 @@
Future<void> test_path_filter() async {
// Setup context.
- var root = resourceProvider.newFolder(projPath);
+ var root = newFolder(projPath);
manager.setRoots(<String>[projPath], <String>[]);
expect(callbacks.currentFilePaths, isEmpty);
// Set ignore patterns for context.
@@ -257,7 +256,7 @@
Future<void> test_refresh_folder_with_packagespec() {
// create a context with a .packages file
var packagespecFile = join(projPath, '.packages');
- resourceProvider.newFile(packagespecFile, '');
+ newFile(packagespecFile, content: '');
manager.setRoots(<String>[projPath], <String>[]);
return pumpEventQueue().then((_) {
expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -280,8 +279,8 @@
var subdir2Path = join(projPath, 'subdir2');
var packagespec1Path = join(subdir1Path, '.packages');
var packagespec2Path = join(subdir2Path, '.packages');
- resourceProvider.newFile(packagespec1Path, '');
- resourceProvider.newFile(packagespec2Path, '');
+ newFile(packagespec1Path, content: '');
+ newFile(packagespec2Path, content: '');
manager.setRoots(<String>[projPath], <String>[]);
return pumpEventQueue().then((_) {
expect(callbacks.currentContextRoots,
@@ -301,7 +300,7 @@
Future<void> test_refresh_folder_with_pubspec() {
// create a context with a pubspec.yaml file
var pubspecPath = join(projPath, 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'pubspec');
+ newFile(pubspecPath, content: 'pubspec');
manager.setRoots(<String>[projPath], <String>[]);
return pumpEventQueue().then((_) {
expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -321,8 +320,8 @@
var subdir2Path = join(projPath, 'subdir2');
var pubspec1Path = join(subdir1Path, 'pubspec.yaml');
var pubspec2Path = join(subdir2Path, 'pubspec.yaml');
- resourceProvider.newFile(pubspec1Path, 'pubspec');
- resourceProvider.newFile(pubspec2Path, 'pubspec');
+ newFile(pubspec1Path, content: 'pubspec');
+ newFile(pubspec2Path, content: 'pubspec');
manager.setRoots(<String>[projPath], <String>[]);
return pumpEventQueue().then((_) {
expect(callbacks.currentContextRoots,
@@ -342,12 +341,12 @@
Future<void> test_refresh_oneContext() {
// create two contexts with pubspec.yaml files
var pubspecPath = join(projPath, 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'pubspec1');
+ newFile(pubspecPath, content: 'pubspec1');
var proj2Path = convertPath('/my/proj2');
- resourceProvider.newFolder(proj2Path);
+ newFolder(proj2Path);
var pubspec2Path = join(proj2Path, 'pubspec.yaml');
- resourceProvider.newFile(pubspec2Path, 'pubspec2');
+ newFile(pubspec2Path, content: 'pubspec2');
var roots = <String>[projPath, proj2Path];
manager.setRoots(roots, <String>[]);
@@ -355,7 +354,7 @@
expect(callbacks.currentContextRoots, unorderedEquals(roots));
var then = callbacks.now;
callbacks.now++;
- manager.refresh([resourceProvider.getResource(proj2Path)]);
+ manager.refresh([getFolder(proj2Path)]);
return pumpEventQueue().then((_) {
expect(callbacks.currentContextRoots, unorderedEquals(roots));
expect(callbacks.currentContextTimestamps[projPath], then);
@@ -365,15 +364,14 @@
}
void test_setRoots_addFolderWithDartFile() {
- var filePath = resourceProvider.pathContext.join(projPath, 'foo.dart');
- resourceProvider.newFile(filePath, 'contents');
+ var filePath = join(projPath, 'foo.dart');
+ newFile(filePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
// verify
var filePaths = callbacks.currentFilePaths;
expect(filePaths, hasLength(1));
expect(filePaths, contains(filePath));
- var drivers =
- manager.getDriversInAnalysisRoot(resourceProvider.newFolder(projPath));
+ var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
expect(drivers, hasLength(1));
expect(drivers[0], isNotNull);
var result = sourceFactory.forUri('dart:async');
@@ -382,7 +380,7 @@
void test_setRoots_addFolderWithDartFileInSubfolder() {
var filePath = join(projPath, 'foo', 'bar.dart');
- resourceProvider.newFile(filePath, 'contents');
+ newFile(filePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
// verify
var filePaths = callbacks.currentFilePaths;
@@ -415,14 +413,12 @@
expect(callbacks.currentContextRoots, contains(projPath));
var projSources = callbacks.currentFileSources(projPath);
expect(projSources, hasLength(1));
- expect(projSources.first.uri.toString(),
- (Uri.file(join(libPath, 'main.dart')).toString()));
+ expect(projSources.first.uri, toUri('$libPath/main.dart'));
expect(callbacks.currentContextRoots, contains(examplePath));
var exampleSources = callbacks.currentFileSources(examplePath);
expect(exampleSources, hasLength(1));
- expect(exampleSources.first.uri.toString(),
- (Uri.file(join(examplePath, 'example.dart')).toString()));
+ expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
}
void test_setRoots_addFolderWithNestedPubspec() {
@@ -449,8 +445,7 @@
expect(callbacks.currentContextRoots, contains(examplePath));
var exampleSources = callbacks.currentFileSources(examplePath);
expect(exampleSources, hasLength(1));
- expect(exampleSources.first.uri.toString(),
- (Uri.file('$examplePath/example.dart').toString()));
+ expect(exampleSources.first.uri, toUri('$examplePath/example.dart'));
}
void test_setRoots_addFolderWithoutPubspec() {
@@ -463,8 +458,7 @@
void test_setRoots_addFolderWithPackagespec() {
var packagespecPath = join(projPath, '.packages');
var testLib = convertPath('/home/somebody/.pub/cache/unittest-0.9.9/lib');
- var testLibUri = resourceProvider.pathContext.toUri(testLib);
- resourceProvider.newFile(packagespecPath, 'unittest:$testLibUri');
+ newFile(packagespecPath, content: 'unittest:${toUriStr(testLib)}');
var libPath = '$projPath/${ContextManagerTest.LIB_NAME}';
var mainFile = newFile('$libPath/main.dart');
var source = mainFile.createSource();
@@ -484,7 +478,7 @@
void test_setRoots_addFolderWithPubspec() {
var pubspecPath = join(projPath, 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'pubspec');
+ newFile(pubspecPath, content: 'pubspec');
manager.setRoots(<String>[projPath], <String>[]);
// verify
expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
@@ -494,8 +488,8 @@
void test_setRoots_addFolderWithPubspec_andPackagespec() {
var pubspecPath = join(projPath, 'pubspec.yaml');
var packagespecPath = join(projPath, '.packages');
- resourceProvider.newFile(pubspecPath, 'pubspec');
- resourceProvider.newFile(packagespecPath, '');
+ newFile(pubspecPath, content: 'pubspec');
+ newFile(packagespecPath, content: '');
manager.setRoots(<String>[projPath], <String>[]);
// verify
callbacks.assertContextPaths([projPath]);
@@ -521,10 +515,10 @@
expect(callbacks.currentContextRoots, unorderedEquals([projPath]));
expect(sources, hasLength(4));
var uris = sources.map((Source source) => source.uri.toString()).toList();
- expect(uris, contains((Uri.file(appPath)).toString()));
+ expect(uris, contains(toUriStr(appPath)));
expect(uris, contains('package:proj/main.dart'));
expect(uris, contains('package:proj/src/internal.dart'));
- expect(uris, contains((Uri.file(testFilePath)).toString()));
+ expect(uris, contains(toUriStr(testFilePath)));
}
void test_setRoots_addFolderWithPubspecAndPackagespecFolders() {
@@ -571,9 +565,9 @@
newFile('$projectB/${ContextManagerImpl.PUBSPEC_NAME}');
newFile('$projectB/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
content: 'bar:lib/');
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subProjectA_file, 'library a;');
- resourceProvider.newFile(subProjectB_file, 'library b;');
+ newFile(rootFile, content: 'library root;');
+ newFile(subProjectA_file, content: 'library a;');
+ newFile(subProjectB_file, content: 'library b;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, projectA, projectB]);
@@ -586,12 +580,12 @@
expect(
_packageMap(projectA),
equals({
- 'foo': [resourceProvider.getFolder(projectALib)]
+ 'foo': [getFolder(projectALib)]
}));
expect(
_packageMap(projectB),
equals({
- 'bar': [resourceProvider.getFolder(projectBLib)]
+ 'bar': [getFolder(projectBLib)]
}));
}
@@ -601,8 +595,8 @@
var file1 = convertPath('$project/file1.dart');
var file2 = convertPath('$project/file2.dart');
// create files
- resourceProvider.newFile(file1, '// 1');
- resourceProvider.newFile(file2, '// 2');
+ newFile(file1, content: '// 1');
+ newFile(file2, content: '// 2');
// set roots
manager.setRoots(<String>[project], <String>[file1]);
callbacks.assertContextPaths([project]);
@@ -617,8 +611,8 @@
var fileA = convertPath('$folderA/a.dart');
var fileB = convertPath('$folderB/b.dart');
// create files
- resourceProvider.newFile(fileA, 'library a;');
- resourceProvider.newFile(fileB, 'library b;');
+ newFile(fileA, content: 'library a;');
+ newFile(fileB, content: 'library b;');
// set roots
manager.setRoots(<String>[project], <String>[folderB]);
callbacks.assertContextPaths([project]);
@@ -631,8 +625,8 @@
var file1 = convertPath('$project/file1.dart');
var file2 = convertPath('$project/file2.dart');
// create files
- resourceProvider.newFile(file1, '// 1');
- resourceProvider.newFile(file2, '// 2');
+ newFile(file1, content: '// 1');
+ newFile(file2, content: '// 2');
// set roots
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
@@ -651,8 +645,8 @@
var fileA = convertPath('$folderA/a.dart');
var fileB = convertPath('$folderB/b.dart');
// create files
- resourceProvider.newFile(fileA, 'library a;');
- resourceProvider.newFile(fileB, 'library b;');
+ newFile(fileA, content: 'library a;');
+ newFile(fileB, content: 'library b;');
// initially both "aaa/a" and "bbb/b" are included
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
@@ -669,8 +663,8 @@
var file1 = convertPath('$project/file1.dart');
var file2 = convertPath('$project/file2.dart');
// create files
- resourceProvider.newFile(file1, '// 1');
- resourceProvider.newFile(file2, '// 2');
+ newFile(file1, content: '// 1');
+ newFile(file2, content: '// 2');
// set roots
manager.setRoots(<String>[project], <String>[file2]);
callbacks.assertContextPaths([project]);
@@ -687,8 +681,8 @@
var file1 = convertPath('$project/bin/file1.dart');
var file2 = convertPath('$project/bin/file2.dart');
// create files
- resourceProvider.newFile(file1, '// 1');
- resourceProvider.newFile(file2, '// 2');
+ newFile(file1, content: '// 1');
+ newFile(file2, content: '// 2');
// set roots
manager.setRoots(<String>[project], <String>[file2]);
callbacks.assertContextPaths([project]);
@@ -707,8 +701,8 @@
var fileA = convertPath('$folderA/a.dart');
var fileB = convertPath('$folderB/b.dart');
// create files
- resourceProvider.newFile(fileA, 'library a;');
- resourceProvider.newFile(fileB, 'library b;');
+ newFile(fileA, content: 'library a;');
+ newFile(fileB, content: 'library b;');
// exclude "bbb/"
manager.setRoots(<String>[project], <String>[folderB]);
callbacks.assertContextPaths([project]);
@@ -724,9 +718,9 @@
var fileA = convertPath('$project/foo.dart');
var fileB = convertPath('$project/lib/doc/bar.dart');
var fileC = convertPath('$project/doc/bar.dart');
- resourceProvider.newFile(fileA, '');
- resourceProvider.newFile(fileB, '');
- resourceProvider.newFile(fileC, '');
+ newFile(fileA, content: '');
+ newFile(fileB, content: '');
+ newFile(fileC, content: '');
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -738,8 +732,8 @@
var example = convertPath('$project/example');
var examplePubspec = convertPath('$example/pubspec.yaml');
// create files
- resourceProvider.newFile(projectPubspec, 'name: project');
- resourceProvider.newFile(examplePubspec, 'name: example');
+ newFile(projectPubspec, content: 'name: project');
+ newFile(examplePubspec, content: 'name: example');
manager.setRoots(<String>[example, project], <String>[]);
// verify
{
@@ -764,8 +758,8 @@
var projectPubspec = convertPath('$project/pubspec.yaml');
var example = convertPath('$project/example');
// create files
- resourceProvider.newFile(projectPubspec, 'name: project');
- resourceProvider.newFolder(example);
+ newFile(projectPubspec, content: 'name: project');
+ newFolder(example);
manager.setRoots(<String>[project, example], <String>[]);
// verify
{
@@ -786,8 +780,8 @@
var example = convertPath('$project/example');
var examplePubspec = convertPath('$example/pubspec.yaml');
// create files
- resourceProvider.newFile(projectPubspec, 'name: project');
- resourceProvider.newFile(examplePubspec, 'name: example');
+ newFile(projectPubspec, content: 'name: project');
+ newFile(examplePubspec, content: 'name: example');
manager.setRoots(<String>[project, example], <String>[]);
// verify
{
@@ -811,7 +805,7 @@
var packagePath = convertPath('/package/foo');
newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
content: 'foo:${toUriStr('/package/foo')}');
- var packageFolder = resourceProvider.newFolder(packagePath);
+ var packageFolder = newFolder(packagePath);
manager.setRoots(<String>[projPath], <String>[]);
expect(
_currentPackageMap,
@@ -826,7 +820,7 @@
var excludedFolder = convertPath('$project/excluded');
var excludedPubspec = convertPath('$excludedFolder/pubspec.yaml');
// create files
- resourceProvider.newFile(excludedPubspec, 'name: ignore-me');
+ newFile(excludedPubspec, content: 'name: ignore-me');
// set "/project", and exclude "/project/excluded"
manager.setRoots(<String>[project], <String>[excludedFolder]);
callbacks.assertContextPaths([project]);
@@ -834,7 +828,7 @@
void test_setRoots_noContext_inDotFolder() {
var pubspecPath = join(projPath, '.pub', 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'name: test');
+ newFile(pubspecPath, content: 'name: test');
manager.setRoots(<String>[projPath], <String>[]);
// verify
expect(callbacks.currentContextRoots, hasLength(1));
@@ -846,11 +840,10 @@
var filePath = join(projPath, 'lib', 'foo.dart');
newFile('$projPath/${ContextManagerImpl.PACKAGE_SPEC_NAME}',
content: 'foo:lib/');
- resourceProvider.newFile(filePath, 'contents');
+ newFile(filePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
- var drivers =
- manager.getDriversInAnalysisRoot(resourceProvider.newFolder(projPath));
+ var drivers = manager.getDriversInAnalysisRoot(newFolder(projPath));
expect(drivers, hasLength(1));
expect(drivers[0], isNotNull);
var result = sourceFactory.forUri('package:foo/foo.dart');
@@ -859,7 +852,7 @@
void test_setRoots_packagesFolder_hasContext() {
var pubspecPath = join(projPath, 'packages', 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'name: test');
+ newFile(pubspecPath, content: 'name: test');
manager.setRoots(<String>[projPath], <String>[]);
// verify
expect(callbacks.currentContextRoots, hasLength(2));
@@ -873,8 +866,8 @@
var project = convertPath('/project');
var fileA = convertPath('$project/foo.dart');
var fileB = convertPath('$project/.pub/bar.dart');
- resourceProvider.newFile(fileA, '');
- resourceProvider.newFile(fileB, '');
+ newFile(fileA, content: '');
+ newFile(fileB, content: '');
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
@@ -893,7 +886,7 @@
void test_setRoots_removeFolderWithPackagespec() {
// create a pubspec
var pubspecPath = join(projPath, '.packages');
- resourceProvider.newFile(pubspecPath, '');
+ newFile(pubspecPath, content: '');
// add one root - there is a context
manager.setRoots(<String>[projPath], <String>[]);
expect(manager.changeSubscriptions, hasLength(1));
@@ -918,12 +911,12 @@
var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
// create files
- resourceProvider.newFile(projectA_file, '// a');
- resourceProvider.newFile(projectB_file, '// b');
- resourceProvider.newFile(subProjectA_pubspec, '');
- resourceProvider.newFile(subProjectB_pubspec, '');
- resourceProvider.newFile(subProjectA_file, '// sub-a');
- resourceProvider.newFile(subProjectB_file, '// sub-b');
+ newFile(projectA_file, content: '// a');
+ newFile(projectB_file, content: '// b');
+ newFile(subProjectA_pubspec, content: '');
+ newFile(subProjectB_pubspec, content: '');
+ newFile(subProjectA_file, content: '// sub-a');
+ newFile(subProjectB_file, content: '// sub-b');
// set roots
manager.setRoots(<String>[projectA, projectB], <String>[]);
callbacks
@@ -942,7 +935,7 @@
void test_setRoots_removeFolderWithPubspec() {
// create a pubspec
var pubspecPath = join(projPath, 'pubspec.yaml');
- resourceProvider.newFile(pubspecPath, 'pubspec');
+ newFile(pubspecPath, content: 'pubspec');
// add one root - there is a context
manager.setRoots(<String>[projPath], <String>[]);
expect(callbacks.currentContextRoots, hasLength(1));
@@ -965,12 +958,12 @@
var subProjectA_file = convertPath('$subProjectA/bin/sub_a.dart');
var subProjectB_file = convertPath('$subProjectB/bin/sub_b.dart');
// create files
- resourceProvider.newFile(projectA_file, '// a');
- resourceProvider.newFile(projectB_file, '// b');
- resourceProvider.newFile(subProjectA_pubspec, 'pubspec');
- resourceProvider.newFile(subProjectB_pubspec, 'pubspec');
- resourceProvider.newFile(subProjectA_file, '// sub-a');
- resourceProvider.newFile(subProjectB_file, '// sub-b');
+ newFile(projectA_file, content: '// a');
+ newFile(projectB_file, content: '// b');
+ newFile(subProjectA_pubspec, content: 'pubspec');
+ newFile(subProjectB_pubspec, content: 'pubspec');
+ newFile(subProjectA_file, content: '// sub-a');
+ newFile(subProjectB_file, content: '// sub-b');
// set roots
manager.setRoots(<String>[projectA, projectB], <String>[]);
callbacks
@@ -992,7 +985,7 @@
// context to be ignored.
var project = convertPath('/.pub/project');
var fileA = convertPath('$project/foo.dart');
- resourceProvider.newFile(fileA, '');
+ newFile(fileA, content: '');
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
@@ -1017,7 +1010,7 @@
expect(callbacks.currentFilePaths, hasLength(0));
// add file
var filePath = join(projPath, 'foo.dart');
- resourceProvider.newFile(filePath, 'contents');
+ newFile(filePath, content: 'contents');
// the file was added
return pumpEventQueue().then((_) {
var filePaths = callbacks.currentFilePaths;
@@ -1034,13 +1027,13 @@
var fileA = convertPath('$folderA/a.dart');
var fileB = convertPath('$folderB/b.dart');
// create files
- resourceProvider.newFile(fileA, 'library a;');
+ newFile(fileA, content: 'library a;');
// set roots
manager.setRoots(<String>[project], <String>[folderB]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
// add a file, ignored as excluded
- resourceProvider.newFile(fileB, 'library b;');
+ newFile(fileB, content: 'library b;');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
@@ -1053,13 +1046,13 @@
var fileA = convertPath('$project/a.dart');
var fileB = convertPath('$project/lib/doc/b.dart');
// create files
- resourceProvider.newFile(fileA, '');
+ newFile(fileA, content: '');
// set roots
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
// add a "lib/doc" file, it is not ignored
- resourceProvider.newFile(fileB, '');
+ newFile(fileB, content: '');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -1072,13 +1065,13 @@
var fileA = convertPath('$project/a.dart');
var fileB = convertPath('$project/doc/b.dart');
// create files
- resourceProvider.newFile(fileA, '');
+ newFile(fileA, content: '');
// set roots
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
// add a "doc" file, it is ignored
- resourceProvider.newFile(fileB, '');
+ newFile(fileB, content: '');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
@@ -1091,11 +1084,11 @@
var project = convertPath('/project');
var fileA = convertPath('$project/foo.dart');
var fileB = convertPath('$project/.pub/bar.dart');
- resourceProvider.newFile(fileA, '');
+ newFile(fileA, content: '');
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
- resourceProvider.newFile(fileB, '');
+ newFile(fileB, content: '');
await pumpEventQueue();
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
@@ -1107,11 +1100,11 @@
var project = convertPath('/.pub/project');
var fileA = convertPath('$project/foo.dart');
var fileB = convertPath('$project/bar/baz.dart');
- resourceProvider.newFile(fileA, '');
+ newFile(fileA, content: '');
manager.setRoots(<String>[project], <String>[]);
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA]);
- resourceProvider.newFile(fileB, '');
+ newFile(fileB, content: '');
await pumpEventQueue();
callbacks.assertContextPaths([project]);
callbacks.assertContextFiles(project, [fileA, fileB]);
@@ -1123,7 +1116,7 @@
expect(callbacks.currentFilePaths, hasLength(0));
// add file in subfolder
var filePath = join(projPath, 'foo', 'bar.dart');
- resourceProvider.newFile(filePath, 'contents');
+ newFile(filePath, content: 'contents');
// the file was added
return pumpEventQueue().then((_) {
var filePaths = callbacks.currentFilePaths;
@@ -1138,14 +1131,14 @@
var rootFile = convertPath('$root/root.dart');
var rootPackagespec = convertPath('$root/.packages');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
+ newFile(rootFile, content: 'library root;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
// verify files
callbacks.assertContextFiles(root, [rootFile]);
// add packagespec - still just one root
- resourceProvider.newFile(rootPackagespec, '');
+ newFile(rootPackagespec, content: '');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1162,15 +1155,15 @@
var subPubspec = convertPath('$subProject/.packages');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
// verify files
callbacks.assertContextFiles(root, [rootFile, subFile]);
// add .packages
- resourceProvider.newFile(subPubspec, '');
+ newFile(subPubspec, content: '');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1187,16 +1180,16 @@
var subFile = convertPath('$subProject/bin/sub.dart');
var subSubPubspec = convertPath('$subProject/subsub/.packages');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subPubspec, '');
- resourceProvider.newFile(subFile, 'library sub;');
+ newFile(rootFile, content: 'library root;');
+ newFile(subPubspec, content: '');
+ newFile(subFile, content: 'library sub;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
callbacks.assertContextFiles(subProject, [subFile]);
// add pubspec - ignore, because is already in a packagespec-based context
- resourceProvider.newFile(subSubPubspec, '');
+ newFile(subSubPubspec, content: '');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1213,9 +1206,9 @@
var subPubspec = convertPath('$subProject/pubspec.yaml');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(subPubspec, 'pubspec');
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(subPubspec, content: 'pubspec');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
@@ -1224,7 +1217,7 @@
callbacks.assertContextFiles(subProject, [subFile]);
// add .packages
- resourceProvider.newFile(subPackagespec, '');
+ newFile(subPackagespec, content: '');
return pumpEventQueue().then((_) {
// Should NOT create another context.
callbacks.assertContextPaths([root, subProject]);
@@ -1239,14 +1232,14 @@
var rootFile = convertPath('$root/root.dart');
var rootPubspec = convertPath('$root/pubspec.yaml');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
+ newFile(rootFile, content: 'library root;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
// verify files
callbacks.assertContextFiles(root, [rootFile]);
// add pubspec - still just one root
- resourceProvider.newFile(rootPubspec, 'pubspec');
+ newFile(rootPubspec, content: 'pubspec');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1261,15 +1254,15 @@
var subPubspec = convertPath('$subProject/pubspec.yaml');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
// verify files
callbacks.assertContextFiles(root, [rootFile, subFile]);
// add pubspec
- resourceProvider.newFile(subPubspec, 'pubspec');
+ newFile(subPubspec, content: 'pubspec');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1286,16 +1279,16 @@
var subFile = convertPath('$subProject/bin/sub.dart');
var subSubPubspec = convertPath('$subProject/subsub/pubspec.yaml');
// create files
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subPubspec, 'pubspec');
- resourceProvider.newFile(subFile, 'library sub;');
+ newFile(rootFile, content: 'library root;');
+ newFile(subPubspec, content: 'pubspec');
+ newFile(subFile, content: 'library sub;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
callbacks.assertContextFiles(subProject, [subFile]);
// add pubspec - ignore, because is already in a pubspec-based context
- resourceProvider.newFile(subSubPubspec, 'pubspec');
+ newFile(subSubPubspec, content: 'pubspec');
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root, subProject]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1306,7 +1299,7 @@
Future<void> test_watch_deleteFile() {
var filePath = join(projPath, 'foo.dart');
// add root with a file
- var file = resourceProvider.newFile(filePath, 'contents');
+ var file = newFile(filePath, content: 'contents');
var projFolder = file.parent;
manager.setRoots(<String>[projPath], <String>[]);
// the file was added
@@ -1316,7 +1309,7 @@
expect(file.exists, isTrue);
expect(projFolder.exists, isTrue);
// delete the file
- resourceProvider.deleteFile(filePath);
+ deleteFile(filePath);
return pumpEventQueue().then((_) {
expect(file.exists, isFalse);
expect(projFolder.exists, isTrue);
@@ -1327,7 +1320,7 @@
Future<void> test_watch_deleteFolder() {
var filePath = join(projPath, 'foo.dart');
// add root with a file
- var file = resourceProvider.newFile(filePath, 'contents');
+ var file = newFile(filePath, content: 'contents');
var projFolder = file.parent;
manager.setRoots(<String>[projPath], <String>[]);
// the file was added
@@ -1337,7 +1330,7 @@
expect(file.exists, isTrue);
expect(projFolder.exists, isTrue);
// delete the folder
- resourceProvider.deleteFolder(projPath);
+ deleteFolder(projPath);
return pumpEventQueue().then((_) {
expect(file.exists, isFalse);
expect(projFolder.exists, isFalse);
@@ -1351,14 +1344,14 @@
var rootPubspec = convertPath('$root/.packages');
var rootFile = convertPath('$root/root.dart');
// create files
- resourceProvider.newFile(rootPubspec, '');
- resourceProvider.newFile(rootFile, 'library root;');
+ newFile(rootPubspec, content: '');
+ newFile(rootFile, content: 'library root;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
// delete the pubspec
- resourceProvider.deleteFile(rootPubspec);
+ deleteFile(rootPubspec);
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1373,9 +1366,9 @@
var subPubspec = convertPath('$subProject/.packages');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(subPubspec, '');
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(subPubspec, content: '');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
@@ -1383,7 +1376,7 @@
callbacks.assertContextFiles(root, [rootFile]);
callbacks.assertContextFiles(subProject, [subFile]);
// delete the pubspec
- resourceProvider.deleteFile(subPubspec);
+ deleteFile(subPubspec);
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile, subFile]);
@@ -1409,10 +1402,10 @@
var subPubspec = convertPath('$subProject/pubspec.yaml');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(subPackagespec, '');
- resourceProvider.newFile(subPubspec, 'pubspec');
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(subPackagespec, content: '');
+ newFile(subPubspec, content: 'pubspec');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
@@ -1420,7 +1413,7 @@
callbacks.assertContextFiles(root, [rootFile]);
callbacks.assertContextFiles(subProject, [subFile]);
// delete the packagespec
- resourceProvider.deleteFile(subPackagespec);
+ deleteFile(subPackagespec);
return pumpEventQueue().then((_) {
// Should NOT merge
callbacks.assertContextPaths([root, subProject]);
@@ -1434,14 +1427,14 @@
var rootPubspec = convertPath('$root/pubspec.yaml');
var rootFile = convertPath('$root/root.dart');
// create files
- resourceProvider.newFile(rootPubspec, 'pubspec');
- resourceProvider.newFile(rootFile, 'library root;');
+ newFile(rootPubspec, content: 'pubspec');
+ newFile(rootFile, content: 'library root;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
// delete the pubspec
- resourceProvider.deleteFile(rootPubspec);
+ deleteFile(rootPubspec);
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile]);
@@ -1456,9 +1449,9 @@
var subPubspec = convertPath('$subProject/pubspec.yaml');
var subFile = convertPath('$subProject/bin/a.dart');
// create files
- resourceProvider.newFile(subPubspec, 'pubspec');
- resourceProvider.newFile(rootFile, 'library root;');
- resourceProvider.newFile(subFile, 'library a;');
+ newFile(subPubspec, content: 'pubspec');
+ newFile(rootFile, content: 'library root;');
+ newFile(subFile, content: 'library a;');
// set roots
manager.setRoots(<String>[root], <String>[]);
callbacks.assertContextPaths([root, subProject]);
@@ -1466,7 +1459,7 @@
callbacks.assertContextFiles(root, [rootFile]);
callbacks.assertContextFiles(subProject, [subFile]);
// delete the pubspec
- resourceProvider.deleteFile(subPubspec);
+ deleteFile(subPubspec);
return pumpEventQueue().then((_) {
callbacks.assertContextPaths([root]);
callbacks.assertContextFiles(root, [rootFile, subFile]);
@@ -1476,7 +1469,7 @@
Future<void> test_watch_modifyFile() {
var filePath = join(projPath, 'foo.dart');
// add root with a file
- resourceProvider.newFile(filePath, 'contents');
+ newFile(filePath, content: 'contents');
manager.setRoots(<String>[projPath], <String>[]);
// the file was added
var filePaths = callbacks.currentFilePaths;
@@ -1485,7 +1478,7 @@
// TODO(brianwilkerson) Test when the file was modified
// update the file
callbacks.now++;
- resourceProvider.modifyFile(filePath, 'new contents');
+ modifyFile(filePath, 'new contents');
return pumpEventQueue().then((_) {
// TODO(brianwilkerson) Test when the file was modified
});
@@ -1495,8 +1488,8 @@
var packageConfigPath = '$projPath/.dart_tool/package_config.json';
var filePath = convertPath('$projPath/bin/main.dart');
- resourceProvider.newFile(packageConfigPath, '');
- resourceProvider.newFile(filePath, 'library main;');
+ newFile(packageConfigPath, content: '');
+ newFile(filePath, content: 'library main;');
manager.setRoots(<String>[projPath], <String>[]);
@@ -1507,7 +1500,7 @@
// update .dart_tool/package_config.json
callbacks.now++;
- resourceProvider.modifyFile(
+ modifyFile(
packageConfigPath,
(PackageConfigFileBuilder()..add(name: 'my', rootPath: '../'))
.toContent(toUriStr: toUriStr),
@@ -1523,8 +1516,8 @@
var packagesPath = convertPath('$projPath/.packages');
var filePath = convertPath('$projPath/bin/main.dart');
- resourceProvider.newFile(packagesPath, '');
- resourceProvider.newFile(filePath, 'library main;');
+ newFile(packagesPath, content: '');
+ newFile(filePath, content: 'library main;');
manager.setRoots(<String>[projPath], <String>[]);
@@ -1535,7 +1528,7 @@
// update .packages
callbacks.now++;
- resourceProvider.modifyFile(packagesPath, 'main:./lib/');
+ modifyFile(packagesPath, 'main:./lib/');
return pumpEventQueue().then((_) {
// verify new package info
expect(_currentPackageMap.keys, unorderedEquals(['main']));
@@ -1615,7 +1608,7 @@
void setUp() {
processRequiredPlugins();
projPath = convertPath('/my/proj');
- resourceProvider.newFolder(projPath);
+ newFolder(projPath);
// Create an SDK in the mock file system.
MockSdk(resourceProvider: resourceProvider);
var sdkManager = DartSdkManager(convertPath(sdkRoot));
@@ -1633,7 +1626,7 @@
}
Map<String, List<Folder>> _packageMap(String contextPath) {
- var folder = resourceProvider.getFolder(contextPath);
+ var folder = getFolder(contextPath);
var info = manager.getContextInfoFor(folder);
return info.analysisDriver.sourceFactory?.packageMap;
}
@@ -1821,7 +1814,7 @@
// Remove the root, with the analysis options file.
// No exceptions.
- resourceProvider.deleteFolder(projPath);
+ deleteFolder(projPath);
await pumpEventQueue();
}
@@ -1867,8 +1860,7 @@
await pumpEventQueue();
// Confirm that one context was created.
- var count = manager
- .numberOfContextsInAnalysisRoot(resourceProvider.newFolder(projPath));
+ var count = manager.numberOfContextsInAnalysisRoot(newFolder(projPath));
expect(count, equals(1));
// Verify options.
@@ -1973,7 +1965,7 @@
Future<void> test_non_analyzable_files_not_considered() async {
// Set up project and get a reference to the driver.
manager.setRoots(<String>[projPath], <String>[]);
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
var drivers = manager.getDriversInAnalysisRoot(projectFolder);
expect(drivers, hasLength(1));
@@ -1999,14 +1991,14 @@
// After a few other changes, the test now times out on my machine, so I'm
// disabling it in order to prevent it from being flaky.
_fail('Test times out');
- var file = resourceProvider.newFile('$projPath/bin/test.dart', r'''
+ var file = newFile('$projPath/bin/test.dart', content: r'''
main() {
var paths = <int>[];
var names = <String>[];
paths.addAll(names.map((s) => s.length));
}
''');
- resourceProvider.newFile('$projPath/$optionsFileName', r'''
+ newFile('$projPath/$optionsFileName', content: r'''
analyzer:
strong-mode: false
''');
@@ -2025,7 +2017,7 @@
expect(result.errors, isEmpty);
// Update the options file - turn on 'strong-mode'.
- resourceProvider.updateFile('$projPath/$optionsFileName', r'''
+ modifyFile('$projPath/$optionsFileName', r'''
analyzer:
strong-mode: true
''');
@@ -2067,7 +2059,7 @@
manager.setRoots(<String>[projPath], <String>[]);
// Verify that analysis options was parsed and the ignore patterns applied.
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
var drivers = manager.getDriversInAnalysisRoot(projectFolder);
expect(drivers, hasLength(1));
var driver = drivers[0];
@@ -2100,7 +2092,7 @@
manager.setRoots(<String>[projPath], <String>[]);
// Verify that the context in other_lib wasn't created and that the
// context in lib was created.
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
var drivers = manager.getDriversInAnalysisRoot(projectFolder);
expect(drivers, hasLength(2));
expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2132,7 +2124,7 @@
// Verify that the context in other_lib wasn't created and that the
// context in lib was created.
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
var drivers = manager.getDriversInAnalysisRoot(projectFolder);
expect(drivers, hasLength(2));
expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2161,7 +2153,7 @@
// Setup context / driver.
manager.setRoots(<String>[projPath], <String>[]);
- var projectFolder = resourceProvider.newFolder(projPath);
+ var projectFolder = newFolder(projPath);
var drivers = manager.getDriversInAnalysisRoot(projectFolder);
expect(drivers, hasLength(2));
expect(drivers[0].name, equals(convertPath('/my/proj')));
@@ -2174,8 +2166,8 @@
var example = convertPath('$project/example');
var examplePubspec = convertPath('$example/pubspec.yaml');
// create files
- resourceProvider.newFile(projectPubspec, 'name: project');
- resourceProvider.newFile(examplePubspec, 'name: example');
+ newFile(projectPubspec, content: 'name: project');
+ newFile(examplePubspec, content: 'name: example');
newFile('$project/$optionsFileName', content: r'''
analyzer:
exclude:
@@ -2207,8 +2199,8 @@
var aPubspec = convertPath('$a/pubspec.yaml');
var cPubspec = convertPath('$c/pubspec.yaml');
// create files
- resourceProvider.newFile(aPubspec, 'name: aaa');
- resourceProvider.newFile(cPubspec, 'name: ccc');
+ newFile(aPubspec, content: 'name: aaa');
+ newFile(cPubspec, content: 'name: ccc');
newFile('$a/$optionsFileName', content: r'''
analyzer:
exclude:
diff --git a/pkg/analysis_server/test/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
index 0b79983..a845792 100644
--- a/pkg/analysis_server/test/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
@@ -2,6 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
+import 'dart:io';
+
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/edit/edit_domain.dart';
import 'package:analyzer/src/generated/engine.dart';
@@ -153,6 +155,9 @@
// The test case currently drops the 'new' but does not convert the code to
// use a set literal. The code is no longer mangled, but we need to run the
// BulkFixProcessor iteratively to solve the second case.
+ if (Platform.isWindows) {
+ fail('Should not be passing on Windows, but it does');
+ }
addAnalysisOptionsFile('''
linter:
rules:
diff --git a/pkg/analysis_server/test/src/computer/highlights2_computer_test.dart b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
similarity index 97%
rename from pkg/analysis_server/test/src/computer/highlights2_computer_test.dart
rename to pkg/analysis_server/test/src/computer/highlights_computer_test.dart
index 33aa117..d251985 100644
--- a/pkg/analysis_server/test/src/computer/highlights2_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/highlights_computer_test.dart
@@ -2,7 +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:analysis_server/src/computer/computer_highlights2.dart';
+import 'package:analysis_server/src/computer/computer_highlights.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
@@ -103,7 +103,7 @@
expect(result.errors, isEmpty);
}
- var computer = DartUnitHighlightsComputer2(result.unit);
+ var computer = DartUnitHighlightsComputer(result.unit);
highlights = computer.compute();
}
}
diff --git a/pkg/analysis_server/test/src/computer/test_all.dart b/pkg/analysis_server/test/src/computer/test_all.dart
index b35ad77..1a04173 100644
--- a/pkg/analysis_server/test/src/computer/test_all.dart
+++ b/pkg/analysis_server/test/src/computer/test_all.dart
@@ -6,7 +6,7 @@
import 'closing_labels_computer_test.dart' as closing_labels_computer;
import 'folding_computer_test.dart' as folding_computer;
-import 'highlights2_computer_test.dart' as highlights2_computer;
+import 'highlights_computer_test.dart' as highlights_computer;
import 'import_elements_computer_test.dart' as import_elements_computer;
import 'imported_elements_computer_test.dart' as imported_elements_computer;
import 'outline_computer_test.dart' as outline_computer;
@@ -15,7 +15,7 @@
defineReflectiveSuite(() {
closing_labels_computer.main();
folding_computer.main();
- highlights2_computer.main();
+ highlights_computer.main();
import_elements_computer.main();
imported_elements_computer.main();
outline_computer.main();
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
index ac2dccb..b4c29ff 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/transform_set_parser_test.dart
@@ -348,6 +348,49 @@
expect(parameter.index, 2);
}
+ void test_addTypeParameter_fromPositionalArgument_variableInOuterScope() {
+ parse('''
+version: 1
+transforms:
+- title: 'Add'
+ date: 2020-09-03
+ element:
+ uris:
+ - 'test.dart'
+ class: 'A'
+ changes:
+ - kind: 'addTypeParameter'
+ index: 0
+ name: 'T'
+ extends:
+ expression: 'Object'
+ argumentValue:
+ expression: '{% t %}'
+ variables:
+ t:
+ kind: 'fragment'
+ value: 'arguments[2]'
+''');
+ var transforms = _transforms('A');
+ expect(transforms, hasLength(1));
+ var transform = transforms[0];
+ expect(transform.title, 'Add');
+ expect(transform.changes, hasLength(1));
+ var change = transform.changes[0] as AddTypeParameter;
+ expect(change.index, 0);
+ expect(change.name, 'T');
+
+ var extendsComponents = change.extendedType.components;
+ expect(extendsComponents, hasLength(1));
+ expect((extendsComponents[0] as TemplateText).text, 'Object');
+
+ var argumentComponents = change.argumentValue.components;
+ expect(argumentComponents, hasLength(1));
+ var value = _accessor(argumentComponents[0]) as ArgumentAccessor;
+ var parameter = value.parameter as PositionalParameterReference;
+ expect(parameter.index, 2);
+ }
+
void test_bulkApply() {
parse('''
version: 1
diff --git a/pkg/analyzer/CHANGELOG.md b/pkg/analyzer/CHANGELOG.md
index 3fdeadb..2e2dce1 100644
--- a/pkg/analyzer/CHANGELOG.md
+++ b/pkg/analyzer/CHANGELOG.md
@@ -10,6 +10,10 @@
* The value of`FunctionType.element` for types created from a `typedef`
is now `FunctionTypeAliasElement`, not its function element.
* Removed deprecated `GenericTypeAliasElement`.
+* Removed `PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS`.
+* Changed the default `PhysicalResourceProvider` constructor to no longer take a
+ required positional parameter (removed the existing `fileReadMode` positional
+ parameter).
## 0.40.6
* The non_nullable feature is released in 2.12 language version.
diff --git a/pkg/analyzer/lib/file_system/physical_file_system.dart b/pkg/analyzer/lib/file_system/physical_file_system.dart
index 0f0ee0d..9561ff6 100644
--- a/pkg/analyzer/lib/file_system/physical_file_system.dart
+++ b/pkg/analyzer/lib/file_system/physical_file_system.dart
@@ -55,22 +55,13 @@
/// A `dart:io` based implementation of [ResourceProvider].
class PhysicalResourceProvider implements ResourceProvider {
- static final String Function(String) NORMALIZE_EOL_ALWAYS =
- (String string) => string.replaceAll(RegExp('\r\n?'), '\n');
-
- static final PhysicalResourceProvider INSTANCE =
- PhysicalResourceProvider(null);
+ static final PhysicalResourceProvider INSTANCE = PhysicalResourceProvider();
/// The path to the base folder where state is stored.
final String _stateLocation;
- PhysicalResourceProvider(String Function(String) fileReadMode,
- {String stateLocation})
- : _stateLocation = stateLocation ?? _getStandardStateLocation() {
- if (fileReadMode != null) {
- FileBasedSource.fileReadMode = fileReadMode;
- }
- }
+ PhysicalResourceProvider({String stateLocation})
+ : _stateLocation = stateLocation ?? _getStandardStateLocation();
@override
Context get pathContext => context;
@@ -188,7 +179,7 @@
String readAsStringSync() {
_throwIfWindowsDeviceDriver();
try {
- return FileBasedSource.fileReadMode(_file.readAsStringSync());
+ return _file.readAsStringSync();
} on io.FileSystemException catch (exception) {
throw FileSystemException(exception.path, exception.message);
}
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 2a7a07a..0b776c8 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -1320,7 +1320,8 @@
// ```
//
// If the instances of the class should be mutable, then remove the
- //annotation, or choose a different superclass if the annotation is inherited:
+ // annotation, or choose a different superclass if the annotation is
+ // inherited:
//
// ```dart
// class C {
diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart
index 2188bba..fb337a8 100644
--- a/pkg/analyzer/lib/src/generated/source_io.dart
+++ b/pkg/analyzer/lib/src/generated/source_io.dart
@@ -13,9 +13,6 @@
/// Instances of the class `FileBasedSource` implement a source that represents
/// a file.
class FileBasedSource extends Source {
- /// A function that changes the way that files are read off of disk.
- static Function fileReadMode = (String s) => s;
-
/// Map from encoded URI/filepath pair to a unique integer identifier. This
/// identifier is used for equality tests and hash codes.
///
@@ -65,7 +62,7 @@
/// See [contents].
TimestampedData<String> get contentsFromFile {
return TimestampedData<String>(
- file.lastModified(), fileReadMode(file.readAsStringSync()));
+ file.lastModified(), file.readAsStringSync());
}
@override
diff --git a/pkg/analyzer/test/file_system/physical_file_system_test.dart b/pkg/analyzer/test/file_system/physical_file_system_test.dart
index 81e3c67..8f41e08 100644
--- a/pkg/analyzer/test/file_system/physical_file_system_test.dart
+++ b/pkg/analyzer/test/file_system/physical_file_system_test.dart
@@ -56,7 +56,7 @@
/// Create the resource provider to be used by the tests. Subclasses can
/// override this method to change the class of resource provider that is
/// used.
- PhysicalResourceProvider createProvider() => PhysicalResourceProvider(null);
+ PhysicalResourceProvider createProvider() => PhysicalResourceProvider();
@override
File getFile({@required bool exists, String content, String filePath}) {
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index 0706fb6..d6c1131 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/dart/sdk/sdk.dart' hide SdkLibrariesReader;
import 'package:analyzer/src/generated/java_engine_io.dart';
import 'package:analyzer/src/generated/java_io.dart';
@@ -176,48 +175,6 @@
expect(source1 == source2, isTrue);
}
- test_fileReadMode() async {
- expect(FileBasedSource.fileReadMode('a'), 'a');
- expect(FileBasedSource.fileReadMode('a\n'), 'a\n');
- expect(FileBasedSource.fileReadMode('ab'), 'ab');
- expect(FileBasedSource.fileReadMode('abc'), 'abc');
- expect(FileBasedSource.fileReadMode('a\nb'), 'a\nb');
- expect(FileBasedSource.fileReadMode('a\rb'), 'a\rb');
- expect(FileBasedSource.fileReadMode('a\r\nb'), 'a\r\nb');
- }
-
- test_fileReadMode_changed() async {
- FileBasedSource.fileReadMode = (String s) => s + 'xyz';
- expect(FileBasedSource.fileReadMode('a'), 'axyz');
- expect(FileBasedSource.fileReadMode('a\n'), 'a\nxyz');
- expect(FileBasedSource.fileReadMode('ab'), 'abxyz');
- expect(FileBasedSource.fileReadMode('abc'), 'abcxyz');
- FileBasedSource.fileReadMode = (String s) => s;
- }
-
- test_fileReadMode_normalize_eol_always() async {
- FileBasedSource.fileReadMode =
- PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS;
- expect(FileBasedSource.fileReadMode('a'), 'a');
-
- // '\n' -> '\n' as first, last and only character
- expect(FileBasedSource.fileReadMode('\n'), '\n');
- expect(FileBasedSource.fileReadMode('a\n'), 'a\n');
- expect(FileBasedSource.fileReadMode('\na'), '\na');
-
- // '\r\n' -> '\n' as first, last and only character
- expect(FileBasedSource.fileReadMode('\r\n'), '\n');
- expect(FileBasedSource.fileReadMode('a\r\n'), 'a\n');
- expect(FileBasedSource.fileReadMode('\r\na'), '\na');
-
- // '\r' -> '\n' as first, last and only character
- expect(FileBasedSource.fileReadMode('\r'), '\n');
- expect(FileBasedSource.fileReadMode('a\r'), 'a\n');
- expect(FileBasedSource.fileReadMode('\ra'), '\na');
-
- FileBasedSource.fileReadMode = (String s) => s;
- }
-
test_getFullName() async {
String fullPath = "/does/not/exist.dart";
JavaFile file = FileUtilities2.createFile(fullPath);
diff --git a/pkg/analyzer/test/src/source/source_resource_test.dart b/pkg/analyzer/test/src/source/source_resource_test.dart
index 29cbc93..5e6f3ec 100644
--- a/pkg/analyzer/test/src/source/source_resource_test.dart
+++ b/pkg/analyzer/test/src/source/source_resource_test.dart
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/file_system/file_system.dart';
-import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer/src/generated/java_engine_io.dart';
import 'package:analyzer/src/generated/sdk.dart';
import 'package:analyzer/src/generated/source.dart';
@@ -63,28 +62,6 @@
FileSource.fileReadMode = (String s) => s;
}
- void test_fileReadMode_normalize_eol_always() {
- FileSource.fileReadMode = PhysicalResourceProvider.NORMALIZE_EOL_ALWAYS;
- expect(FileSource.fileReadMode('a'), 'a');
-
- // '\n' -> '\n' as first, last and only character
- expect(FileSource.fileReadMode('\n'), '\n');
- expect(FileSource.fileReadMode('a\n'), 'a\n');
- expect(FileSource.fileReadMode('\na'), '\na');
-
- // '\r\n' -> '\n' as first, last and only character
- expect(FileSource.fileReadMode('\r\n'), '\n');
- expect(FileSource.fileReadMode('a\r\n'), 'a\n');
- expect(FileSource.fileReadMode('\r\na'), '\na');
-
- // '\r' -> '\n' as first, last and only character
- expect(FileSource.fileReadMode('\r'), '\n');
- expect(FileSource.fileReadMode('a\r'), 'a\n');
- expect(FileSource.fileReadMode('\ra'), '\na');
-
- FileSource.fileReadMode = (String s) => s;
- }
-
void test_getFullName() {
File file = getFile("/does/not/exist.dart");
FileSource source = FileSource(file);
diff --git a/pkg/analyzer/tool/diagnostics/diagnostics.md b/pkg/analyzer/tool/diagnostics/diagnostics.md
index 5b680ce..f9d33c5 100644
--- a/pkg/analyzer/tool/diagnostics/diagnostics.md
+++ b/pkg/analyzer/tool/diagnostics/diagnostics.md
@@ -5645,6 +5645,8 @@
{% endprettify %}
If the instances of the class should be mutable, then remove the
+annotation, or choose a different superclass if the annotation is
+inherited:
{% prettify dart tag=pre+code %}
class C {
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index a1dd1f5..6cb97b4 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -142,7 +142,7 @@
abstractValueStrategy = options.useTrivialAbstractValueDomain
? const TrivialAbstractValueStrategy()
: const TypeMaskStrategy();
- if (options.experimentalWrapped) {
+ if (options.experimentalWrapped || options.testMode) {
abstractValueStrategy =
WrappedAbstractValueStrategy(abstractValueStrategy);
} else if (options.experimentalPowersets) {
diff --git a/pkg/compiler/lib/src/js_backend/annotations.dart b/pkg/compiler/lib/src/js_backend/annotations.dart
index f80bbbf..8955a8d 100644
--- a/pkg/compiler/lib/src/js_backend/annotations.dart
+++ b/pkg/compiler/lib/src/js_backend/annotations.dart
@@ -102,6 +102,14 @@
14, 'downcast:check',
forFunctionsOnly: false, internalOnly: false);
+ static const PragmaAnnotation indexBoundsTrust = const PragmaAnnotation(
+ 15, 'index-bounds:trust',
+ forFunctionsOnly: false, internalOnly: false);
+
+ static const PragmaAnnotation indexBoundsCheck = const PragmaAnnotation(
+ 16, 'index-bounds:check',
+ forFunctionsOnly: false, internalOnly: false);
+
static const List<PragmaAnnotation> values = [
noInline,
tryInline,
@@ -118,6 +126,8 @@
parameterCheck,
downcastTrust,
downcastCheck,
+ indexBoundsTrust,
+ indexBoundsCheck,
];
static const Map<PragmaAnnotation, Set<PragmaAnnotation>> implies = {
@@ -319,6 +329,12 @@
///
/// If [member] is `null`, the default policy is returned.
CheckPolicy getExplicitCastCheckPolicy(MemberEntity member);
+
+ /// What should the compiler do with index bounds checks `[]`, `[]=` and
+ /// `removeLast()` operations in the body of [member].
+ ///
+ /// If [member] is `null`, the default policy is returned.
+ CheckPolicy getIndexBoundsCheckPolicy(MemberEntity member);
}
class AnnotationsDataImpl implements AnnotationsData {
@@ -330,6 +346,7 @@
final CheckPolicy _defaultImplicitDowncastCheckPolicy;
final CheckPolicy _defaultConditionCheckPolicy;
final CheckPolicy _defaultExplicitCastCheckPolicy;
+ final CheckPolicy _defaultIndexBoundsCheckPolicy;
final Map<MemberEntity, EnumSet<PragmaAnnotation>> pragmaAnnotations;
AnnotationsDataImpl(CompilerOptions options, this.pragmaAnnotations)
@@ -338,7 +355,9 @@
options.defaultImplicitDowncastCheckPolicy,
this._defaultConditionCheckPolicy = options.defaultConditionCheckPolicy,
this._defaultExplicitCastCheckPolicy =
- options.defaultExplicitCastCheckPolicy;
+ options.defaultExplicitCastCheckPolicy,
+ this._defaultIndexBoundsCheckPolicy =
+ options.defaultIndexBoundsCheckPolicy;
factory AnnotationsDataImpl.readFromDataSource(
CompilerOptions options, DataSource source) {
@@ -504,6 +523,21 @@
}
return _defaultExplicitCastCheckPolicy;
}
+
+ @override
+ CheckPolicy getIndexBoundsCheckPolicy(MemberEntity member) {
+ if (member != null) {
+ EnumSet<PragmaAnnotation> annotations = pragmaAnnotations[member];
+ if (annotations != null) {
+ if (annotations.contains(PragmaAnnotation.indexBoundsTrust)) {
+ return CheckPolicy.trusted;
+ } else if (annotations.contains(PragmaAnnotation.indexBoundsCheck)) {
+ return CheckPolicy.checked;
+ }
+ }
+ }
+ return _defaultIndexBoundsCheckPolicy;
+ }
}
class AnnotationsDataBuilder {
diff --git a/pkg/compiler/lib/src/options.dart b/pkg/compiler/lib/src/options.dart
index 378c5b4..47b0985 100644
--- a/pkg/compiler/lib/src/options.dart
+++ b/pkg/compiler/lib/src/options.dart
@@ -319,6 +319,11 @@
/// This is an internal configuration option derived from other flags.
CheckPolicy defaultExplicitCastCheckPolicy;
+ /// What should the compiler do with List index bounds checks.
+ ///
+ /// This is an internal configuration option derived from other flags.
+ CheckPolicy defaultIndexBoundsCheckPolicy;
+
/// Whether to generate code compliant with content security policy (CSP).
bool useContentSecurityPolicy = false;
@@ -610,6 +615,11 @@
} else {
defaultExplicitCastCheckPolicy = CheckPolicy.checked;
}
+ if (trustPrimitives) {
+ defaultIndexBoundsCheckPolicy = CheckPolicy.trusted;
+ } else {
+ defaultIndexBoundsCheckPolicy = CheckPolicy.checked;
+ }
if (_disableMinification) {
enableMinification = false;
diff --git a/pkg/compiler/lib/src/ssa/builder_kernel.dart b/pkg/compiler/lib/src/ssa/builder_kernel.dart
index 1032865..e1c0789 100644
--- a/pkg/compiler/lib/src/ssa/builder_kernel.dart
+++ b/pkg/compiler/lib/src/ssa/builder_kernel.dart
@@ -1981,6 +1981,8 @@
_sourceInformationBuilder.buildForInCurrent(node);
HInstruction index = localsHandler.readLocal(indexVariable,
sourceInformation: sourceInformation);
+ // No bound check is necessary on indexer as it is immediately guarded by
+ // the condition.
HInstruction value = new HIndex(array, index, type)
..sourceInformation = sourceInformation;
add(value);
@@ -4983,6 +4985,7 @@
typeArguments, sourceInformation,
isIntercepted: isIntercepted);
}
+ invoke.instructionContext = _currentFrame.member;
if (node is ir.MethodInvocation) {
invoke.isInvariant = node.isInvariant;
invoke.isBoundsSafe = node.isBoundsSafe;
diff --git a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
index 92745f6..97bb616 100644
--- a/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
+++ b/pkg/compiler/lib/src/ssa/invoke_dynamic_specializers.dart
@@ -84,6 +84,7 @@
int argumentCount = selector.argumentCount;
if (argumentCount == 0) {
if (name == 'abs') return const AbsSpecializer();
+ if (name == 'removeLast') return const RemoveLastSpecializer();
if (name == 'round') return const RoundSpecializer();
if (name == 'toInt') return const ToIntSpecializer();
if (name == 'trim') return const TrimSpecializer();
@@ -107,6 +108,27 @@
}
return const InvokeDynamicSpecializer();
}
+
+ HBoundsCheck insertBoundsCheck(HInstruction indexerNode, HInstruction array,
+ HInstruction indexArgument, JClosedWorld closedWorld) {
+ final abstractValueDomain = closedWorld.abstractValueDomain;
+ HGetLength length = HGetLength(array, abstractValueDomain.positiveIntType,
+ isAssignable: abstractValueDomain
+ .isFixedLengthJsIndexable(array.instructionType)
+ .isPotentiallyFalse);
+ indexerNode.block.addBefore(indexerNode, length);
+
+ AbstractValue type =
+ indexArgument.isPositiveInteger(abstractValueDomain).isDefinitelyTrue
+ ? indexArgument.instructionType
+ : abstractValueDomain.positiveIntType;
+ HBoundsCheck check = HBoundsCheck(indexArgument, length, array, type)
+ ..sourceInformation = indexerNode.sourceInformation;
+ indexerNode.block.addBefore(indexerNode, check);
+ // TODO(sra): This should be useful but causes some crashes. Figure out why:
+ // indexArgument.replaceAllUsersDominatedBy(indexerNode, check);
+ return check;
+ }
}
bool canBeNegativeZero(HInstruction input) {
@@ -178,8 +200,16 @@
return null;
}
}
- HIndexAssign converted = new HIndexAssign(
- closedWorld.abstractValueDomain, receiver, index, value);
+
+ HInstruction checkedIndex = index;
+ if (closedWorld.annotationsData
+ .getIndexBoundsCheckPolicy(instruction.instructionContext)
+ .isEmitted) {
+ checkedIndex =
+ insertBoundsCheck(instruction, receiver, index, closedWorld);
+ }
+ HIndexAssign converted = HIndexAssign(
+ closedWorld.abstractValueDomain, receiver, checkedIndex, value);
log?.registerIndexAssign(instruction, converted);
return converted;
}
@@ -231,36 +261,86 @@
JCommonElements commonElements,
JClosedWorld closedWorld,
OptimizationTestLog log) {
+ HInstruction receiver = instruction.getDartReceiver(closedWorld);
var abstractValueDomain = closedWorld.abstractValueDomain;
- if (instruction.inputs[1]
- .isIndexablePrimitive(abstractValueDomain)
- .isPotentiallyFalse) {
+ if (receiver.isIndexablePrimitive(abstractValueDomain).isPotentiallyFalse) {
return null;
}
- if (instruction.inputs[2]
- .isInteger(abstractValueDomain)
- .isPotentiallyFalse &&
+ HInstruction index = instruction.inputs.last;
+ if (index.isInteger(abstractValueDomain).isPotentiallyFalse &&
// TODO(johnniwinther): Support annotations on the possible targets
// and used their parameter check policy here.
closedWorld.annotationsData.getParameterCheckPolicy(null).isEmitted) {
// We want the right checked mode error.
return null;
}
- AbstractValue receiverType =
- instruction.getDartReceiver(closedWorld).instructionType;
+ AbstractValue receiverType = receiver.instructionType;
AbstractValue elementType =
AbstractValueFactory.inferredResultTypeForSelector(
instruction.selector, receiverType, results);
if (abstractValueDomain.isTypedArray(receiverType).isDefinitelyTrue) {
elementType = abstractValueDomain.excludeNull(elementType);
}
- HIndex converted =
- new HIndex(instruction.inputs[1], instruction.inputs[2], elementType);
+
+ HInstruction checkedIndex = index;
+ if (closedWorld.annotationsData
+ .getIndexBoundsCheckPolicy(instruction.instructionContext)
+ .isEmitted) {
+ checkedIndex =
+ insertBoundsCheck(instruction, receiver, index, closedWorld);
+ }
+ HIndex converted = HIndex(receiver, checkedIndex, elementType);
log?.registerIndex(instruction, converted);
return converted;
}
}
+class RemoveLastSpecializer extends InvokeDynamicSpecializer {
+ const RemoveLastSpecializer();
+
+ @override
+ HInstruction tryConvertToBuiltin(
+ HInvokeDynamic instruction,
+ HGraph graph,
+ GlobalTypeInferenceResults results,
+ JCommonElements commonElements,
+ JClosedWorld closedWorld,
+ OptimizationTestLog log) {
+ HInstruction receiver = instruction.getDartReceiver(closedWorld);
+ final abstractValueDomain = closedWorld.abstractValueDomain;
+ if (receiver.isExtendableArray(abstractValueDomain).isPotentiallyFalse) {
+ return null;
+ }
+
+ // We are essentially inlining `result = a[a.length - 1]`. `0` is the only
+ // index that can fail so we check zero directly, but we want to report the
+ // error index as `-1`, so we add `-1` as an extra input that to the check.
+ if (closedWorld.annotationsData
+ .getIndexBoundsCheckPolicy(instruction.instructionContext)
+ .isEmitted) {
+ HConstant zeroIndex = graph.addConstantInt(0, closedWorld);
+ HBoundsCheck check =
+ insertBoundsCheck(instruction, receiver, zeroIndex, closedWorld);
+ HInstruction minusOne = graph.addConstantInt(-1, closedWorld);
+ check.inputs.add(minusOne);
+ minusOne.usedBy.add(check);
+ }
+ // `Array.pop` is encoded as a non-intercepted call to `JSArray.removeLast`.
+ // TODO(sra): Add a better encoding for `Array.pop`, perhaps a HInstruction.
+ HInvokeDynamic converted = HInvokeDynamicMethod(
+ instruction.selector,
+ instruction.receiverType,
+ [receiver], // Drop interceptor.
+ instruction.instructionType,
+ instruction.typeArguments,
+ instruction.sourceInformation,
+ isIntercepted: false)
+ ..element = commonElements.jsArrayRemoveLast;
+ log?.registerRemoveLast(instruction, converted);
+ return converted;
+ }
+}
+
class BitNotSpecializer extends InvokeDynamicSpecializer {
const BitNotSpecializer();
diff --git a/pkg/compiler/lib/src/ssa/logging.dart b/pkg/compiler/lib/src/ssa/logging.dart
index e857648..d7c7e54 100644
--- a/pkg/compiler/lib/src/ssa/logging.dart
+++ b/pkg/compiler/lib/src/ssa/logging.dart
@@ -114,6 +114,10 @@
_registerSpecializer(original, converted, 'Index');
}
+ void registerRemoveLast(HInvokeDynamic original, HInvokeDynamic converted) {
+ _registerSpecializer(original, converted, 'RemoveLast');
+ }
+
void registerBitNot(HInvokeDynamic original, HBitNot converted) {
_registerSpecializer(original, converted, 'BitNot');
}
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 77c24d3..115e891 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -1389,6 +1389,15 @@
String toString() => '${this.runtimeType}()';
}
+/// An interface implemented by certain kinds of [HInstruction]. This makes it
+/// possible to discover which annotations were in force in the code from which
+/// the instruction originated.
+// TODO(sra): It would be easier to use a mostly-shared Map-like structure that
+// surfaces the ambient annotations at any point in the code.
+abstract class InstructionContext {
+ MemberEntity instructionContext;
+}
+
/// The set of uses of [source] that are dominated by [dominator].
class DominatedUses {
final HInstruction _source;
@@ -1702,7 +1711,7 @@
}
}
-abstract class HInvokeDynamic extends HInvoke {
+abstract class HInvokeDynamic extends HInvoke implements InstructionContext {
final InvokeDynamicSpecializer specializer;
Selector _selector;
@@ -1728,6 +1737,9 @@
// stub.
MemberEntity element;
+ @override
+ MemberEntity instructionContext;
+
HInvokeDynamic(Selector selector, this._receiverType, this.element,
List<HInstruction> inputs, bool isIntercepted, AbstractValue resultType)
: this._selector = selector,
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index ad03cf6..5ea6c69 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -72,8 +72,6 @@
assert(graph.isValid(), 'Graph not valid after ${phase.name}');
}
- bool trustPrimitives = _options.trustPrimitives;
- Set<HInstruction> boundsChecked = new Set<HInstruction>();
SsaCodeMotion codeMotion;
SsaLoadElimination loadElimination;
@@ -114,7 +112,6 @@
typeRecipeDomain,
registry,
log),
- new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
new SsaInstructionSimplifier(
globalInferenceResults,
_options,
@@ -123,7 +120,6 @@
typeRecipeDomain,
registry,
log),
- new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
new SsaTypePropagator(globalInferenceResults,
closedWorld.commonElements, closedWorld, log),
// Run a dead code eliminator before LICM because dead
@@ -155,7 +151,6 @@
typeRecipeDomain,
registry,
log),
- new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
];
phases.forEach(runPhase);
@@ -184,7 +179,6 @@
typeRecipeDomain,
registry,
log),
- new SsaCheckInserter(trustPrimitives, closedWorld, boundsChecked),
new SsaSimplifyInterceptors(closedWorld, member.enclosingClass),
new SsaDeadCodeEliminator(closedWorld, this),
];
@@ -676,9 +670,7 @@
if (selector.isCall || selector.isOperator) {
FunctionEntity target;
if (input.isExtendableArray(_abstractValueDomain).isDefinitelyTrue) {
- if (applies(commonElements.jsArrayRemoveLast)) {
- target = commonElements.jsArrayRemoveLast;
- } else if (applies(commonElements.jsArrayAdd)) {
+ if (applies(commonElements.jsArrayAdd)) {
// Codegen special cases array calls to `Array.push`, but does not
// inline argument type checks. We lower if the check always passes
// (due to invariance or being a top-type), or if the check is not
@@ -718,6 +710,7 @@
// bounds check on removeLast). Once we start inlining, the
// bounds check will become explicit, so we won't need this
// optimization.
+ // TODO(sra): Fix comment - SsaCheckInserter is deleted.
HInvokeDynamicMethod result = new HInvokeDynamicMethod(
node.selector,
node.receiverType,
@@ -2288,99 +2281,6 @@
}
}
-class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase {
- final Set<HInstruction> boundsChecked;
- final bool trustPrimitives;
- final JClosedWorld closedWorld;
- @override
- final String name = "SsaCheckInserter";
- HGraph graph;
-
- SsaCheckInserter(this.trustPrimitives, this.closedWorld, this.boundsChecked);
-
- AbstractValueDomain get _abstractValueDomain =>
- closedWorld.abstractValueDomain;
-
- @override
- void visitGraph(HGraph graph) {
- this.graph = graph;
-
- // In --trust-primitives mode we don't add bounds checks. This is better
- // than trying to remove them later as the limit expression would become
- // dead and require DCE.
- if (trustPrimitives) return;
-
- visitDominatorTree(graph);
- }
-
- @override
- void visitBasicBlock(HBasicBlock block) {
- HInstruction instruction = block.first;
- while (instruction != null) {
- HInstruction next = instruction.next;
- instruction = instruction.accept(this);
- instruction = next;
- }
- }
-
- HBoundsCheck insertBoundsCheck(
- HInstruction indexNode, HInstruction array, HInstruction indexArgument) {
- HGetLength length = new HGetLength(
- array, closedWorld.abstractValueDomain.positiveIntType,
- isAssignable: !isFixedLength(array.instructionType, closedWorld));
- indexNode.block.addBefore(indexNode, length);
-
- AbstractValue type =
- indexArgument.isPositiveInteger(_abstractValueDomain).isDefinitelyTrue
- ? indexArgument.instructionType
- : closedWorld.abstractValueDomain.positiveIntType;
- HBoundsCheck check = new HBoundsCheck(indexArgument, length, array, type)
- ..sourceInformation = indexNode.sourceInformation;
- indexNode.block.addBefore(indexNode, check);
- // If the index input to the bounds check was not known to be an integer
- // then we replace its uses with the bounds check, which is known to be an
- // integer. However, if the input was already an integer we don't do this
- // because putting in a check instruction might obscure the real nature of
- // the index eg. if it is a constant. The range information from the
- // BoundsCheck instruction is attached to the input directly by
- // visitBoundsCheck in the SsaValueRangeAnalyzer.
- if (indexArgument.isInteger(_abstractValueDomain).isPotentiallyFalse) {
- indexArgument.replaceAllUsersDominatedBy(indexNode, check);
- }
- boundsChecked.add(indexNode);
- return check;
- }
-
- @override
- void visitIndex(HIndex node) {
- if (boundsChecked.contains(node)) return;
- HInstruction index = node.index;
- index = insertBoundsCheck(node, node.receiver, index);
- }
-
- @override
- void visitIndexAssign(HIndexAssign node) {
- if (boundsChecked.contains(node)) return;
- HInstruction index = node.index;
- index = insertBoundsCheck(node, node.receiver, index);
- }
-
- @override
- void visitInvokeDynamicMethod(HInvokeDynamicMethod node) {
- MemberEntity element = node.element;
- if (node.isInterceptedCall) return;
- if (element != closedWorld.commonElements.jsArrayRemoveLast) return;
- if (boundsChecked.contains(node)) return;
- // `0` is the index we want to check, but we want to report `-1`, as if we
- // executed `a[a.length-1]`
- HBoundsCheck check = insertBoundsCheck(
- node, node.receiver, graph.addConstantInt(0, closedWorld));
- HInstruction minusOne = graph.addConstantInt(-1, closedWorld);
- check.inputs.add(minusOne);
- minusOne.usedBy.add(check);
- }
-}
-
class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase {
@override
final String name = "SsaDeadCodeEliminator";
@@ -3631,14 +3531,14 @@
@override
void visitIndex(HIndex instruction) {
HInstruction receiver = instruction.receiver.nonCheck();
- HInstruction existing =
- memorySet.lookupKeyedValue(receiver, instruction.index);
+ HInstruction index = instruction.index.nonCheck();
+ HInstruction existing = memorySet.lookupKeyedValue(receiver, index);
if (existing != null) {
checkNewGvnCandidates(instruction, existing);
instruction.block.rewriteWithBetterUser(instruction, existing);
instruction.block.remove(instruction);
} else {
- memorySet.registerKeyedValue(receiver, instruction.index, instruction);
+ memorySet.registerKeyedValue(receiver, index, instruction);
}
}
@@ -3646,7 +3546,7 @@
void visitIndexAssign(HIndexAssign instruction) {
HInstruction receiver = instruction.receiver.nonCheck();
memorySet.registerKeyedValueUpdate(
- receiver, instruction.index, instruction.value);
+ receiver, instruction.index.nonCheck(), instruction.value);
}
// Pure operations that do not escape their inputs.
diff --git a/pkg/compiler/test/annotations/data/directives.dart b/pkg/compiler/test/annotations/data/directives.dart
index fadf7d8..5a11424 100644
--- a/pkg/compiler/test/annotations/data/directives.dart
+++ b/pkg/compiler/test/annotations/data/directives.dart
@@ -13,6 +13,8 @@
downcastCheck();
parameterTrust();
parameterCheck();
+ indexBoundsTrust();
+ indexBoundsCheck();
}
/*member: typesTrust:types:trust*/
@@ -46,3 +48,11 @@
/*member: parameterCheck:parameter:check*/
@pragma('dart2js:parameter:check')
parameterCheck() {}
+
+/*member: indexBoundsTrust:index-bounds:trust*/
+@pragma('dart2js:index-bounds:trust')
+indexBoundsTrust() {}
+
+/*member: indexBoundsCheck:index-bounds:check*/
+@pragma('dart2js:index-bounds:check')
+indexBoundsCheck() {}
diff --git a/pkg/compiler/test/codegen/expect_annotations_test.dart b/pkg/compiler/test/codegen/expect_annotations_test.dart
index 5fc56d7..9ed3517 100644
--- a/pkg/compiler/test/codegen/expect_annotations_test.dart
+++ b/pkg/compiler/test/codegen/expect_annotations_test.dart
@@ -10,7 +10,6 @@
import 'package:compiler/src/commandline_options.dart';
import 'package:compiler/src/elements/entities.dart';
import 'package:compiler/src/inferrer/abstract_value_domain.dart';
-import 'package:compiler/src/inferrer/typemasks/masks.dart';
import 'package:compiler/src/inferrer/types.dart';
import 'package:compiler/src/world.dart' show JClosedWorld;
import '../inference/type_mask_test_helper.dart';
@@ -50,16 +49,19 @@
AbstractValueDomain commonMasks = closedWorld.abstractValueDomain;
Expect.isFalse(compiler.compilationFailed, 'Unsuccessful compilation');
- void testTypeMatch(FunctionEntity function, TypeMask expectedParameterType,
- TypeMask expectedReturnType, GlobalTypeInferenceResults results) {
+ void testTypeMatch(
+ FunctionEntity function,
+ AbstractValue expectedParameterType,
+ AbstractValue expectedReturnType,
+ GlobalTypeInferenceResults results) {
closedWorld.elementEnvironment.forEachParameterAsLocal(
closedWorld.globalLocalsMap, function, (Local parameter) {
- TypeMask type = results.resultOfParameter(parameter);
+ AbstractValue type = results.resultOfParameter(parameter);
Expect.equals(
expectedParameterType, simplify(type, commonMasks), "$parameter");
});
if (expectedReturnType != null) {
- TypeMask type = results.resultOfMember(function).returnType;
+ AbstractValue type = results.resultOfMember(function).returnType;
Expect.equals(
expectedReturnType, simplify(type, commonMasks), "$function");
}
@@ -67,8 +69,8 @@
void test(String name,
{bool expectNoInline: false,
- TypeMask expectedParameterType: null,
- TypeMask expectedReturnType: null,
+ AbstractValue expectedParameterType: null,
+ AbstractValue expectedReturnType: null,
bool expectAssumeDynamic: false}) {
LibraryEntity mainApp = closedWorld.elementEnvironment.mainLibrary;
FunctionEntity method =
diff --git a/pkg/compiler/test/inference/map_tracer_test.dart b/pkg/compiler/test/inference/map_tracer_test.dart
index 4cb4c1e..5e0ebf4 100644
--- a/pkg/compiler/test/inference/map_tracer_test.dart
+++ b/pkg/compiler/test/inference/map_tracer_test.dart
@@ -258,7 +258,7 @@
K(TypeMask other) => simplify(keyType.union(other, commonMasks), commonMasks);
V(TypeMask other) =>
- simplify(valueType.union(other, commonMasks), commonMasks).nullable();
+ simplify(valueType.union(other, commonMasks).nullable(), commonMasks);
checkType('mapInField', K(aKeyType), V(commonMasks.numType));
checkType('mapPassedToMethod', K(aKeyType), V(commonMasks.numType));
diff --git a/pkg/compiler/test/inference/type_mask_test_helper.dart b/pkg/compiler/test/inference/type_mask_test_helper.dart
index 9e5d665..ba29b08 100644
--- a/pkg/compiler/test/inference/type_mask_test_helper.dart
+++ b/pkg/compiler/test/inference/type_mask_test_helper.dart
@@ -6,19 +6,19 @@
library type_mask_test_helper;
+import 'package:compiler/src/inferrer/abstract_value_domain.dart';
import 'package:compiler/src/inferrer/typemasks/masks.dart';
import 'package:compiler/src/world.dart' show JClosedWorld;
export 'package:compiler/src/inferrer/types.dart';
-TypeMask simplify(TypeMask mask, CommonMasks commonMasks) {
- if (mask is ForwardingTypeMask) {
- return simplify(mask.forwardTo, commonMasks);
- } else if (mask is UnionTypeMask) {
- return UnionTypeMask.flatten(
- mask.disjointMasks, mask.isNullable, commonMasks);
+AbstractValue simplify(AbstractValue value, AbstractValueDomain domain) {
+ if (value is ForwardingTypeMask) {
+ return simplify(value.forwardTo, domain);
+ } else if (value is UnionTypeMask) {
+ return UnionTypeMask.flatten(value.disjointMasks, value.isNullable, domain);
} else {
- return mask;
+ return value;
}
}
diff --git a/pkg/compiler/test/optimization/data/remove_last.dart b/pkg/compiler/test/optimization/data/remove_last.dart
new file mode 100644
index 0000000..a4f073f
--- /dev/null
+++ b/pkg/compiler/test/optimization/data/remove_last.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.7
+
+import 'dart:collection';
+import 'dart:typed_data';
+
+/*member: dynamicIndex:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+dynamicIndex(var list) {
+ return list.removeLast(); // This is not known to be an indexable primitive.
+}
+
+/*member: unknownList:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+unknownList(List list) {
+ return list.removeLast(); // This is not known to be an indexable primitive.
+}
+
+/*member: possiblyNullMutableList:Specializer=[RemoveLast]*/
+@pragma('dart2js:noInline')
+possiblyNullMutableList(bool b) {
+ var list = b ? [0] : null;
+ return list.removeLast();
+}
+
+/*member: mutableList:Specializer=[RemoveLast]*/
+@pragma('dart2js:noInline')
+mutableList() {
+ var list = [0];
+ return list.removeLast();
+}
+
+/*member: typedList:Specializer=[!RemoveLast]*/
+@pragma('dart2js:noInline')
+typedList() {
+ var list = Uint8List(10);
+ return list.removeLast();
+}
+
+main() {
+ dynamicIndex([]);
+ dynamicIndex({});
+ unknownList([]);
+ unknownList(new MyList());
+ possiblyNullMutableList(true);
+ possiblyNullMutableList(false);
+ mutableList();
+ typedList();
+}
+
+class MyList<E> extends ListBase<E> {
+ E operator [](int index) => null;
+ void operator []=(int index, E value) {}
+ int get length => 0;
+ void set length(int value) {}
+}
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 3ded01d..9bf97f5 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -496,7 +496,7 @@
///
/// This class may be used directly by clients that with to run migration but
/// provide their own command-line interface.
-class MigrationCliRunner {
+class MigrationCliRunner implements DartFixListenerClient {
final MigrationCli cli;
/// Logger instance we use to give feedback to the user.
@@ -613,6 +613,55 @@
sdkPath: sdkPath);
}
+ @override
+ void onException(String detail) {
+ if (_hasExceptions) return;
+ _hasExceptions = true;
+ if (options.ignoreExceptions) {
+ logger.stdout('''
+Exception(s) occurred during migration. Attempting to perform
+migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}.
+
+To see exception details, re-run without --${CommandLineOptions.ignoreExceptionsFlag}.
+''');
+ } else {
+ if (_hasAnalysisErrors) {
+ logger.stderr('''
+Aborting migration due to an exception. This may be due to a bug in
+the migration tool, or it may be due to errors in the source code
+being migrated. If possible, try to fix errors in the source code and
+re-try migrating. If that doesn't work, consider filing a bug report
+at:
+''');
+ } else {
+ logger.stderr('''
+Aborting migration due to an exception. This most likely is due to a
+bug in the migration tool. Please consider filing a bug report at:
+''');
+ }
+ logger.stderr('https://github.com/dart-lang/sdk/issues/new');
+ logger.stderr('''
+To attempt to perform migration anyway, you may re-run with
+--${CommandLineOptions.ignoreExceptionsFlag}.
+
+Exception details:
+''');
+ logger.stderr(detail);
+ throw MigrationExit(1);
+ }
+ }
+
+ @override
+ void onFatalError(String detail) {
+ logger.stderr(detail);
+ throw MigrationExit(1);
+ }
+
+ @override
+ void onMessage(String detail) {
+ logger.stdout(detail);
+ }
+
/// Runs the full migration process.
///
/// If something goes wrong, a message is printed using the logger configured
@@ -636,10 +685,8 @@
logger.stdout(ansi.emphasized('Analyzing project...'));
_fixCodeProcessor = _FixCodeProcessor(context, this);
- _dartFixListener = DartFixListener(
- DriverProviderImpl(resourceProvider, context),
- _exceptionReported,
- _fatalErrorReported);
+ _dartFixListener =
+ DartFixListener(DriverProviderImpl(resourceProvider, context), this);
nonNullableFix = createNonNullableFix(_dartFixListener, resourceProvider,
_fixCodeProcessor.getLineInfo, computeBindAddress(),
included: [options.directory],
@@ -807,48 +854,6 @@
}
}
- void _exceptionReported(String detail) {
- if (_hasExceptions) return;
- _hasExceptions = true;
- if (options.ignoreExceptions) {
- logger.stdout('''
-Exception(s) occurred during migration. Attempting to perform
-migration anyway due to the use of --${CommandLineOptions.ignoreExceptionsFlag}.
-
-To see exception details, re-run without --${CommandLineOptions.ignoreExceptionsFlag}.
-''');
- } else {
- if (_hasAnalysisErrors) {
- logger.stderr('''
-Aborting migration due to an exception. This may be due to a bug in
-the migration tool, or it may be due to errors in the source code
-being migrated. If possible, try to fix errors in the source code and
-re-try migrating. If that doesn't work, consider filing a bug report
-at:
-''');
- } else {
- logger.stderr('''
-Aborting migration due to an exception. This most likely is due to a
-bug in the migration tool. Please consider filing a bug report at:
-''');
- }
- logger.stderr('https://github.com/dart-lang/sdk/issues/new');
- logger.stderr('''
-To attempt to perform migration anyway, you may re-run with
---${CommandLineOptions.ignoreExceptionsFlag}.
-
-Exception details:
-''');
- logger.stderr(detail);
- throw MigrationExit(1);
- }
- }
-
- void _fatalErrorReported(String detail) {
- logger.stderr(detail);
- throw MigrationExit(1);
- }
-
void _logErrors(AnalysisResult analysisResult) {
logger.stdout('');
@@ -912,6 +917,7 @@
_fixCodeProcessor._task.includedRoot,
_dartFixListener,
_fixCodeProcessor._task.instrumentationListener,
+ {},
analysisResult);
} else {
logger.stdout(ansi.emphasized('Re-generating migration suggestions...'));
@@ -1044,9 +1050,6 @@
}
Future<AnalysisResult> runFirstPhase() async {
- // Process package
- _task.processPackage(context.contextRoot.root);
-
var analysisErrors = <AnalysisError>[];
// All tasks should be registered; [numPhases] should be finalized.
@@ -1107,6 +1110,7 @@
_migrationCli.logger.stdout(_migrationCli.ansi
.emphasized('Compiling instrumentation information...'));
var state = await _task.finish();
+ _task.processPackage(context.contextRoot.root, state.neededPackages);
if (_migrationCli.options.webPreview) {
await _task.startPreviewServer(state, _migrationCli.applyHook);
}
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index af33d0d..25499a8 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -12,6 +12,7 @@
import 'package:meta/meta.dart';
import 'package:nnbd_migration/instrumentation.dart';
import 'package:nnbd_migration/src/nullability_migration_impl.dart';
+import 'package:pub_semver/pub_semver.dart';
export 'package:nnbd_migration/src/utilities/hint_utils.dart' show HintComment;
@@ -314,18 +315,12 @@
/// Optional parameter [warnOnWeakCode] indicates whether weak-only code
/// should be warned about or removed (in the way specified by
/// [removeViaComments]).
- ///
- /// Optional parameter [transformWhereOrNull] indicates whether Iterable
- /// methods should be transformed to their "OrNull" equivalents when possible.
- /// This feature is a work in progress, so by default they are not
- /// transformed.
factory NullabilityMigration(NullabilityMigrationListener listener,
LineInfo Function(String) getLineInfo,
{bool permissive,
NullabilityMigrationInstrumentation instrumentation,
bool removeViaComments,
- bool warnOnWeakCode,
- bool transformWhereOrNull}) = NullabilityMigrationImpl;
+ bool warnOnWeakCode}) = NullabilityMigrationImpl;
/// Check if this migration is being run permissively.
bool get isPermissive;
@@ -337,7 +332,13 @@
void finalizeInput(ResolvedUnitResult result);
- void finish();
+ /// Finishes the migration. Returns a map indicating packages that have been
+ /// newly imported by the migration; the caller should ensure that these
+ /// packages are properly imported by the package's pubspec.
+ ///
+ /// Keys of the returned map are package names; values indicate the minimum
+ /// required version of each package.
+ Map<String, Version> finish();
void prepareInput(ResolvedUnitResult result);
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 3611061..f3875f3 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -217,7 +217,7 @@
final Map<Token, HintComment> _nullCheckHints = {};
/// Helper that assists us in transforming Iterable methods to their "OrNull"
- /// equivalents, or `null` if we are not doing such transformations.
+ /// equivalents.
final WhereOrNullTransformer _whereOrNullTransformer;
/// Deferred processing that should be performed once we have finished
@@ -225,20 +225,12 @@
final Map<MethodInvocation, DecoratedType Function(DecoratedType)>
_deferredMethodInvocationProcessing = {};
- EdgeBuilder(
- this.typeProvider,
- this._typeSystem,
- this._variables,
- this._graph,
- this.source,
- this.listener,
- this._decoratedClassHierarchy,
- bool transformWhereOrNull,
+ EdgeBuilder(this.typeProvider, this._typeSystem, this._variables, this._graph,
+ this.source, this.listener, this._decoratedClassHierarchy,
{this.instrumentation})
: _inheritanceManager = InheritanceManager3(),
- _whereOrNullTransformer = transformWhereOrNull
- ? WhereOrNullTransformer(typeProvider, _typeSystem)
- : null;
+ _whereOrNullTransformer =
+ WhereOrNullTransformer(typeProvider, _typeSystem);
/// Gets the decorated type of [element] from [_variables], performing any
/// necessary substitutions.
@@ -2304,7 +2296,7 @@
}
} else {
var transformationInfo =
- _whereOrNullTransformer?.tryTransformOrElseArgument(expression);
+ _whereOrNullTransformer.tryTransformOrElseArgument(expression);
if (transformationInfo != null) {
// Don't build any edges for this argument; if necessary we'll transform
// it rather than make things nullable. But do save the nullability of
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index 32b2777..ae77229 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -40,6 +40,7 @@
import 'package:nnbd_migration/src/utilities/resolution_utils.dart';
import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart';
import 'package:nnbd_migration/src/variables.dart';
+import 'package:pub_semver/pub_semver.dart';
bool _isIncrementOrDecrementOperator(TokenType tokenType) {
switch (tokenType) {
@@ -121,7 +122,7 @@
final NullabilityGraph _graph;
/// Helper that assists us in transforming Iterable methods to their "OrNull"
- /// equivalents, or `null` if we are not doing such transformations.
+ /// equivalents.
final WhereOrNullTransformer _whereOrNullTransformer;
/// Indicates whether an import of package:collection's `IterableExtension`
@@ -129,6 +130,11 @@
@visibleForTesting
bool needsIterableExtension = false;
+ /// Map of additional package dependencies that will be required by the
+ /// migrated code. Keys are package names; values indicate the minimum
+ /// required version of each package.
+ final Map<String, Version> _neededPackages;
+
factory FixBuilder(
Source source,
DecoratedClassHierarchy decoratedClassHierarchy,
@@ -140,7 +146,7 @@
CompilationUnit unit,
bool warnOnWeakCode,
NullabilityGraph graph,
- bool transformWhereOrNull) {
+ Map<String, Version> neededPackages) {
var migrationResolutionHooks = MigrationResolutionHooksImpl();
return FixBuilder._(
decoratedClassHierarchy,
@@ -156,7 +162,7 @@
migrationResolutionHooks,
warnOnWeakCode,
graph,
- transformWhereOrNull);
+ neededPackages);
}
FixBuilder._(
@@ -170,11 +176,10 @@
this.migrationResolutionHooks,
this.warnOnWeakCode,
this._graph,
- bool transformWhereOrNull)
+ this._neededPackages)
: typeProvider = _typeSystem.typeProvider,
- _whereOrNullTransformer = transformWhereOrNull
- ? WhereOrNullTransformer(_typeSystem.typeProvider, _typeSystem)
- : null {
+ _whereOrNullTransformer =
+ WhereOrNullTransformer(_typeSystem.typeProvider, _typeSystem) {
migrationResolutionHooks._fixBuilder = this;
assert(_typeSystem.isNonNullableByDefault);
assert((typeProvider as TypeProviderImpl).isNonNullableByDefault);
@@ -640,10 +645,12 @@
InferenceContext.getContext(ancestor) ?? DynamicTypeImpl.instance;
if (!_fixBuilder._typeSystem.isSubtypeOf(type, context)) {
var transformationInfo =
- _fixBuilder._whereOrNullTransformer?.tryTransformOrElseArgument(node);
+ _fixBuilder._whereOrNullTransformer.tryTransformOrElseArgument(node);
if (transformationInfo != null) {
// We can fix this by dropping the node and changing the method call.
_fixBuilder.needsIterableExtension = true;
+ _fixBuilder._neededPackages['collection'] =
+ Version.parse('1.15.0-nullsafety.4');
var info = AtomicEditInfo(
NullabilityFixDescription.changeMethodName(
transformationInfo.originalName,
diff --git a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
index 1a4e769..4c6fa9c 100644
--- a/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/dartfix_listener.dart
@@ -7,6 +7,7 @@
hide AnalysisError;
import 'package:meta/meta.dart';
import 'package:nnbd_migration/src/front_end/driver_provider_impl.dart';
+import 'package:pub_semver/src/version.dart';
class DartFixListener {
final DriverProviderImpl server;
@@ -15,14 +16,9 @@
final List<DartFixSuggestion> suggestions = [];
- /// Add the given [detail] to the list of details to be returned to the
- /// client.
- final void Function(String detail) reportException;
+ final DartFixListenerClient client;
- /// Callback that reports a fatal error to the client.
- final void Function(String detail) reportFatalError;
-
- DartFixListener(this.server, this.reportException, this.reportFatalError);
+ DartFixListener(this.server, this.client);
/// Record an edit to be sent to the client.
///
@@ -49,6 +45,13 @@
suggestions.add(DartFixSuggestion(description, location: location));
}
+ /// Reports to then user that they need to run `dart pub get` after the
+ /// migration finishes.
+ void reportPubGetNeeded(Map<String, Version> neededPackages) {
+ client.onMessage(
+ 'Your pubspec has been updated. Please run `dart pub get`.');
+ }
+
/// Reset this listener so that it can accrue a new set of changes.
void reset() {
suggestions.clear();
@@ -60,6 +63,18 @@
}
}
+abstract class DartFixListenerClient {
+ /// Add the given [detail] to the list of details to be returned to the
+ /// client.
+ void onException(String detail);
+
+ /// Callback that reports a fatal error to the client.
+ void onFatalError(String detail);
+
+ /// Reports the given [detail] message to the client; not an error condition.
+ void onMessage(String detail);
+}
+
class DartFixSuggestion {
final String description;
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
index bfbf7cd..016c204 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
@@ -11,6 +11,7 @@
import 'package:nnbd_migration/src/front_end/instrumentation_listener.dart';
import 'package:nnbd_migration/src/front_end/migration_info.dart';
import 'package:nnbd_migration/src/front_end/path_mapper.dart';
+import 'package:pub_semver/src/version.dart';
/// The state of an NNBD migration.
class MigrationState {
@@ -44,10 +45,16 @@
/*late*/ List<String> previewUrls;
+ /// Map of additional package dependencies that will be required by the
+ /// migrated code. Keys are package names; values indicate the minimum
+ /// required version of each package.
+ final Map<String, Version> neededPackages;
+
/// Initialize a newly created migration state with the given values.
MigrationState(this.migration, this.includedRoot, this.listener,
- this.instrumentationListener,
- [this.analysisResult]);
+ this.instrumentationListener, this.neededPackages,
+ [this.analysisResult])
+ : assert(neededPackages != null);
/// If the migration has been applied to disk.
bool get hasBeenApplied => _hasBeenApplied;
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index a691876..7b21c71 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -105,9 +105,9 @@
}
Future<MigrationState> finish() async {
- migration.finish();
- final state = MigrationState(
- migration, includedRoot, listener, instrumentationListener);
+ var neededPackages = migration.finish();
+ final state = MigrationState(migration, includedRoot, listener,
+ instrumentationListener, neededPackages);
await state.refresh(_logger);
return state;
}
@@ -120,7 +120,11 @@
///
/// This means updating the pubspec.yaml file, the package_config.json
/// file, and the analysis_options.yaml file, each only if necessary.
- void processPackage(Folder pkgFolder) {
+ ///
+ /// [neededPackages] is a map whose keys are the names of packages that should
+ /// be dependend upon by the package's pubspec, and whose values are the
+ /// minimum required versions of those packages.
+ void processPackage(Folder pkgFolder, Map<String, Version> neededPackages) {
var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml');
if (!pubspecFile.exists) {
// If the pubspec file cannot be found, we do not attempt to change the
@@ -139,7 +143,7 @@
return;
}
- var updated = _processPubspec(pubspec);
+ var updated = _processPubspec(pubspec, neededPackages);
if (updated) {
_processConfigFile(pkgFolder, pubspec);
}
@@ -280,7 +284,12 @@
/// Updates the pubspec.yaml file to specify a minimum Dart SDK version which
/// supports null safety.
- bool _processPubspec(_YamlFile pubspec) {
+ ///
+ /// Return value indicates whether the user's `package_config.json` file
+ /// should be updated.
+ bool _processPubspec(_YamlFile pubspec, Map<String, Version> neededPackages) {
+ bool packageConfigNeedsUpdate = false;
+ bool packageDepsUpdated = false;
var pubspecMap = pubspec.content;
YamlNode environmentOptions;
if (pubspecMap is YamlMap) {
@@ -291,46 +300,56 @@
var content = '''
environment:
sdk: '$_intendedSdkVersionConstraint'
-
''';
pubspec._insertAfterParent(
SourceSpan(start, start, ''), content, listener);
+ packageConfigNeedsUpdate = true;
} else if (environmentOptions is YamlMap) {
- var sdk = environmentOptions.nodes['sdk'];
- if (sdk == null) {
- var content = """
-
- sdk: '$_intendedSdkVersionConstraint'""";
- pubspec._insertAfterParent(environmentOptions.span, content, listener);
- } else if (sdk is YamlScalar) {
- VersionConstraint currentConstraint;
- if (sdk.value is String) {
- currentConstraint = VersionConstraint.parse(sdk.value as String);
- if (currentConstraint is VersionRange &&
- currentConstraint.min >= _intendedMinimumSdkVersion) {
- // The current SDK version constraint already enables Null Safety.
- // Do not edit pubspec.yaml, nor package_config.json.
- return false;
- } else {
- // TODO(srawlins): This overwrites the current maximum version. In
- // the uncommon situation that the maximum is not '<3.0.0', it
- // should not.
- pubspec._replaceSpan(
- sdk.span, "'$_intendedSdkVersionConstraint'", listener);
+ if (_updatePubspecConstraint(pubspec, environmentOptions, 'sdk',
+ "'$_intendedSdkVersionConstraint'", _intendedMinimumSdkVersion)) {
+ packageConfigNeedsUpdate = true;
+ }
+ } else {
+ // Odd malformed pubspec. Leave it alone, but go ahead and update the
+ // package_config.json file.
+ packageConfigNeedsUpdate = true;
+ }
+ if (neededPackages.isNotEmpty) {
+ YamlNode dependencies;
+ if (pubspecMap is YamlMap) {
+ dependencies = pubspecMap.nodes['dependencies'];
+ }
+ if (dependencies == null) {
+ var depLines = [
+ for (var entry in neededPackages.entries)
+ ' ${entry.key}: ^${entry.value}'
+ ];
+ var start = SourceLocation(0, line: 0, column: 0);
+ var content = '''
+dependencies:
+${depLines.join('\n')}
+''';
+ pubspec._insertAfterParent(
+ SourceSpan(start, start, ''), content, listener);
+ packageDepsUpdated = true;
+ } else if (dependencies is YamlMap) {
+ for (var neededPackage in neededPackages.entries) {
+ if (_updatePubspecConstraint(pubspec, dependencies, neededPackage.key,
+ '^${neededPackage.value}', neededPackage.value)) {
+ packageDepsUpdated = true;
}
- } else {
- // Something is odd with the SDK constraint we've found in
- // pubspec.yaml; Best to leave it alone.
- return false;
}
}
}
+ if (packageDepsUpdated) {
+ listener.reportPubGetNeeded(neededPackages);
+ }
- return true;
+ return packageConfigNeedsUpdate;
}
void _processPubspecException(String action, String pubspecPath, error) {
- listener.reportFatalError('''Failed to $action pubspec file
+ listener.client.onFatalError('''Failed to $action pubspec file
$pubspecPath
$error
@@ -343,6 +362,48 @@
throw StateError('listener.reportFatalError should never return');
}
+ /// Updates a constraint in the given [pubspec] file. If [key] is found in
+ /// [map], and the corresponding value does has a minimum less than
+ /// [minimumVersion], it is updated to [fullVersionConstraint]. If it is not
+ /// found, then an entry is added.
+ ///
+ /// Return value indicates whether a change was made.
+ bool _updatePubspecConstraint(_YamlFile pubspec, YamlMap map, String key,
+ String fullVersionConstraint, Version minimumVersion) {
+ var node = map.nodes[key];
+ if (node == null) {
+ var content = '''
+
+ $key: $fullVersionConstraint''';
+ pubspec._insertAfterParent(map.span, content, listener);
+ return true;
+ } else if (node is YamlScalar) {
+ VersionConstraint currentConstraint;
+ if (node.value is String) {
+ currentConstraint = VersionConstraint.parse(node.value as String);
+ if (currentConstraint is VersionRange &&
+ currentConstraint.min >= minimumVersion) {
+ // The current version constraint is already up to date. Do not edit.
+ return false;
+ } else {
+ // TODO(srawlins): This overwrites the current maximum version. In
+ // the uncommon situation that there is a special maximum, it should
+ // not.
+ pubspec._replaceSpan(node.span, fullVersionConstraint, listener);
+ return true;
+ }
+ } else {
+ // Something is odd with the constraint we've found in pubspec.yaml;
+ // Best to leave it alone.
+ return false;
+ }
+ } else {
+ // Something is odd with the format of pubspec.yaml; best to leave it
+ // alone.
+ return false;
+ }
+ }
+
/// Allows unit tests to shut down any rogue servers that have been started,
/// so that unit testing can complete.
@visibleForTesting
@@ -449,7 +510,7 @@
@override
void reportException(
Source source, AstNode node, Object exception, StackTrace stackTrace) {
- listener.reportException('''
+ listener.client.onException('''
$exception
$stackTrace''');
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 81a3079..6c89f59 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -23,6 +23,7 @@
import 'package:nnbd_migration/src/nullability_node.dart';
import 'package:nnbd_migration/src/postmortem_file.dart';
import 'package:nnbd_migration/src/variables.dart';
+import 'package:pub_semver/pub_semver.dart';
/// Implementation of the [NullabilityMigration] public API.
class NullabilityMigrationImpl implements NullabilityMigration {
@@ -60,10 +61,6 @@
final LineInfo Function(String) _getLineInfo;
- /// Indicates whether we should transform iterable methods taking an "orElse"
- /// parameter into their "OrNull" equivalents if possible.
- final bool transformWhereOrNull;
-
/// Map from [Source] object to a boolean indicating whether the source is
/// opted in to null safety.
final Map<Source, bool> _libraryOptInStatus = {};
@@ -71,6 +68,11 @@
/// Indicates whether the client has used the [unmigratedDependencies] getter.
bool _queriedUnmigratedDependencies = false;
+ /// Map of additional package dependencies that will be required by the
+ /// migrated code. Keys are package names; values indicate the minimum
+ /// required version of each package.
+ final Map<String, Version> _neededPackages = {};
+
/// Prepares to perform nullability migration.
///
/// If [permissive] is `true`, exception handling logic will try to proceed
@@ -84,18 +86,12 @@
/// Optional parameter [warnOnWeakCode] indicates whether weak-only code
/// should be warned about or removed (in the way specified by
/// [removeViaComments]).
- ///
- /// Optional parameter [transformWhereOrNull] indicates whether Iterable
- /// methods should be transformed to their "OrNull" equivalents when possible.
- /// This feature is a work in progress, so by default they are not
- /// transformed.
NullabilityMigrationImpl(NullabilityMigrationListener listener,
LineInfo Function(String) getLineInfo,
{bool permissive = false,
NullabilityMigrationInstrumentation instrumentation,
bool removeViaComments = false,
- bool warnOnWeakCode = true,
- bool transformWhereOrNull = true})
+ bool warnOnWeakCode = true})
: this._(
listener,
NullabilityGraph(instrumentation: instrumentation),
@@ -103,8 +99,7 @@
instrumentation,
removeViaComments,
warnOnWeakCode,
- getLineInfo,
- transformWhereOrNull);
+ getLineInfo);
NullabilityMigrationImpl._(
this.listener,
@@ -113,8 +108,7 @@
this._instrumentation,
this.removeViaComments,
this.warnOnWeakCode,
- this._getLineInfo,
- this.transformWhereOrNull) {
+ this._getLineInfo) {
_instrumentation?.immutableNodes(_graph.never, _graph.always);
_postmortemFileWriter?.graph = _graph;
}
@@ -169,7 +163,7 @@
unit,
warnOnWeakCode,
_graph,
- transformWhereOrNull);
+ _neededPackages);
try {
DecoratedTypeParameterBounds.current = _decoratedTypeParameterBounds;
fixBuilder.visitAll();
@@ -196,7 +190,7 @@
}
}
- void finish() {
+ Map<String, Version> finish() {
if (!_propagated) {
// [finalizeInput] sets this field to `true`, so if it's still false, that
// means it was never called; this probably means that all the code fed
@@ -205,6 +199,7 @@
}
_postmortemFileWriter?.write();
_instrumentation?.finished();
+ return _neededPackages;
}
void prepareInput(ResolvedUnitResult result) {
@@ -260,7 +255,6 @@
unit.declaredElement.source,
_permissive ? listener : null,
_decoratedClassHierarchy,
- transformWhereOrNull,
instrumentation: _instrumentation));
} finally {
DecoratedTypeParameterBounds.current = null;
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 1e86235..1eb3737 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -60,8 +60,7 @@
var migration = NullabilityMigration(listener, getLineInfo,
permissive: _usePermissiveMode,
removeViaComments: removeViaComments,
- warnOnWeakCode: warnOnWeakCode,
- transformWhereOrNull: true);
+ warnOnWeakCode: warnOnWeakCode);
for (var path in input.keys) {
if (!(session.getFile(path)).isPart) {
for (var unit in (await session.getResolvedLibrary(path)).units) {
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 29e39622..7989905 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -3538,8 +3538,7 @@
null,
scope.thisOrAncestorOfType<CompilationUnit>(),
warnOnWeakCode,
- graph,
- true);
+ graph, {});
}
bool _isInScope(AstNode node, AstNode scope) {
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index 0ca5ea4..b941ca3 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -17,8 +17,25 @@
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
-import 'analysis_abstract.dart';
import '../utilities/test_logger.dart';
+import 'analysis_abstract.dart';
+
+class ListenerClient implements DartFixListenerClient {
+ @override
+ void onException(String detail) {
+ fail('Unexpected call to onException($detail)');
+ }
+
+ @override
+ void onFatalError(String detail) {
+ fail('Unexpected call to onFatalError($detail)');
+ }
+
+ @override
+ void onMessage(String detail) {
+ fail('Unexpected call to onMessage($detail)');
+ }
+}
@reflectiveTest
class NnbdMigrationTestBase extends AbstractAnalysisTest {
@@ -201,16 +218,14 @@
// Compute the analysis results.
var server = DriverProviderImpl(resourceProvider, driver.analysisContext);
// Run the migration engine.
- var listener =
- DartFixListener(server, _exceptionReported, _exceptionReported);
+ var listener = DartFixListener(server, ListenerClient());
var instrumentationListener = InstrumentationListener();
var adapter = NullabilityMigrationAdapter(listener);
var migration = NullabilityMigration(adapter, getLineInfo,
permissive: false,
instrumentation: instrumentationListener,
removeViaComments: removeViaComments,
- warnOnWeakCode: warnOnWeakCode,
- transformWhereOrNull: true);
+ warnOnWeakCode: warnOnWeakCode);
Future<void> _forEachPath(
void Function(ResolvedUnitResult) callback) async {
for (var testPath in testPaths) {
@@ -231,8 +246,4 @@
migration, nodeMapper, logger);
infos = await builder.explainMigration();
}
-
- void _exceptionReported(String detail) {
- fail('Unexpected error during migration: $detail');
- }
}
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index ce9eb5f..9648403 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -1694,6 +1694,101 @@
}
}
+ test_pubspec_add_collection_dependency() async {
+ var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+ => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.6.0 <3.0.0'
+dependencies:
+ foo: ^1.2.3
+''');
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ expect(
+ logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+ => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.12.0 <3.0.0'
+dependencies:
+ foo: ^1.2.3
+ collection: ^1.15.0-nullsafety.4
+'''));
+ }
+
+ test_pubspec_add_dependency_and_environment_sections() async {
+ var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+ => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+''');
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ expect(
+ logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+ => x.firstWhereOrNull((x) => x.isEven);
+''',
+ // Note: section order is weird, but it's valid and this is a rare use
+ // case.
+ pubspecText: '''
+environment:
+ sdk: '>=2.12.0 <3.0.0'
+dependencies:
+ collection: ^1.15.0-nullsafety.4
+name: test
+'''));
+ }
+
+ test_pubspec_add_dependency_section() async {
+ var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+ => x.firstWhere((x) => x.isEven, orElse: () => null);
+''');
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ expect(
+ logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+ // The Dart source code should still be migrated.
+ assertProjectContents(projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+ => x.firstWhereOrNull((x) => x.isEven);
+''',
+ // Note: `dependencies` section is in a weird place, but it's valid and
+ // this is a rare use case.
+ pubspecText: '''
+dependencies:
+ collection: ^1.15.0-nullsafety.4
+name: test
+environment:
+ sdk: '>=2.12.0 <3.0.0'
+'''));
+ }
+
test_pubspec_does_not_exist() async {
var projectContents = simpleProject()..remove('pubspec.yaml');
var projectDir = createProjectDir(projectContents);
@@ -1801,7 +1896,6 @@
'''
environment:
sdk: '>=2.12.0 <3.0.0'
-
name: test
'''));
}
@@ -1817,6 +1911,72 @@
expect(message, contains('Failed to parse pubspec file'));
}
+ test_pubspec_preserve_collection_dependency() async {
+ var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+ => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.6.0 <3.0.0'
+dependencies:
+ collection: ^1.16.0
+''');
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ expect(logger.stdoutBuffer.toString(),
+ isNot(contains('Please run `dart pub get`')));
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+ => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.12.0 <3.0.0'
+dependencies:
+ collection: ^1.16.0
+'''));
+ }
+
+ test_pubspec_update_collection_dependency() async {
+ var projectContents = simpleProject(sourceText: '''
+int firstEven(Iterable<int> x)
+ => x.firstWhere((x) => x.isEven, orElse: () => null);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.6.0 <3.0.0'
+dependencies:
+ collection: ^1.14.0
+''');
+ var projectDir = createProjectDir(projectContents);
+ var cliRunner = _createCli()
+ .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
+ await cliRunner.run();
+ expect(
+ logger.stdoutBuffer.toString(), contains('Please run `dart pub get`'));
+ // The Dart source code should still be migrated.
+ assertProjectContents(
+ projectDir, simpleProject(migrated: true, sourceText: '''
+import 'package:collection/collection.dart' show IterableExtension;
+
+int? firstEven(Iterable<int> x)
+ => x.firstWhereOrNull((x) => x.isEven);
+''', pubspecText: '''
+name: test
+environment:
+ sdk: '>=2.12.0 <3.0.0'
+dependencies:
+ collection: ^1.15.0-nullsafety.4
+'''));
+ }
+
test_pubspec_with_sdk_version_beta() async {
var projectDir = createProjectDir(simpleProject());
var cliRunner = _createCli(sdkVersion: '2.12.0-1.2.beta')
diff --git a/pkg/nnbd_migration/test/migration_visitor_test_base.dart b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
index f853f96..5ed5feb 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test_base.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test_base.dart
@@ -149,7 +149,7 @@
var unit = await super.analyze(code);
decoratedClassHierarchy = DecoratedClassHierarchy(variables, graph);
unit.accept(EdgeBuilder(typeProvider, typeSystem, variables, graph,
- testSource, null, decoratedClassHierarchy, true));
+ testSource, null, decoratedClassHierarchy));
return unit;
}
}
diff --git a/pkg/nnbd_migration/test/preview/preview_site_test.dart b/pkg/nnbd_migration/test/preview/preview_site_test.dart
index 43fd177..3f9ce89 100644
--- a/pkg/nnbd_migration/test/preview/preview_site_test.dart
+++ b/pkg/nnbd_migration/test/preview/preview_site_test.dart
@@ -36,11 +36,10 @@
}
void setUp() {
- dartfixListener =
- DartFixListener(null, _exceptionReported, _exceptionReported);
+ dartfixListener = DartFixListener(null, ListenerClient());
resourceProvider = MemoryResourceProvider();
final migrationInfo = MigrationInfo({}, {}, null, null);
- state = MigrationState(null, null, dartfixListener, null);
+ state = MigrationState(null, null, dartfixListener, null, {});
state.pathMapper = PathMapper(resourceProvider);
state.migrationInfo = migrationInfo;
site = PreviewSite(state, () async {
@@ -166,10 +165,6 @@
expect(file.readAsStringSync(), currentContent);
expect(state.hasBeenApplied, false);
}
-
- void _exceptionReported(String detail) {
- fail('Unexpected error during migration: $detail');
- }
}
mixin PreviewSiteTestMixin {
@@ -191,10 +186,9 @@
@override
void setUp() {
super.setUp();
- dartfixListener =
- DartFixListener(null, _exceptionReported, _exceptionReported);
+ dartfixListener = DartFixListener(null, ListenerClient());
final migrationInfo = MigrationInfo({}, {}, null, null);
- state = MigrationState(null, null, dartfixListener, null);
+ state = MigrationState(null, null, dartfixListener, null, {});
nodeMapper = state.nodeMapper;
state.pathMapper = PathMapper(resourceProvider);
state.migrationInfo = migrationInfo;
@@ -339,8 +333,4 @@
expect(state.hasBeenApplied, false);
expect(state.needsRerun, true);
}
-
- void _exceptionReported(String detail) {
- fail('Unexpected error during migration: $detail');
- }
}
diff --git a/runtime/observatory/tests/service/service.status b/runtime/observatory/tests/service/service.status
index f95c71d..bc2bb42 100644
--- a/runtime/observatory/tests/service/service.status
+++ b/runtime/observatory/tests/service/service.status
@@ -11,7 +11,6 @@
pause_on_start_and_exit_with_child_test: Pass, RuntimeError # Issue 33049
reload_sources_test: Pass, Slow # Reload is slow on the bots
valid_source_locations_test: Pass, Slow # Generally slow, even in release-x64.
-validate_timer_port_behavior_test: Skip # Issue 44166
[ $arch == arm ]
process_service_test: Pass, Fail # Issue 24344
diff --git a/runtime/observatory/tests/service/service_kernel.status b/runtime/observatory/tests/service/service_kernel.status
index 8c09f06..d8996be 100644
--- a/runtime/observatory/tests/service/service_kernel.status
+++ b/runtime/observatory/tests/service/service_kernel.status
@@ -187,6 +187,7 @@
step_through_switch_test: SkipByDesign
step_through_switch_with_continue_test: SkipByDesign
valid_source_locations_test: Skip, Timeout
+validate_timer_port_behavior_test: SkipByDesign, Timeout # Debugger disabled in AOT
vm_timeline_flags_test: Skip, Timeout
weak_properties_test: CompileTimeError
yield_positions_with_finally_test: SkipByDesign
diff --git a/runtime/observatory_2/tests/service_2/service_2.status b/runtime/observatory_2/tests/service_2/service_2.status
index 6c5e1b4..c16feab 100644
--- a/runtime/observatory_2/tests/service_2/service_2.status
+++ b/runtime/observatory_2/tests/service_2/service_2.status
@@ -11,7 +11,6 @@
pause_on_start_and_exit_with_child_test: Pass, RuntimeError # Issue 33049
reload_sources_test: Pass, Slow # Reload is slow on the bots
valid_source_locations_test: Pass, Slow # Generally slow, even in release-x64.
-validate_timer_port_behavior_test: Skip # Issue 44166
[ $arch == arm ]
process_service_test: Pass, Fail # Issue 24344
diff --git a/runtime/observatory_2/tests/service_2/service_2_kernel.status b/runtime/observatory_2/tests/service_2/service_2_kernel.status
index 8c09f06..d8996be 100644
--- a/runtime/observatory_2/tests/service_2/service_2_kernel.status
+++ b/runtime/observatory_2/tests/service_2/service_2_kernel.status
@@ -187,6 +187,7 @@
step_through_switch_test: SkipByDesign
step_through_switch_with_continue_test: SkipByDesign
valid_source_locations_test: Skip, Timeout
+validate_timer_port_behavior_test: SkipByDesign, Timeout # Debugger disabled in AOT
vm_timeline_flags_test: Skip, Timeout
weak_properties_test: CompileTimeError
yield_positions_with_finally_test: SkipByDesign
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index 3091db9..73afe4c 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -14,6 +14,7 @@
dart/data_uri_import_test/none: SkipByDesign
dart/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
dart/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
+dart/isolates/dart_api_create_lightweight_isolate_test: Skip # Issue #44180
dart/minimal_kernel_test: Pass, Slow # Spawns several subprocesses
dart/null_safety_autodetection_in_kernel_compiler_test: Pass, Slow # Spawns several subprocesses
dart/slow_path_shared_stub_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
@@ -23,6 +24,7 @@
dart_2/data_uri_import_test/none: SkipByDesign
dart_2/emit_aot_size_info_flag_test: Pass, Slow # Spawns several subprocesses
dart_2/isolates/*: Pass, Slow # Tests use many isolates and take a longer time.
+dart_2/isolates/dart_api_create_lightweight_isolate_test: Skip # Issue #44180
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/slow_path_shared_stub_test: Pass, Slow # Uses --shared-slow-path-triggers-gc flag.
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index f570648..1d9be24 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -806,7 +806,7 @@
}
// If we are loading from source, figure out the mode from the source.
- if (!KernelIsolate::GetExperimentalFlag("no-non-nullable")) {
+ if (KernelIsolate::GetExperimentalFlag(ExperimentalFeature::non_nullable)) {
return KernelIsolate::DetectNullSafety(script_uri, package_config,
original_working_directory);
}
diff --git a/runtime/vm/experimental_features.cc b/runtime/vm/experimental_features.cc
new file mode 100644
index 0000000..1fffd1a
--- /dev/null
+++ b/runtime/vm/experimental_features.cc
@@ -0,0 +1,45 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: 2.12.0
+
+#include "vm/experimental_features.h"
+
+#include <cstring>
+#include "platform/assert.h"
+#include "vm/globals.h"
+
+namespace dart {
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature) {
+ constexpr bool kFeatureValues[] = {
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ };
+ ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureValues));
+ return kFeatureValues[static_cast<int>(feature)];
+}
+
+const char* GetExperimentalFeatureName(ExperimentalFeature feature) {
+ constexpr const char* kFeatureNames[] = {
+ "non-nullable",
+ "extension-methods",
+ "constant-update-2018",
+ "control-flow-collections",
+ "set-literals",
+ "spread-collections",
+ };
+ ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureNames));
+ return kFeatureNames[static_cast<int>(feature)];
+}
+
+} // namespace dart
diff --git a/runtime/vm/experimental_features.h b/runtime/vm/experimental_features.h
new file mode 100644
index 0000000..797b0d2
--- /dev/null
+++ b/runtime/vm/experimental_features.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: 2.12.0
+
+#ifndef RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+#define RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+
+namespace dart {
+
+enum class ExperimentalFeature {
+ non_nullable,
+ extension_methods,
+ constant_update_2018,
+ control_flow_collections,
+ set_literals,
+ spread_collections,
+};
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature);
+const char* GetExperimentalFeatureName(ExperimentalFeature feature);
+
+} // namespace dart
+
+#endif // RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
diff --git a/runtime/vm/kernel_isolate.cc b/runtime/vm/kernel_isolate.cc
index f8f0d54..9af2bd6 100644
--- a/runtime/vm/kernel_isolate.cc
+++ b/runtime/vm/kernel_isolate.cc
@@ -417,13 +417,16 @@
free(temp);
}
-bool KernelIsolate::GetExperimentalFlag(const char* value) {
+bool KernelIsolate::GetExperimentalFlag(ExperimentalFeature feature) {
+ const char* value = GetExperimentalFeatureName(feature);
for (const char* str : *experimental_flags_) {
if (strcmp(str, value) == 0) {
return true;
+ } else if (strstr(str, "no-") == str && strcmp(str + 3, value) == 0) {
+ return false;
}
}
- return false;
+ return GetExperimentalFeatureDefault(feature);
}
DEFINE_OPTION_HANDLER(KernelIsolate::AddExperimentalFlag,
diff --git a/runtime/vm/kernel_isolate.h b/runtime/vm/kernel_isolate.h
index e45022d..0aa5221 100644
--- a/runtime/vm/kernel_isolate.h
+++ b/runtime/vm/kernel_isolate.h
@@ -12,6 +12,7 @@
#include "vm/allocation.h"
#include "vm/dart.h"
+#include "vm/experimental_features.h"
#include "vm/os_thread.h"
namespace dart {
@@ -78,7 +79,7 @@
static void NotifyAboutIsolateShutdown(const Isolate* isolate);
static void AddExperimentalFlag(const char* value);
- static bool GetExperimentalFlag(const char* value);
+ static bool GetExperimentalFlag(ExperimentalFeature feature);
protected:
static void InitCallback(Isolate* I);
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index 528aeb9e..52f46f8 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -220,7 +220,7 @@
}
bool TestCase::IsNNBD() {
- return !KernelIsolate::GetExperimentalFlag("no-non-nullable");
+ return KernelIsolate::GetExperimentalFlag(ExperimentalFeature::non_nullable);
}
#ifndef PRODUCT
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index a0e71ef..fda79fb 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -98,6 +98,8 @@
"elf.h",
"exceptions.cc",
"exceptions.h",
+ "experimental_features.cc",
+ "experimental_features.h",
"ffi_callback_trampolines.cc",
"ffi_callback_trampolines.h",
"field_table.cc",
diff --git a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
index 9b45b66..6799347 100644
--- a/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
+++ b/sdk/lib/_internal/js_dev_runtime/patch/isolate_patch.dart
@@ -83,7 +83,7 @@
@patch
class ReceivePort {
@patch
- factory ReceivePort() = _ReceivePort;
+ factory ReceivePort([String debugName]) = _ReceivePort;
@patch
factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) =>
@@ -94,6 +94,8 @@
/// (async_helper, unittest) create a dummy receive port to keep the Dart VM
/// alive.
class _ReceivePort extends Stream implements ReceivePort {
+ _ReceivePort([String debugName = '']);
+
close() {}
get sendPort => _unsupported();
@@ -108,7 +110,8 @@
@patch
class RawReceivePort {
@patch
- factory RawReceivePort([Function? handler]) => _unsupported();
+ factory RawReceivePort([Function? handler, String debugName = '']) =>
+ _unsupported();
}
@patch
diff --git a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
index 228fc3a..1c0b053 100644
--- a/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/js_runtime/lib/isolate_patch.dart
@@ -111,7 +111,7 @@
@patch
class ReceivePort {
@patch
- factory ReceivePort() = _ReceivePortImpl;
+ factory ReceivePort([String debugName]) = _ReceivePortImpl;
@patch
factory ReceivePort.fromRawReceivePort(RawReceivePort rawPort) {
@@ -120,6 +120,8 @@
}
class _ReceivePortImpl extends Stream implements ReceivePort {
+ _ReceivePortImpl([String debugName = '']);
+
StreamSubscription listen(void Function(dynamic)? onData,
{Function? onError,
void Function()? onDone,
@@ -135,7 +137,7 @@
@patch
class RawReceivePort {
@patch
- factory RawReceivePort([Function? handler]) {
+ factory RawReceivePort([Function? handler, String debugName = '']) {
throw new UnsupportedError('new RawReceivePort');
}
}
diff --git a/sdk/lib/_internal/vm/bin/io_service_patch.dart b/sdk/lib/_internal/vm/bin/io_service_patch.dart
index d54bbf2..7b93d0e 100644
--- a/sdk/lib/_internal/vm/bin/io_service_patch.dart
+++ b/sdk/lib/_internal/vm/bin/io_service_patch.dart
@@ -74,7 +74,7 @@
static void _ensureInitialize() {
if (_receivePort == null) {
- _receivePort = new RawReceivePort();
+ _receivePort = new RawReceivePort(null, 'IO Service');
_replyToPort = _receivePort!.sendPort;
_receivePort!.handler = (data) {
assert(data is List && data.length == 2);
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 976b1f1..d567603 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -1206,7 +1206,7 @@
void connectToEventHandler() {
assert(!isClosed);
if (eventPort == null) {
- eventPort = new RawReceivePort(multiplex);
+ eventPort = new RawReceivePort(multiplex, 'Socket Event Handler');
}
}
diff --git a/sdk/lib/_internal/vm/lib/isolate_patch.dart b/sdk/lib/_internal/vm/lib/isolate_patch.dart
index 187efdb..23d5dfe 100644
--- a/sdk/lib/_internal/vm/lib/isolate_patch.dart
+++ b/sdk/lib/_internal/vm/lib/isolate_patch.dart
@@ -391,7 +391,8 @@
}
const bool newIsolateGroup = false;
- final RawReceivePort readyPort = new RawReceivePort();
+ final RawReceivePort readyPort =
+ new RawReceivePort(null, 'Isolate.spawn ready');
try {
spawnFunction(
readyPort.sendPort,
@@ -468,7 +469,8 @@
// The VM will invoke [_startIsolate] and not `main`.
final packageConfigString = packageConfig?.toString();
- final RawReceivePort readyPort = new RawReceivePort();
+ final RawReceivePort readyPort =
+ new RawReceivePort(null, 'Isolate.spawnUri ready');
try {
_spawnUri(
readyPort.sendPort,
diff --git a/sdk/lib/_internal/vm/lib/timer_impl.dart b/sdk/lib/_internal/vm/lib/timer_impl.dart
index a348d19..ac1939d 100644
--- a/sdk/lib/_internal/vm/lib/timer_impl.dart
+++ b/sdk/lib/_internal/vm/lib/timer_impl.dart
@@ -452,7 +452,7 @@
if (_receivePort == null) {
assert(_receivePort == null);
assert(_sendPort == null);
- _receivePort = RawReceivePort(_handleMessage);
+ _receivePort = RawReceivePort(_handleMessage, 'Timer');
_sendPort = _receivePort!.sendPort;
_scheduledWakeupTime = 0;
} else {
diff --git a/sdk/lib/developer/service.dart b/sdk/lib/developer/service.dart
index a563f39..6fd58c3 100644
--- a/sdk/lib/developer/service.dart
+++ b/sdk/lib/developer/service.dart
@@ -41,7 +41,8 @@
/// Uri to access the service).
static Future<ServiceProtocolInfo> getInfo() async {
// Port to receive response from service isolate.
- final RawReceivePort receivePort = new RawReceivePort();
+ final RawReceivePort receivePort =
+ new RawReceivePort(null, 'Service.getInfo');
final Completer<Uri?> uriCompleter = new Completer<Uri?>();
receivePort.handler = (Uri? uri) => uriCompleter.complete(uri);
// Request the information from the service isolate.
@@ -62,7 +63,8 @@
// TODO: When NNBD is complete, delete the following line.
ArgumentError.checkNotNull(enable, 'enable');
// Port to receive response from service isolate.
- final RawReceivePort receivePort = new RawReceivePort();
+ final RawReceivePort receivePort =
+ new RawReceivePort(null, 'Service.controlWebServer');
final Completer<Uri> uriCompleter = new Completer<Uri>();
receivePort.handler = (Uri uri) => uriCompleter.complete(uri);
// Request the information from the service isolate.
diff --git a/sdk/lib/vmservice/message.dart b/sdk/lib/vmservice/message.dart
index ee29fe3..5b07cea 100644
--- a/sdk/lib/vmservice/message.dart
+++ b/sdk/lib/vmservice/message.dart
@@ -156,7 +156,7 @@
}
Future<Response> sendToIsolate(SendPort sendPort) {
- final receivePort = RawReceivePort();
+ final receivePort = RawReceivePort(null, 'Isolate Message');
receivePort.handler = (value) {
receivePort.close();
_setResponseFromPort(value);
@@ -200,7 +200,7 @@
}
Future<Response> sendToVM() {
- final receivePort = RawReceivePort();
+ final receivePort = RawReceivePort(null, 'VM Message');
receivePort.handler = (value) {
receivePort.close();
_setResponseFromPort(value);
diff --git a/sdk/lib/vmservice/vmservice.dart b/sdk/lib/vmservice/vmservice.dart
index 98ce332..f7f4a26 100644
--- a/sdk/lib/vmservice/vmservice.dart
+++ b/sdk/lib/vmservice/vmservice.dart
@@ -21,8 +21,8 @@
part 'message_router.dart';
part 'named_lookup.dart';
-final isolateControlPort = RawReceivePort();
-final scriptLoadPort = RawReceivePort();
+final isolateControlPort = RawReceivePort(null, 'Isolate Control Port');
+final scriptLoadPort = RawReceivePort(null, 'Script Load');
abstract class IsolateEmbedderData {
void cleanup();
diff --git a/tools/VERSION b/tools/VERSION
index ffc3992..77d848e 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 12
PATCH 0
-PRERELEASE 37
+PRERELEASE 38
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index e1403b8..99f8c15 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -10,7 +10,8 @@
#
# ### Code Generation
#
-# When you change this file, run the following to update analyzer and kernel:
+# When you change this file, run the following to update analyzer, kernel, and
+# vm:
#
# analyzer:
# dart pkg/analyzer/tool/experiments/generate.dart
@@ -18,6 +19,9 @@
# kernel:
# pkg/front_end/tool/fasta generate-experimental-flags
#
+# vm:
+# dart tools/generate_experimental_flags.dart
+#
# ### Overview
#
# This document consists mostly of a map called "features".
diff --git a/tools/generate_experimental_flags.dart b/tools/generate_experimental_flags.dart
new file mode 100644
index 0000000..bdb089e
--- /dev/null
+++ b/tools/generate_experimental_flags.dart
@@ -0,0 +1,125 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart = 2.9
+
+import 'dart:io' show File, Platform;
+import 'package:yaml/yaml.dart' show YamlMap, loadYaml;
+
+void main() {
+ YamlMap yaml =
+ loadYaml(new File.fromUri(computeYamlFile()).readAsStringSync());
+ final currentVersion = getAsVersionNumber(yaml['current-version']);
+ final enumNames = new StringBuffer();
+ final featureValues = new StringBuffer();
+ final featureNames = new StringBuffer();
+
+ YamlMap features = yaml['features'];
+ for (var entry in features.entries) {
+ final category = (entry.value as YamlMap)['category'];
+ if (category == null || category == "vm" || category == "language") {
+ final version = getAsVersionNumber((entry.value as YamlMap)['enabledIn']);
+ if (version != null) {
+ final value = isGreaterOrEqualVersion(currentVersion, version);
+ final name = entry.key.replaceAll('-', '_');
+ enumNames.write(' $name,\n');
+ featureValues.write(' $value,\n');
+ featureNames.write(' "${entry.key}",\n');
+ }
+ }
+ }
+
+ final h = '''
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: ${currentVersion.join('.')}
+
+#ifndef RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+#define RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+
+namespace dart {
+
+enum class ExperimentalFeature {
+$enumNames};
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature);
+const char* GetExperimentalFeatureName(ExperimentalFeature feature);
+
+} // namespace dart
+
+#endif // RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
+''';
+
+ final cc = '''
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
+//
+// Instead modify 'tools/experimental_features.yaml' and run
+// 'dart tools/generate_experimental_flags.dart' to update.
+//
+// Current version: ${currentVersion.join('.')}
+
+#include "vm/experimental_features.h"
+
+#include <cstring>
+#include "platform/assert.h"
+#include "vm/globals.h"
+
+namespace dart {
+
+bool GetExperimentalFeatureDefault(ExperimentalFeature feature) {
+ constexpr bool kFeatureValues[] = {
+$featureValues };
+ ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureValues));
+ return kFeatureValues[static_cast<int>(feature)];
+}
+
+const char* GetExperimentalFeatureName(ExperimentalFeature feature) {
+ constexpr const char* kFeatureNames[] = {
+$featureNames };
+ ASSERT(static_cast<size_t>(feature) < ARRAY_SIZE(kFeatureNames));
+ return kFeatureNames[static_cast<int>(feature)];
+}
+
+} // namespace dart
+''';
+
+ File.fromUri(computeHFile()).writeAsStringSync(h);
+ File.fromUri(computeCcFile()).writeAsStringSync(cc);
+}
+
+Uri computeYamlFile() {
+ return Platform.script.resolve("experimental_features.yaml");
+}
+
+Uri computeCcFile() {
+ return Platform.script.resolve("../runtime/vm/experimental_features.cc");
+}
+
+Uri computeHFile() {
+ return Platform.script.resolve("../runtime/vm/experimental_features.h");
+}
+
+List<num> getAsVersionNumber(dynamic value) {
+ if (value == null) return null;
+ final version = List.of("$value".split(".").map(int.parse));
+ while (version.length < 3) version.add(0);
+ return version;
+}
+
+bool isGreaterOrEqualVersion(List<num> left, List<num> right) {
+ assert(left.length == right.length);
+ for (var i = 0; i < left.length; ++i) {
+ if (left[i] != right[i]) return left[i] > right[i];
+ }
+ return true;
+}