Commit e0cd42e4 authored by Alexander Aprelev's avatar Alexander Aprelev Committed by Siva

Roll engine to 76cb311d9c33720dcd19274228b39ecdbad8d9af (with rolled dart) (#16518)

* Handle error count reported by frontend.

Extend compilation result from single string to a structure(string filename and integer error count).

* Use ?.

* Include engine roll with dart sdk roll.

* parse(onError) -> tryParse

* Make '?? throw' more readable and avoid issue with analyzer

* Fix test so it mocks compiler output including errors count
parent c73b8a7c
8a6e64a8ef09f1f1af207725b40022d8d7a9dcd7 76cb311d9c33720dcd19274228b39ecdbad8d9af
...@@ -238,8 +238,14 @@ dependencies: ...@@ -238,8 +238,14 @@ dependencies:
throw 'failed to parse error message: $error'; throw 'failed to parse error message: $error';
} }
final String column = error.substring(colon2 + kColon.length, bullet2); final String column = error.substring(colon2 + kColon.length, bullet2);
final int lineNumber = int.parse(line, radix: 10, onError: (String source) => throw 'failed to parse error message: $error'); final int lineNumber = int.tryParse(line, radix: 10);
final int columnNumber = int.parse(column, radix: 10, onError: (String source) => throw 'failed to parse error message: $error'); if (lineNumber == null) {
throw 'failed to parse error message: $error';
}
final int columnNumber = int.tryParse(column, radix: 10);
if (columnNumber == null) {
throw 'failed to parse error message: $error';
}
if (lineNumber < 1 || lineNumber > lines.length) { if (lineNumber < 1 || lineNumber > lines.length) {
keepMain = true; keepMain = true;
throw 'failed to parse error message (read line number as $lineNumber; total number of lines is ${lines.length}): $error'; throw 'failed to parse error message (read line number as $lineNumber; total number of lines is ${lines.length}): $error';
......
...@@ -210,7 +210,7 @@ class AndroidDevice extends Device { ...@@ -210,7 +210,7 @@ class AndroidDevice extends Device {
// Sample output: '22' // Sample output: '22'
final String sdkVersion = await _getProperty('ro.build.version.sdk'); final String sdkVersion = await _getProperty('ro.build.version.sdk');
final int sdkVersionParsed = int.parse(sdkVersion, onError: (String source) => null); final int sdkVersionParsed = int.tryParse(sdkVersion);
if (sdkVersionParsed == null) { if (sdkVersionParsed == null) {
printError('Unexpected response from getprop: "$sdkVersion"'); printError('Unexpected response from getprop: "$sdkVersion"');
return false; return false;
...@@ -817,7 +817,7 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder { ...@@ -817,7 +817,7 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
final AndroidDevice device; final AndroidDevice device;
static int _extractPort(String portString) { static int _extractPort(String portString) {
return int.parse(portString.trim(), onError: (_) => null); return int.tryParse(portString.trim());
} }
@override @override
......
...@@ -105,7 +105,7 @@ Future<void> build({ ...@@ -105,7 +105,7 @@ Future<void> build({
String kernelBinaryFilename; String kernelBinaryFilename;
if (needBuild) { if (needBuild) {
ensureDirectoryExists(applicationKernelFilePath); ensureDirectoryExists(applicationKernelFilePath);
kernelBinaryFilename = await compile( final CompilerOutput compilerOutput = await compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
incrementalCompilerByteStorePath: fs.path.absolute(getIncrementalCompilerByteStoreDirectory()), incrementalCompilerByteStorePath: fs.path.absolute(getIncrementalCompilerByteStoreDirectory()),
mainPath: fs.file(mainPath).absolute.path, mainPath: fs.file(mainPath).absolute.path,
...@@ -116,6 +116,7 @@ Future<void> build({ ...@@ -116,6 +116,7 @@ Future<void> build({
fileSystemScheme: fileSystemScheme, fileSystemScheme: fileSystemScheme,
packagesPath: packagesPath, packagesPath: packagesPath,
); );
kernelBinaryFilename = compilerOutput?.outputFilename;
if (kernelBinaryFilename == null) { if (kernelBinaryFilename == null) {
throwToolExit('Compiler failed on $mainPath'); throwToolExit('Compiler failed on $mainPath');
} }
......
...@@ -387,7 +387,7 @@ Future<String> _buildAotSnapshot( ...@@ -387,7 +387,7 @@ Future<String> _buildAotSnapshot(
} }
if (previewDart2) { if (previewDart2) {
mainPath = await compile( final CompilerOutput compilerOutput = await compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
mainPath: mainPath, mainPath: mainPath,
outputFilePath: kApplicationKernelPath, outputFilePath: kApplicationKernelPath,
...@@ -398,6 +398,7 @@ Future<String> _buildAotSnapshot( ...@@ -398,6 +398,7 @@ Future<String> _buildAotSnapshot(
entryPointsJsonFiles: entryPointsJsonFiles, entryPointsJsonFiles: entryPointsJsonFiles,
trackWidgetCreation: false, trackWidgetCreation: false,
); );
mainPath = compilerOutput?.outputFilename;
if (mainPath == null) { if (mainPath == null) {
printError('Compiler terminated unexpectedly.'); printError('Compiler terminated unexpectedly.');
return null; return null;
......
...@@ -384,7 +384,7 @@ class FuchsiaReloadCommand extends FlutterCommand { ...@@ -384,7 +384,7 @@ class FuchsiaReloadCommand extends FlutterCommand {
final int lastSpace = trimmed.lastIndexOf(' '); final int lastSpace = trimmed.lastIndexOf(' ');
final String lastWord = trimmed.substring(lastSpace + 1); final String lastWord = trimmed.substring(lastSpace + 1);
if ((lastWord != '.') && (lastWord != '..')) { if ((lastWord != '.') && (lastWord != '..')) {
final int value = int.parse(lastWord, onError: (_) => null); final int value = int.tryParse(lastWord);
if (value != null) if (value != null)
ports.add(value); ports.add(value);
} }
......
...@@ -784,7 +784,7 @@ class PubspecChecksum extends PubspecLine { ...@@ -784,7 +784,7 @@ class PubspecChecksum extends PubspecLine {
if (twoLines.length != 2) { if (twoLines.length != 2) {
return new PubspecChecksum(-1, line); return new PubspecChecksum(-1, line);
} }
final int value = int.parse(twoLines.last.trim(), radix: 16, onError: (String _) => -1); final int value = int.tryParse(twoLines.last.trim(), radix: 16) ?? -1;
return new PubspecChecksum(value, line); return new PubspecChecksum(value, line);
} }
} }
......
...@@ -15,6 +15,13 @@ import 'globals.dart'; ...@@ -15,6 +15,13 @@ import 'globals.dart';
typedef void CompilerMessageConsumer(String message); typedef void CompilerMessageConsumer(String message);
class CompilerOutput {
String outputFilename;
int errorCount;
CompilerOutput(this.outputFilename, this.errorCount);
}
class _StdoutHandler { class _StdoutHandler {
_StdoutHandler({this.consumer: printError}) { _StdoutHandler({this.consumer: printError}) {
reset(); reset();
...@@ -22,17 +29,24 @@ class _StdoutHandler { ...@@ -22,17 +29,24 @@ class _StdoutHandler {
final CompilerMessageConsumer consumer; final CompilerMessageConsumer consumer;
String boundaryKey; String boundaryKey;
Completer<String> outputFilename; Completer<CompilerOutput> compilerOutput;
void handler(String string) { void handler(String string) {
const String kResultPrefix = 'result '; const String kResultPrefix = 'result ';
if (boundaryKey == null) { if (boundaryKey == null) {
if (string.startsWith(kResultPrefix)) if (string.startsWith(kResultPrefix))
boundaryKey = string.substring(kResultPrefix.length); boundaryKey = string.substring(kResultPrefix.length);
} else if (string.startsWith(boundaryKey)) } else if (string.startsWith(boundaryKey)) {
outputFilename.complete(string.length > boundaryKey.length if (string.length <= boundaryKey.length) {
? string.substring(boundaryKey.length + 1) compilerOutput.complete(null);
: null); return;
}
final int spaceDelimiter = string.lastIndexOf(' ');
compilerOutput.complete(
new CompilerOutput(
string.substring(boundaryKey.length + 1, spaceDelimiter),
int.parse(string.substring(spaceDelimiter + 1).trim())));
}
else else
consumer('compiler message: $string'); consumer('compiler message: $string');
} }
...@@ -41,11 +55,11 @@ class _StdoutHandler { ...@@ -41,11 +55,11 @@ class _StdoutHandler {
// with its own boundary key and new completer. // with its own boundary key and new completer.
void reset() { void reset() {
boundaryKey = null; boundaryKey = null;
outputFilename = new Completer<String>(); compilerOutput = new Completer<CompilerOutput>();
} }
} }
Future<String> compile( Future<CompilerOutput> compile(
{String sdkRoot, {String sdkRoot,
String mainPath, String mainPath,
String outputFilePath, String outputFilePath,
...@@ -132,7 +146,7 @@ Future<String> compile( ...@@ -132,7 +146,7 @@ Future<String> compile(
.transform(const LineSplitter()) .transform(const LineSplitter())
.listen(stdoutHandler.handler); .listen(stdoutHandler.handler);
final int exitCode = await server.exitCode; final int exitCode = await server.exitCode;
return exitCode == 0 ? stdoutHandler.outputFilename.future : null; return exitCode == 0 ? stdoutHandler.compilerOutput.future : null;
} }
/// Wrapper around incremental frontend server compiler, that communicates with /// Wrapper around incremental frontend server compiler, that communicates with
...@@ -170,7 +184,7 @@ class ResidentCompiler { ...@@ -170,7 +184,7 @@ class ResidentCompiler {
/// point that is used for recompilation. /// point that is used for recompilation.
/// Binary file name is returned if compilation was successful, otherwise /// Binary file name is returned if compilation was successful, otherwise
/// null is returned. /// null is returned.
Future<String> recompile(String mainPath, List<String> invalidatedFiles, Future<CompilerOutput> recompile(String mainPath, List<String> invalidatedFiles,
{String outputPath, String packagesFilePath}) async { {String outputPath, String packagesFilePath}) async {
stdoutHandler.reset(); stdoutHandler.reset();
...@@ -186,10 +200,11 @@ class ResidentCompiler { ...@@ -186,10 +200,11 @@ class ResidentCompiler {
} }
_server.stdin.writeln(inputKey); _server.stdin.writeln(inputKey);
return stdoutHandler.outputFilename.future; return stdoutHandler.compilerOutput.future;
} }
Future<String> _compile(String scriptFilename, String outputPath, String packagesFilePath) async { Future<CompilerOutput> _compile(String scriptFilename, String outputPath,
String packagesFilePath) async {
final String frontendServer = artifacts.getArtifactPath( final String frontendServer = artifacts.getArtifactPath(
Artifact.frontendServerSnapshotForEngineDartSdk Artifact.frontendServerSnapshotForEngineDartSdk
); );
...@@ -231,8 +246,8 @@ class ResidentCompiler { ...@@ -231,8 +246,8 @@ class ResidentCompiler {
onDone: () { onDone: () {
// when outputFilename future is not completed, but stdout is closed // when outputFilename future is not completed, but stdout is closed
// process has died unexpectedly. // process has died unexpectedly.
if (!stdoutHandler.outputFilename.isCompleted) { if (!stdoutHandler.compilerOutput.isCompleted) {
stdoutHandler.outputFilename.complete(null); stdoutHandler.compilerOutput.complete(null);
} }
}); });
...@@ -243,7 +258,7 @@ class ResidentCompiler { ...@@ -243,7 +258,7 @@ class ResidentCompiler {
_server.stdin.writeln('compile $scriptFilename'); _server.stdin.writeln('compile $scriptFilename');
return stdoutHandler.outputFilename.future; return stdoutHandler.compilerOutput.future;
} }
......
...@@ -501,10 +501,11 @@ class DevFS { ...@@ -501,10 +501,11 @@ class DevFS {
if (fullRestart) { if (fullRestart) {
generator.reset(); generator.reset();
} }
final String compiledBinary = final CompilerOutput compilerOutput =
await generator.recompile(mainPath, invalidatedFiles, await generator.recompile(mainPath, invalidatedFiles,
outputPath: dillOutputPath ?? fs.path.join(getBuildDirectory(), 'app.dill'), outputPath: dillOutputPath ?? fs.path.join(getBuildDirectory(), 'app.dill'),
packagesFilePath : _packagesFilePath); packagesFilePath : _packagesFilePath);
final String compiledBinary = compilerOutput?.outputFilename;
if (compiledBinary != null && compiledBinary.isNotEmpty) { if (compiledBinary != null && compiledBinary.isNotEmpty) {
final String entryUri = projectRootPath != null ? final String entryUri = projectRootPath != null ?
fs.path.relative(mainPath, from: projectRootPath): fs.path.relative(mainPath, from: projectRootPath):
......
...@@ -143,10 +143,13 @@ class _Compiler { ...@@ -143,10 +143,13 @@ class _Compiler {
printTrace('Compiling ${request.path}'); printTrace('Compiling ${request.path}');
compiler ??= createCompiler(); compiler ??= createCompiler();
suppressOutput = false; suppressOutput = false;
final String outputPath = await handleTimeout(compiler.recompile(request.path, final CompilerOutput compilerOutput = await handleTimeout<CompilerOutput>(
<String>[request.path], compiler.recompile(
outputPath: outputDill.path, request.path,
), request.path); <String>[request.path],
outputPath: outputDill.path),
request.path);
final String outputPath = compilerOutput?.outputFilename;
// Check if the compiler produced the output. If it failed then // Check if the compiler produced the output. If it failed then
// outputPath would be null. In this case pass null upwards to the // outputPath would be null. In this case pass null upwards to the
...@@ -179,7 +182,7 @@ class _Compiler { ...@@ -179,7 +182,7 @@ class _Compiler {
Future<String> compile(String mainDart) { Future<String> compile(String mainDart) {
final Completer<String> completer = new Completer<String>(); final Completer<String> completer = new Completer<String>();
compilerController.add(new _CompilationRequest(mainDart, completer)); compilerController.add(new _CompilationRequest(mainDart, completer));
return handleTimeout(completer.future, mainDart); return handleTimeout<String>(completer.future, mainDart);
} }
Future<dynamic> shutdown() async { Future<dynamic> shutdown() async {
...@@ -187,7 +190,7 @@ class _Compiler { ...@@ -187,7 +190,7 @@ class _Compiler {
compiler = null; compiler = null;
} }
static Future<String> handleTimeout(Future<String> value, String path) { static Future<T> handleTimeout<T>(Future<T> value, String path) {
return value.timeout(const Duration(minutes: 5), onTimeout: () { return value.timeout(const Duration(minutes: 5), onTimeout: () {
printError('Compilation of $path timed out after 5 minutes.'); printError('Compilation of $path timed out after 5 minutes.');
return null; return null;
......
...@@ -499,7 +499,7 @@ class GitTagVersion { ...@@ -499,7 +499,7 @@ class GitTagVersion {
return const GitTagVersion.unknown(); return const GitTagVersion.unknown();
} }
final List<int> parsedParts = parts.take(4).map<int>( final List<int> parsedParts = parts.take(4).map<int>(
(String value) => int.parse(value, onError: (String value) => null), (String value) => int.tryParse(value),
).toList(); ).toList();
return new GitTagVersion(parsedParts[0], parsedParts[1], parsedParts[2], parsedParts[3], parts[4]); return new GitTagVersion(parsedParts[0], parsedParts[1], parsedParts[2], parsedParts[3], parts[4]);
} }
......
...@@ -43,15 +43,15 @@ void main() { ...@@ -43,15 +43,15 @@ void main() {
when(mockFrontendServer.stdout) when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture( .thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture(
new Future<List<int>>.value(utf8.encode( new Future<List<int>>.value(utf8.encode(
'result abc\nline1\nline2\nabc /path/to/main.dart.dill' 'result abc\nline1\nline2\nabc /path/to/main.dart.dill 0'
)) ))
)); ));
final String output = await compile(sdkRoot: '/path/to/sdkroot', final CompilerOutput output = await compile(sdkRoot: '/path/to/sdkroot',
mainPath: '/path/to/main.dart' mainPath: '/path/to/main.dart'
); );
expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n')); expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(output, equals('/path/to/main.dart.dill')); expect(output.outputFilename, equals('/path/to/main.dart.dill'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
...@@ -66,7 +66,7 @@ void main() { ...@@ -66,7 +66,7 @@ void main() {
)) ))
)); ));
final String output = await compile(sdkRoot: '/path/to/sdkroot', final CompilerOutput output = await compile(sdkRoot: '/path/to/sdkroot',
mainPath: '/path/to/main.dart' mainPath: '/path/to/main.dart'
); );
expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
...@@ -88,7 +88,7 @@ void main() { ...@@ -88,7 +88,7 @@ void main() {
)) ))
)); ));
final String output = await compile(sdkRoot: '/path/to/sdkroot', final CompilerOutput output = await compile(sdkRoot: '/path/to/sdkroot',
mainPath: '/path/to/main.dart' mainPath: '/path/to/main.dart'
); );
expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
...@@ -137,17 +137,17 @@ void main() { ...@@ -137,17 +137,17 @@ void main() {
when(mockFrontendServer.stdout) when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture( .thenAnswer((Invocation invocation) => new Stream<List<int>>.fromFuture(
new Future<List<int>>.value(utf8.encode( new Future<List<int>>.value(utf8.encode(
'result abc\nline1\nline2\nabc /path/to/main.dart.dill' 'result abc\nline1\nline2\nabc /path/to/main.dart.dill 0'
)) ))
)); ));
final String output = await generator.recompile( final CompilerOutput output = await generator.recompile(
'/path/to/main.dart', null /* invalidatedFiles */ '/path/to/main.dart', null /* invalidatedFiles */
); );
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
verifyNoMoreInteractions(mockFrontendServerStdIn); verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n')); expect(logger.errorText, equals('compiler message: line1\ncompiler message: line2\n'));
expect(output, equals('/path/to/main.dart.dill')); expect(output.outputFilename, equals('/path/to/main.dart.dill'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
...@@ -157,7 +157,7 @@ void main() { ...@@ -157,7 +157,7 @@ void main() {
.thenAnswer((Invocation invocation) => const Stream<List<int>>.empty() .thenAnswer((Invocation invocation) => const Stream<List<int>>.empty()
); );
final String output = await generator.recompile( final CompilerOutput output = await generator.recompile(
'/path/to/main.dart', null /* invalidatedFiles */ '/path/to/main.dart', null /* invalidatedFiles */
); );
expect(output, equals(null)); expect(output, equals(null));
...@@ -171,12 +171,12 @@ void main() { ...@@ -171,12 +171,12 @@ void main() {
final StreamController<List<int>> streamController = new StreamController<List<int>>(); final StreamController<List<int>> streamController = new StreamController<List<int>>();
when(mockFrontendServer.stdout) when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream); .thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode('result abc\nline0\nline1\nabc /path/to/main.dart.dill\n')); streamController.add(utf8.encode('result abc\nline0\nline1\nabc /path/to/main.dart.dill 0\n'));
await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */); await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
await _recompile(streamController, generator, mockFrontendServerStdIn, await _recompile(streamController, generator, mockFrontendServerStdIn,
'result abc\nline1\nline2\nabc /path/to/main.dart.dill\n'); 'result abc\nline1\nline2\nabc /path/to/main.dart.dill 0\n');
verifyNoMoreInteractions(mockFrontendServerStdIn); verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
...@@ -195,15 +195,15 @@ void main() { ...@@ -195,15 +195,15 @@ void main() {
when(mockFrontendServer.stdout) when(mockFrontendServer.stdout)
.thenAnswer((Invocation invocation) => streamController.stream); .thenAnswer((Invocation invocation) => streamController.stream);
streamController.add(utf8.encode( streamController.add(utf8.encode(
'result abc\nline0\nline1\nabc /path/to/main.dart.dill\n' 'result abc\nline0\nline1\nabc /path/to/main.dart.dill 0\n'
)); ));
await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */); await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */);
expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n');
await _recompile(streamController, generator, mockFrontendServerStdIn, await _recompile(streamController, generator, mockFrontendServerStdIn,
'result abc\nline1\nline2\nabc /path/to/main.dart.dill\n'); 'result abc\nline1\nline2\nabc /path/to/main.dart.dill 0\n');
await _recompile(streamController, generator, mockFrontendServerStdIn, await _recompile(streamController, generator, mockFrontendServerStdIn,
'result abc\nline2\nline3\nabc /path/to/main.dart.dill\n'); 'result abc\nline2\nline3\nabc /path/to/main.dart.dill 0\n');
verifyNoMoreInteractions(mockFrontendServerStdIn); verifyNoMoreInteractions(mockFrontendServerStdIn);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
...@@ -226,8 +226,8 @@ Future<Null> _recompile(StreamController<List<int>> streamController, ...@@ -226,8 +226,8 @@ Future<Null> _recompile(StreamController<List<int>> streamController,
new Future<List<int>>(() { new Future<List<int>>(() {
streamController.add(utf8.encode(mockCompilerOutput)); streamController.add(utf8.encode(mockCompilerOutput));
}); });
final String output = await generator.recompile(null /* mainPath */, <String>['/path/to/main.dart']); final CompilerOutput output = await generator.recompile(null /* mainPath */, <String>['/path/to/main.dart']);
expect(output, equals('/path/to/main.dart.dill')); expect(output.outputFilename, equals('/path/to/main.dart.dill'));
final String commands = mockFrontendServerStdIn.getAndClear(); final String commands = mockFrontendServerStdIn.getAndClear();
final RegExp re = new RegExp('^recompile (.*)\\n/path/to/main.dart\\n(.*)\\n\$'); final RegExp re = new RegExp('^recompile (.*)\\n/path/to/main.dart\\n(.*)\\n\$');
expect(commands, matches(re)); expect(commands, matches(re));
......
...@@ -197,7 +197,7 @@ class FuchsiaRemoteConnection { ...@@ -197,7 +197,7 @@ class FuchsiaRemoteConnection {
final int lastSpace = trimmed.lastIndexOf(' '); final int lastSpace = trimmed.lastIndexOf(' ');
final String lastWord = trimmed.substring(lastSpace + 1); final String lastWord = trimmed.substring(lastSpace + 1);
if ((lastWord != '.') && (lastWord != '..')) { if ((lastWord != '.') && (lastWord != '..')) {
final int value = int.parse(lastWord, onError: (_) => null); final int value = int.tryParse(lastWord);
if (value != null) { if (value != null) {
ports.add(value); ports.add(value);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment