From dc634e195e03eeb9644bdcd80aede331f142366a Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Thu, 2 Feb 2017 15:48:35 -0800 Subject: [PATCH] Introduce the concept of asynchronicity to the service extensions. (#7823) This allows us, for example, to wait for the slow mode banner to have been removed from the screen before triggering a screen shot. --- .../lib/src/foundation/basic_types.dart | 33 ++- .../flutter/lib/src/foundation/binding.dart | 35 ++-- .../flutter/lib/src/foundation/print.dart | 9 + .../flutter/lib/src/rendering/binding.dart | 16 +- .../flutter/lib/src/scheduler/binding.dart | 28 ++- .../flutter/lib/src/services/binding.dart | 4 +- packages/flutter/lib/src/widgets/binding.dart | 16 +- .../lib/src/android/android_device.dart | 6 +- .../lib/src/commands/screenshot.dart | 3 +- packages/flutter_tools/lib/src/devfs.dart | 24 ++- packages/flutter_tools/lib/src/device.dart | 2 +- .../flutter_tools/lib/src/ios/devices.dart | 5 +- .../flutter_tools/lib/src/ios/simulators.dart | 4 +- .../lib/src/resident_runner.dart | 27 ++- packages/flutter_tools/lib/src/vmservice.dart | 188 ++++++++++-------- packages/flutter_tools/test/devfs_test.dart | 7 +- 16 files changed, 261 insertions(+), 146 deletions(-) diff --git a/packages/flutter/lib/src/foundation/basic_types.dart b/packages/flutter/lib/src/foundation/basic_types.dart index 997caa91271..5d63bf244f6 100644 --- a/packages/flutter/lib/src/foundation/basic_types.dart +++ b/packages/flutter/lib/src/foundation/basic_types.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:collection'; // COMMON SIGNATURES @@ -20,16 +21,46 @@ typedef void ValueChanged(T value); /// For example, service extensions use this callback because they /// call the callback whenever the extension is called with a /// value, regardless of whether the given value is new or not. +/// +/// See also: +/// * [ValueGetter], the getter equivalent of this signature. +/// * [AsyncValueSetter], an asynchronous version of this signature. typedef void ValueSetter(T value); /// Signature for callbacks that are to report a value on demand. /// -/// See also [ValueSetter]. +/// See also: +/// * [ValueSetter], the setter equivalent of this signature. +/// * [AsyncValueGetter], an asynchronous version of this signature. typedef T ValueGetter(); /// Signature for callbacks that filter an iterable. typedef Iterable IterableFilter(Iterable input); +/// Signature of callbacks that have no arguments and return no data, but that +/// return a [Future] to indicate when their work is complete. +/// +/// See also: +/// * [VoidCallback], a synchronous version of this signature. +/// * [AsyncValueGetter], a signature for asynchronous getters. +/// * [AsyncValueSetter], a signature for asynchronous setters. +typedef Future AsyncCallback(); + +/// Signature for callbacks that report that a value has been set and return a +/// [Future] that completes when the value has been saved. +/// +/// See also: +/// * [ValueSetter], a synchronous version of this signature. +/// * [AsyncValueGetter], the getter equivalent of this signature. +typedef Future AsyncValueSetter(T value); + +/// Signature for callbacks that are to asynchronously report a value on demand. +/// +/// See also: +/// * [ValueGetter], a synchronous version of this signature. +/// * [AsyncValueSetter], the setter equivalent of this signature. +typedef Future AsyncValueGetter(); + // BITFIELD diff --git a/packages/flutter/lib/src/foundation/binding.dart b/packages/flutter/lib/src/foundation/binding.dart index 9741eb8ccdd..b73ab860cc0 100644 --- a/packages/flutter/lib/src/foundation/binding.dart +++ b/packages/flutter/lib/src/foundation/binding.dart @@ -113,7 +113,7 @@ abstract class BindingBase { ); registerSignalServiceExtension( name: 'frameworkPresent', - callback: () => null + callback: () => new Future.value() ); assert(() { _debugServiceExtensionsRegistered = true; return true; }); } @@ -128,8 +128,9 @@ abstract class BindingBase { /// code, and to flush any caches of previously computed values, in /// case the new code would compute them differently. @mustCallSuper - void reassembleApplication() { + Future reassembleApplication() { FlutterError.resetErrorCount(); + return new Future.value(); } @@ -141,14 +142,14 @@ abstract class BindingBase { @protected void registerSignalServiceExtension({ @required String name, - @required VoidCallback callback + @required AsyncCallback callback }) { assert(name != null); assert(callback != null); registerServiceExtension( name: name, callback: (Map parameters) async { - callback(); + await callback(); return {}; } ); @@ -169,8 +170,8 @@ abstract class BindingBase { @protected void registerBoolServiceExtension({ String name, - @required ValueGetter getter, - @required ValueSetter setter + @required AsyncValueGetter getter, + @required AsyncValueSetter setter }) { assert(name != null); assert(getter != null); @@ -179,8 +180,8 @@ abstract class BindingBase { name: name, callback: (Map parameters) async { if (parameters.containsKey('enabled')) - setter(parameters['enabled'] == 'true'); - return { 'enabled': getter() }; + await setter(parameters['enabled'] == 'true'); + return { 'enabled': await getter() }; } ); } @@ -199,8 +200,8 @@ abstract class BindingBase { @protected void registerNumericServiceExtension({ @required String name, - @required ValueGetter getter, - @required ValueSetter setter + @required AsyncValueGetter getter, + @required AsyncValueSetter setter }) { assert(name != null); assert(getter != null); @@ -209,8 +210,8 @@ abstract class BindingBase { name: name, callback: (Map parameters) async { if (parameters.containsKey(name)) - setter(double.parse(parameters[name])); - return { name: getter() }; + await setter(double.parse(parameters[name])); + return { name: await getter() }; } ); } @@ -228,8 +229,8 @@ abstract class BindingBase { @protected void registerStringServiceExtension({ @required String name, - @required ValueGetter getter, - @required ValueSetter setter + @required AsyncValueGetter getter, + @required AsyncValueSetter setter }) { assert(name != null); assert(getter != null); @@ -238,8 +239,8 @@ abstract class BindingBase { name: name, callback: (Map parameters) async { if (parameters.containsKey('value')) - setter(parameters['value']); - return { 'value': getter() }; + await setter(parameters['value']); + return { 'value': await getter() }; } ); } @@ -300,6 +301,6 @@ abstract class BindingBase { } /// Terminate the Flutter application. -void _exitApplication() { +Future _exitApplication() async { exit(0); } diff --git a/packages/flutter/lib/src/foundation/print.dart b/packages/flutter/lib/src/foundation/print.dart index d1b72550693..7e5750a7fb2 100644 --- a/packages/flutter/lib/src/foundation/print.dart +++ b/packages/flutter/lib/src/foundation/print.dart @@ -54,6 +54,7 @@ const int _kDebugPrintCapacity = 16 * 1024; const Duration _kDebugPrintPauseTime = const Duration(seconds: 1); final Queue _debugPrintBuffer = new Queue(); final Stopwatch _debugPrintStopwatch = new Stopwatch(); +Completer _debugPrintCompleter; bool _debugPrintScheduled = false; void _debugPrintTask() { _debugPrintScheduled = false; @@ -71,11 +72,19 @@ void _debugPrintTask() { _debugPrintScheduled = true; _debugPrintedCharacters = 0; new Timer(_kDebugPrintPauseTime, _debugPrintTask); + _debugPrintCompleter ??= new Completer(); } else { _debugPrintStopwatch.start(); + _debugPrintCompleter?.complete(); + _debugPrintCompleter = null; } } +/// A Future that resolves when there is no longer any buffered content being +/// printed by [debugPrintThrottled] (which is the default implementation for +/// [debugPrint], which is used to report errors to the console). +Future get debugPrintDone => _debugPrintCompleter?.future ?? new Future.value(); + final RegExp _indentPattern = new RegExp('^ *(?:[-+*] |[0-9]+[.):] )?'); enum _WordWrapParseMode { inSpace, inWord, atBreak } /// Wraps the given string at the given width. diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart index 9d7fbd2d9db..f857349f5c2 100644 --- a/packages/flutter/lib/src/rendering/binding.dart +++ b/packages/flutter/lib/src/rendering/binding.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; import 'dart:developer'; import 'dart:ui' as ui show window; @@ -51,12 +52,13 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding, // this service extension only works in checked mode registerBoolServiceExtension( name: 'debugPaint', - getter: () => debugPaintSizeEnabled, + getter: () async => debugPaintSizeEnabled, setter: (bool value) { if (debugPaintSizeEnabled == value) - return; + return new Future.value(); debugPaintSizeEnabled = value; _forceRepaint(); + return endOfFrame; } ); return true; @@ -64,19 +66,20 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding, registerSignalServiceExtension( name: 'debugDumpRenderTree', - callback: debugDumpRenderTree + callback: () { debugDumpRenderTree(); return debugPrintDone; } ); assert(() { // this service extension only works in checked mode registerBoolServiceExtension( name: 'repaintRainbow', - getter: () => debugRepaintRainbowEnabled, + getter: () async => debugRepaintRainbowEnabled, setter: (bool value) { bool repaint = debugRepaintRainbowEnabled && !value; debugRepaintRainbowEnabled = value; if (repaint) _forceRepaint(); + return endOfFrame; } ); return true; @@ -226,8 +229,8 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding, } @override - void reassembleApplication() { - super.reassembleApplication(); + Future reassembleApplication() async { + await super.reassembleApplication(); Timeline.startSync('Dirty Render Tree'); try { renderView.reassemble(); @@ -235,6 +238,7 @@ abstract class RendererBinding extends BindingBase implements SchedulerBinding, Timeline.finishSync(); } handleBeginFrame(null); + await endOfFrame; } @override diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart index ff71f1cf79e..915ad244918 100644 --- a/packages/flutter/lib/src/scheduler/binding.dart +++ b/packages/flutter/lib/src/scheduler/binding.dart @@ -157,8 +157,8 @@ abstract class SchedulerBinding extends BindingBase { super.initServiceExtensions(); registerNumericServiceExtension( name: 'timeDilation', - getter: () => timeDilation, - setter: (double value) { + getter: () async => timeDilation, + setter: (double value) async { timeDilation = value; } ); @@ -441,6 +441,30 @@ abstract class SchedulerBinding extends BindingBase { _postFrameCallbacks.add(callback); } + Completer _nextFrameCompleter; + + /// Returns a Future that completes after the frame completes. + /// + /// If this is called between frames, a frame is immediately scheduled if + /// necessary. If this is called during a frame, the Future completes after + /// the current frame. + /// + /// If the device's screen is currently turned off, this may wait a very long + /// time, since frames are not scheduled while the device's screen is turned + /// off. + Future get endOfFrame { + if (_nextFrameCompleter == null) { + if (schedulerPhase == SchedulerPhase.idle) + scheduleFrame(); + _nextFrameCompleter = new Completer(); + addPostFrameCallback((Duration timeStamp) { + _nextFrameCompleter.complete(); + _nextFrameCompleter = null; + }); + } + return _nextFrameCompleter.future; + } + /// Whether this scheduler has requested that handleBeginFrame be called soon. bool get hasScheduledFrame => _hasScheduledFrame; bool _hasScheduledFrame = false; diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index cfa79ecf24e..aedb0a52e37 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -52,8 +52,8 @@ abstract class ServicesBinding extends BindingBase { // out the cache of resources that have changed. // TODO(ianh): find a way to only evict affected images, not all images name: 'evict', - getter: () => '', - setter: (String value) { + getter: () async => '', + setter: (String value) async { rootBundle.evict(value); imageCache.clear(); } diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index b2ba9169409..82410b8c3a9 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -76,28 +76,30 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren registerSignalServiceExtension( name: 'debugDumpApp', - callback: debugDumpApp + callback: () { debugDumpApp(); return debugPrintDone; } ); registerBoolServiceExtension( name: 'showPerformanceOverlay', - getter: () => WidgetsApp.showPerformanceOverlayOverride, + getter: () => new Future.value(WidgetsApp.showPerformanceOverlayOverride), setter: (bool value) { if (WidgetsApp.showPerformanceOverlayOverride == value) - return; + return new Future.value(); WidgetsApp.showPerformanceOverlayOverride = value; buildOwner.reassemble(renderViewElement); + return endOfFrame; } ); registerBoolServiceExtension( name: 'debugAllowBanner', - getter: () => WidgetsApp.debugAllowBannerOverride, + getter: () => new Future.value(WidgetsApp.debugAllowBannerOverride), setter: (bool value) { if (WidgetsApp.debugAllowBannerOverride == value) - return; + return new Future.value(); WidgetsApp.debugAllowBannerOverride = value; buildOwner.reassemble(renderViewElement); + return endOfFrame; } ); } @@ -365,12 +367,12 @@ abstract class WidgetsBinding extends BindingBase implements GestureBinding, Ren } @override - void reassembleApplication() { + Future reassembleApplication() { _needToReportFirstFrame = true; preventThisFrameFromBeingReportedAsFirstFrame(); if (renderViewElement != null) buildOwner.reassemble(renderViewElement); - super.reassembleApplication(); + return super.reassembleApplication(); } } diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 3d96dbebcb6..2d0898c493d 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart @@ -432,14 +432,12 @@ class AndroidDevice extends Device { bool get supportsScreenshot => true; @override - Future takeScreenshot(File outputFile) { + Future takeScreenshot(File outputFile) { const String remotePath = '/data/local/tmp/flutter_screenshot.png'; - runCheckedSync(adbCommandForDevice(['shell', 'screencap', '-p', remotePath])); runCheckedSync(adbCommandForDevice(['pull', remotePath, outputFile.path])); runCheckedSync(adbCommandForDevice(['shell', 'rm', remotePath])); - - return new Future.value(true); + return new Future.value(); } @override diff --git a/packages/flutter_tools/lib/src/commands/screenshot.dart b/packages/flutter_tools/lib/src/commands/screenshot.dart index 31a91e3001d..81b517d68b1 100644 --- a/packages/flutter_tools/lib/src/commands/screenshot.dart +++ b/packages/flutter_tools/lib/src/commands/screenshot.dart @@ -85,8 +85,7 @@ class ScreenshotCommand extends FlutterCommand { Future runScreenshot(File outputFile) async { outputFile ??= getUniqueFile(fs.currentDirectory, 'flutter', 'png'); try { - if (!await device.takeScreenshot(outputFile)) - throwToolExit('Screenshot failed'); + await device.takeScreenshot(outputFile); } catch (error) { throwToolExit('Error taking screenshot: $error'); } diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index 1fcd99aa73b..0c86acabc45 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -181,8 +181,10 @@ class ServiceProtocolDevFSOperations implements DevFSOperations { @override Future destroy(String fsName) async { - await vmService.vm.invokeRpcRaw('_deleteDevFS', - { 'fsName': fsName }); + await vmService.vm.invokeRpcRaw( + '_deleteDevFS', + params: { 'fsName': fsName }, + ); } @override @@ -195,14 +197,16 @@ class ServiceProtocolDevFSOperations implements DevFSOperations { } String fileContents = BASE64.encode(bytes); try { - return await vmService.vm.invokeRpcRaw('_writeDevFSFile', - { - 'fsName': fsName, - 'path': devicePath, - 'fileContents': fileContents - }); - } catch (e) { - printTrace('DevFS: Failed to write $devicePath: $e'); + return await vmService.vm.invokeRpcRaw( + '_writeDevFSFile', + params: { + 'fsName': fsName, + 'path': devicePath, + 'fileContents': fileContents + }, + ); + } catch (error) { + printTrace('DevFS: Failed to write $devicePath: $error'); } } diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index d2cbfe32c9f..2f74b9caf7c 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -219,7 +219,7 @@ abstract class Device { bool get supportsScreenshot => false; - Future takeScreenshot(File outputFile) => new Future.error('unimplemented'); + Future takeScreenshot(File outputFile) => new Future.error('unimplemented'); /// Find the apps that are currently running on this device. Future> discoverApps() => diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index e6cf2d606b7..8408cc54ab9 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -365,12 +365,11 @@ class IOSDevice extends Device { bool get supportsScreenshot => false; @override - Future takeScreenshot(File outputFile) { + Future takeScreenshot(File outputFile) { // We could use idevicescreenshot here (installed along with the brew // ideviceinstaller tools). It however requires a developer disk image on // the device. - - return new Future.value(false); + return new Future.error('Taking screenshots is not supported on iOS devices. Consider using a simulator instead.'); } } diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 4b0ec378295..eb83e78a6ab 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -587,7 +587,7 @@ class IOSSimulator extends Device { bool get supportsScreenshot => true; @override - Future takeScreenshot(File outputFile) async { + Future takeScreenshot(File outputFile) async { Directory desktopDir = fs.directory(path.join(homeDirPath, 'Desktop')); // 'Simulator Screen Shot Mar 25, 2016, 2.59.43 PM.png' @@ -621,8 +621,6 @@ class IOSSimulator extends Device { File shot = shots.first; outputFile.writeAsBytesSync(shot.readAsBytesSync()); shot.delete(); - - return true; } } diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index cf5754baeb5..32464f18701 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -108,19 +108,34 @@ abstract class ResidentRunner { } Future _screenshot() async { + Status status = logger.startProgress('Taking screenshot...'); File outputFile = getUniqueFile(fs.currentDirectory, 'flutter', 'png'); try { if (vmService != null) await vmService.vm.refreshViews(); - if (isRunningDebug) - await currentView.uiIsolate.flutterDebugAllowBanner(false); - if (!await device.takeScreenshot(outputFile)) - printError('Error taking screenshot.'); - if (isRunningDebug) - await currentView.uiIsolate.flutterDebugAllowBanner(true); + try { + if (isRunningDebug) + await currentView.uiIsolate.flutterDebugAllowBanner(false); + } catch (error) { + status.stop(); + printError(error); + } + try { + await device.takeScreenshot(outputFile); + } finally { + try { + if (isRunningDebug) + await currentView.uiIsolate.flutterDebugAllowBanner(true); + } catch (error) { + status.stop(); + printError(error); + } + } int sizeKB = (await outputFile.length()) ~/ 1024; + status.stop(); printStatus('Screenshot written to ${path.relative(outputFile.path)} (${sizeKB}kB).'); } catch (error) { + status.stop(); printError('Error taking screenshot: $error'); } } diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart index 00ba8589c6e..a7dd70565f9 100644 --- a/packages/flutter_tools/lib/src/vmservice.dart +++ b/packages/flutter_tools/lib/src/vmservice.dart @@ -34,7 +34,7 @@ class VMService { /// /// Requests made via the returns [VMService] time out after [requestTimeout] /// amount of time, which is [kDefaultRequestTimeout] by default. - static Future connect(Uri httpUri, {Duration requestTimeout: kDefaultRequestTimeout}) async { + static Future connect(Uri httpUri, { Duration requestTimeout: kDefaultRequestTimeout }) async { Uri wsUri = httpUri.replace(scheme: 'ws', path: path.join(httpUri.path, 'ws')); WebSocket ws; try { @@ -264,7 +264,7 @@ abstract class ServiceObject { Map params = { 'objectId': id, }; - return _owner.isolate.invokeRpcRaw('getObject', params); + return _owner.isolate.invokeRpcRaw('getObject', params: params); } Future _inProgressReload; @@ -437,7 +437,7 @@ class VM extends ServiceObjectOwner { @override Future> _fetchDirect() async { - return invokeRpcRaw('getVM', {}); + return invokeRpcRaw('getVM'); } @override @@ -567,27 +567,39 @@ class VM extends ServiceObjectOwner { } /// Invoke the RPC and return the raw response. - Future> invokeRpcRaw( - String method, [Map params, Duration timeout]) async { - if (params == null) { - params = {}; - } - + /// + /// If `timeoutFatal` is false, then a timeout will result in a null return + /// value. Otherwise, it results in an exception. + Future> invokeRpcRaw(String method, { + Map params: const {}, + Duration timeout, + bool timeoutFatal: true, + }) async { + assert(params != null); + timeout ??= _vmService._requestTimeout; try { Map result = await _vmService.peer .sendRequest(method, params) - .timeout(timeout ?? _vmService._requestTimeout); + .timeout(timeout); return result; - } on TimeoutException catch(_) { - printError('Request to Dart VM Service timed out: $method($params)'); - rethrow; + } on TimeoutException { + printTrace('Request to Dart VM Service timed out: $method($params)'); + if (timeoutFatal) + throw new TimeoutException('Request to Dart VM Service timed out: $method($params)'); + return null; } } - /// Invoke the RPC and return a ServiceObject response. - Future invokeRpc( - String method, { Map params, Duration timeout }) async { - Map response = await invokeRpcRaw(method, params, timeout); + /// Invoke the RPC and return a [ServiceObject] response. + Future invokeRpc(String method, { + Map params: const {}, + Duration timeout, + }) async { + Map response = await invokeRpcRaw( + method, + params: params, + timeout: timeout, + ); ServiceObject serviceObject = new ServiceObject._fromMap(this, response); if ((serviceObject != null) && (serviceObject._canCache)) { String serviceObjectId = serviceObject.id; @@ -597,19 +609,13 @@ class VM extends ServiceObjectOwner { } /// Create a new development file system on the device. - Future> createDevFS(String fsName) async { - Map response = - await invokeRpcRaw('_createDevFS', { - 'fsName': fsName - }); - return response; + Future> createDevFS(String fsName) { + return invokeRpcRaw('_createDevFS', params: { 'fsName': fsName }); } /// List the development file system son the device. Future> listDevFS() async { - Map response = - await invokeRpcRaw('_listDevFS', {}); - return response['fsNames']; + return (await invokeRpcRaw('_listDevFS'))['fsNames']; } // Write one file into a file system. @@ -619,36 +625,36 @@ class VM extends ServiceObjectOwner { }) { assert(path != null); assert(fileContents != null); - - return invokeRpcRaw('_writeDevFSFile', { - 'fsName': fsName, - 'path': path, - 'fileContents': BASE64.encode(fileContents) - }); + return invokeRpcRaw( + '_writeDevFSFile', + params: { + 'fsName': fsName, + 'path': path, + 'fileContents': BASE64.encode(fileContents), + }, + ); } // Read one file from a file system. - Future> readDevFSFile(String fsName, String path) { - return invokeRpcRaw('_readDevFSFile', { - 'fsName': fsName, - 'path': path - }).then((Map response) { - return BASE64.decode(response['fileContents']); - }); + Future> readDevFSFile(String fsName, String path) async { + Map response = await invokeRpcRaw( + '_readDevFSFile', + params: { + 'fsName': fsName, + 'path': path, + }, + ); + return BASE64.decode(response['fileContents']); } /// The complete list of a file system. - Future> listDevFSFiles(String fsName) { - return invokeRpcRaw('_listDevFSFiles', { - 'fsName': fsName - }).then((Map response) { - return response['files']; - }); + Future> listDevFSFiles(String fsName) async { + return (await invokeRpcRaw('_listDevFSFiles', params: { 'fsName': fsName }))['files']; } /// Delete an existing file system. Future> deleteDevFS(String fsName) { - return invokeRpcRaw('_deleteDevFS', { 'fsName': fsName }); + return invokeRpcRaw('_deleteDevFS', params: { 'fsName': fsName }); } Future runInView(String viewId, @@ -665,20 +671,21 @@ class VM extends ServiceObjectOwner { } Future> clearVMTimeline() { - return invokeRpcRaw('_clearVMTimeline', {}); + return invokeRpcRaw('_clearVMTimeline'); } - Future> setVMTimelineFlags( - List recordedStreams) { + Future> setVMTimelineFlags(List recordedStreams) { assert(recordedStreams != null); - - return invokeRpcRaw('_setVMTimelineFlags', { - 'recordedStreams': recordedStreams - }); + return invokeRpcRaw( + '_setVMTimelineFlags', + params: { + 'recordedStreams': recordedStreams, + }, + ); } Future> getVMTimeline() { - return invokeRpcRaw('_getVMTimeline', {}, kLongRequestTimeout); + return invokeRpcRaw('_getVMTimeline', timeout: kLongRequestTimeout); } Future refreshViews() async { @@ -736,12 +743,15 @@ class Isolate extends ServiceObjectOwner { @override Future> _fetchDirect() { - return invokeRpcRaw('getIsolate', {}); + return invokeRpcRaw('getIsolate'); } /// Invoke the RPC and return the raw response. - Future> invokeRpcRaw( - String method, [Map params, Duration timeout]) { + Future> invokeRpcRaw(String method, { + Map params, + Duration timeout, + bool timeoutFatal: true, + }) { // Inject the 'isolateId' parameter. if (params == null) { params = { @@ -750,21 +760,18 @@ class Isolate extends ServiceObjectOwner { } else { params['isolateId'] = id; } - return vm.invokeRpcRaw(method, params, timeout); + return vm.invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal); } /// Invoke the RPC and return a ServiceObject response. - Future invokeRpc( - String method, Map params) async { - Map response = await invokeRpcRaw(method, params); - return getFromMap(response); + Future invokeRpc(String method, Map params) async { + return getFromMap(await invokeRpcRaw(method, params: params)); } @override void _update(Map map, bool mapIsRef) { - if (mapIsRef) { + if (mapIsRef) return; - } _loaded = true; int startTimeMillis = map['startTime']; @@ -791,7 +798,7 @@ class Isolate extends ServiceObjectOwner { if (packagesPath != null) { arguments['packagesUri'] = packagesPath; } - Map response = await invokeRpcRaw('_reloadSources', arguments); + Map response = await invokeRpcRaw('_reloadSources', params: arguments); return response; } on rpc.RpcException catch(e) { return new Future>.error({ @@ -807,9 +814,14 @@ class Isolate extends ServiceObjectOwner { // Invoke a flutter extension method, if the flutter extension is not // available, returns null. Future> invokeFlutterExtensionRpcRaw( - String method, { Map params, Duration timeout }) async { + String method, { + Map params, + Duration timeout, + bool timeoutFatal: true, + } + ) async { try { - return await invokeRpcRaw(method, params, timeout); + return await invokeRpcRaw(method, params: params, timeout: timeout, timeoutFatal: timeoutFatal); } on rpc.RpcException catch (e) { // If an application is not using the framework if (e.code == rpc_error_code.METHOD_NOT_FOUND) @@ -821,29 +833,42 @@ class Isolate extends ServiceObjectOwner { // Debug dump extension methods. Future> flutterDebugDumpApp() { - return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp'); + return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp', timeout: kLongRequestTimeout); } Future> flutterDebugDumpRenderTree() { - return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree'); + return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree', timeout: kLongRequestTimeout); } Future> flutterToggleDebugPaintSizeEnabled() async { Map state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint'); - if (state != null && state.containsKey('enabled') && state['enabled'] is bool) - state = await invokeFlutterExtensionRpcRaw('ext.flutter.debugPaint', - params: { 'enabled': !state['enabled'] }); + if (state != null && state.containsKey('enabled') && state['enabled'] is bool) { + state = await invokeFlutterExtensionRpcRaw( + 'ext.flutter.debugPaint', + params: { 'enabled': !state['enabled'] }, + timeout: const Duration(milliseconds: 150), + timeoutFatal: false, + ); + } return state; } Future flutterDebugAllowBanner(bool show) async { - await invokeFlutterExtensionRpcRaw('ext.flutter.debugAllowBanner', params: { 'enabled': show }); + await invokeFlutterExtensionRpcRaw( + 'ext.flutter.debugAllowBanner', + params: { 'enabled': show }, + timeout: const Duration(milliseconds: 150), + timeoutFatal: false, + ); } // Reload related extension methods. Future> flutterReassemble() async { - return await invokeFlutterExtensionRpcRaw('ext.flutter.reassemble', - timeout: kLongRequestTimeout); + return await invokeFlutterExtensionRpcRaw( + 'ext.flutter.reassemble', + timeout: kLongRequestTimeout, + timeoutFatal: false, + ); } Future flutterFrameworkPresent() async { @@ -856,16 +881,19 @@ class Isolate extends ServiceObjectOwner { Future> flutterEvictAsset(String assetPath) async { return await invokeFlutterExtensionRpcRaw('ext.flutter.evict', - params: { - 'value': assetPath - } + params: { + 'value': assetPath, + } ); } // Application control extension methods. Future> flutterExit() async { - return await invokeFlutterExtensionRpcRaw('ext.flutter.exit').timeout( - const Duration(seconds: 2), onTimeout: () => null); + return await invokeFlutterExtensionRpcRaw( + 'ext.flutter.exit', + timeout: const Duration(seconds: 2), + timeoutFatal: false, + ); } } diff --git a/packages/flutter_tools/test/devfs_test.dart b/packages/flutter_tools/test/devfs_test.dart index 281a6fa6703..ba5d42568ae 100644 --- a/packages/flutter_tools/test/devfs_test.dart +++ b/packages/flutter_tools/test/devfs_test.dart @@ -297,8 +297,11 @@ class MockVM implements VM { } @override - Future> invokeRpcRaw( - String method, [Map params, Duration timeout]) async { + Future> invokeRpcRaw(String method, { + Map params: const {}, + Duration timeout, + bool timeoutFatal: true, + }) async { _service.messages.add('$method $params'); return {'success': true}; }