Revert "Remove all service extensions from release mode (#23038)" (#23290)

This reverts commit 8e2ca93f52.

Fixes https://github.com/flutter/flutter/issues/23172
This commit is contained in:
Michael Goderbauer 2018-10-19 11:48:15 +02:00 committed by GitHub
parent b357b55904
commit bb0290a4dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 197 deletions

View file

@ -93,7 +93,9 @@ abstract class BindingBase {
/// Implementations of this method must call their superclass
/// implementation.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
/// Service extensions are only exposed when the observatory is
/// included in the build, which should only happen in checked mode
/// and in profile mode.
///
/// See also:
///
@ -102,23 +104,18 @@ abstract class BindingBase {
@mustCallSuper
void initServiceExtensions() {
assert(!_debugServiceExtensionsRegistered);
assert(() {
registerSignalServiceExtension(
name: 'reassemble',
callback: reassembleApplication,
);
return true;
}());
const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
if (!isReleaseMode) {
registerSignalServiceExtension(
name: 'exit',
callback: _exitApplication,
);
}
registerSignalServiceExtension(
name: 'reassemble',
callback: reassembleApplication,
);
registerSignalServiceExtension(
name: 'exit',
callback: _exitApplication,
);
registerSignalServiceExtension(
name: 'frameworkPresent',
callback: () => Future<void>.value(),
);
assert(() {
registerServiceExtension(
name: 'platformOverride',
@ -242,8 +239,6 @@ abstract class BindingBase {
/// no value.
///
/// Calls the `callback` callback when the service extension is called.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected
void registerSignalServiceExtension({
@required String name,
@ -272,8 +267,6 @@ abstract class BindingBase {
///
/// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected
void registerBoolServiceExtension({
@required String name,
@ -304,8 +297,6 @@ abstract class BindingBase {
///
/// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected
void registerNumericServiceExtension({
@required String name,
@ -335,8 +326,6 @@ abstract class BindingBase {
///
/// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected
void registerStringServiceExtension({
@required String name,
@ -356,51 +345,16 @@ abstract class BindingBase {
);
}
/// Registers a service extension method with the given name (full name
/// "ext.flutter.name").
///
/// The given callback is called when the extension method is called. The
/// callback must return a [Future] that either eventually completes to a
/// return value in the form of a name/value map where the values can all be
/// converted to JSON using `json.encode()` (see [JsonEncoder]), or fails. In
/// case of failure, the failure is reported to the remote caller and is
/// dumped to the logs.
/// Registers a service extension method with the given name (full
/// name "ext.flutter.name"). The given callback is called when the
/// extension method is called. The callback must return a [Future]
/// that either eventually completes to a return value in the form
/// of a name/value map where the values can all be converted to
/// JSON using `json.encode()` (see [JsonEncoder]), or fails. In case of failure, the
/// failure is reported to the remote caller and is dumped to the
/// logs.
///
/// The returned map will be mutated.
///
/// {@template flutter.foundation.bindingBase.registerServiceExtension}
/// A registered service extension can only be activated if the vm-service
/// is included in the build, which only happens in debug and profile mode.
/// Although a service extension cannot be used in release mode its code may
/// still be included in the Dart snapshot and blow up binary size if it is
/// not wrapped in a guard that allows the tree shaker to remove it (see
/// sample code below).
///
/// ## Sample Code
///
/// The following code registers a service extension that is only included in
/// debug builds:
///
/// ```dart
/// assert(() {
/// // Register your service extension here.
/// return true;
/// }());
///
/// ```
///
/// A service extension registered with the following code snippet is
/// available in debug and profile mode:
///
/// ```dart
/// if (!const bool.fromEnvironment('dart.vm.product')) {
// // Register your service extension here.
// }
/// ```
///
/// Both guards ensure that Dart's tree shaker can remove the code for the
/// service extension in release builds.
/// {@endTemplate}
@protected
void registerServiceExtension({
@required String name,

View file

@ -51,7 +51,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
super.initServiceExtensions();
assert(() {
// these service extensions only work in debug mode
// these service extensions only work in checked mode
registerBoolServiceExtension(
name: 'debugPaint',
getter: () async => debugPaintSizeEnabled,
@ -60,17 +60,17 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
return Future<void>.value();
debugPaintSizeEnabled = value;
return _forceRepaint();
},
}
);
registerBoolServiceExtension(
name: 'debugPaintBaselinesEnabled',
getter: () async => debugPaintBaselinesEnabled,
setter: (bool value) {
name: 'debugPaintBaselinesEnabled',
getter: () async => debugPaintBaselinesEnabled,
setter: (bool value) {
if (debugPaintBaselinesEnabled == value)
return Future<void>.value();
debugPaintBaselinesEnabled = value;
return _forceRepaint();
},
}
);
registerBoolServiceExtension(
name: 'repaintRainbow',
@ -83,43 +83,28 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
return Future<void>.value();
},
);
registerSignalServiceExtension(
name: 'debugDumpLayerTree',
callback: () {
debugDumpLayerTree();
return debugPrintDone;
},
);
return true;
}());
const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
if (!isReleaseMode) {
// these service extensions work in debug or profile mode
registerSignalServiceExtension(
name: 'debugDumpRenderTree',
callback: () {
debugDumpRenderTree();
return debugPrintDone;
},
);
registerSignalServiceExtension(
name: 'debugDumpRenderTree',
callback: () { debugDumpRenderTree(); return debugPrintDone; }
);
registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInTraversalOrder',
callback: () {
debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder);
return debugPrintDone;
},
);
registerSignalServiceExtension(
name: 'debugDumpLayerTree',
callback: () { debugDumpLayerTree(); return debugPrintDone; }
);
registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInInverseHitTestOrder',
callback: () {
debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest);
return debugPrintDone;
},
);
}
registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInTraversalOrder',
callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder); return debugPrintDone; }
);
registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInInverseHitTestOrder',
callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest); return debugPrintDone; }
);
}
/// Creates a [RenderView] object to be the root of the

View file

@ -203,17 +203,13 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
@override
void initServiceExtensions() {
super.initServiceExtensions();
const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
if (!isReleaseMode) {
registerNumericServiceExtension(
name: 'timeDilation',
getter: () async => timeDilation,
setter: (double value) async {
timeDilation = value;
},
);
}
registerNumericServiceExtension(
name: 'timeDilation',
getter: () async => timeDilation,
setter: (double value) async {
timeDilation = value;
}
);
}
/// Whether the application is visible, and if so, whether it is currently

View file

@ -86,21 +86,17 @@ mixin ServicesBinding on BindingBase {
@override
void initServiceExtensions() {
super.initServiceExtensions();
assert(() {
registerStringServiceExtension(
// ext.flutter.evict value=foo.png will cause foo.png to be evicted from
// the rootBundle cache and cause the entire image cache to be cleared.
// This is used by hot reload mode to clear out the cache of resources
// that have changed.
name: 'evict',
getter: () async => '',
setter: (String value) async {
evict(value);
},
);
return true;
}());
registerStringServiceExtension(
// ext.flutter.evict value=foo.png will cause foo.png to be evicted from
// the rootBundle cache and cause the entire image cache to be cleared.
// This is used by hot reload mode to clear out the cache of resources
// that have changed.
name: 'evict',
getter: () async => '',
setter: (String value) async {
evict(value);
}
);
}
/// Called in response to the `ext.flutter.evict` service extension.

View file

@ -265,41 +265,37 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
void initServiceExtensions() {
super.initServiceExtensions();
const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
if (!isReleaseMode) {
registerSignalServiceExtension(
name: 'debugDumpApp',
callback: () {
debugDumpApp();
return debugPrintDone;
},
);
registerSignalServiceExtension(
name: 'debugDumpApp',
callback: () {
debugDumpApp();
return debugPrintDone;
}
);
registerBoolServiceExtension(
name: 'showPerformanceOverlay',
getter: () =>
Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
setter: (bool value) {
if (WidgetsApp.showPerformanceOverlayOverride == value)
return Future<void>.value();
WidgetsApp.showPerformanceOverlayOverride = value;
return _forceRebuild();
},
);
}
registerBoolServiceExtension(
name: 'showPerformanceOverlay',
getter: () => Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
setter: (bool value) {
if (WidgetsApp.showPerformanceOverlayOverride == value)
return Future<void>.value();
WidgetsApp.showPerformanceOverlayOverride = value;
return _forceRebuild();
}
);
registerBoolServiceExtension(
name: 'debugAllowBanner',
getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
setter: (bool value) {
if (WidgetsApp.debugAllowBannerOverride == value)
return Future<void>.value();
WidgetsApp.debugAllowBannerOverride = value;
return _forceRebuild();
}
);
assert(() {
registerBoolServiceExtension(
name: 'debugAllowBanner',
getter: () => Future<bool>.value(WidgetsApp.debugAllowBannerOverride),
setter: (bool value) {
if (WidgetsApp.debugAllowBannerOverride == value)
return Future<void>.value();
WidgetsApp.debugAllowBannerOverride = value;
return _forceRebuild();
},
);
// Expose the ability to send Widget rebuilds as [Timeline] events.
registerBoolServiceExtension(
name: 'profileWidgetBuilds',
@ -307,13 +303,25 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
setter: (bool value) async {
if (debugProfileBuildsEnabled != value)
debugProfileBuildsEnabled = value;
},
}
);
WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);
return true;
}());
// This service extension is deprecated and will be removed by 7/1/2018.
// Use ext.flutter.inspector.show instead.
registerBoolServiceExtension(
name: 'debugWidgetInspector',
getter: () async => WidgetsApp.debugShowWidgetInspectorOverride,
setter: (bool value) {
if (WidgetsApp.debugShowWidgetInspectorOverride == value)
return Future<void>.value();
WidgetsApp.debugShowWidgetInspectorOverride = value;
return _forceRebuild();
}
);
WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);
}
Future<void> _forceRebuild() {

View file

@ -917,11 +917,13 @@ mixin WidgetInspectorService {
/// Called to register service extensions.
///
/// Service extensions are only exposed when the observatory is
/// included in the build, which should only happen in checked mode
/// and in profile mode.
///
/// See also:
///
/// * <https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses>
/// * [BindingBase.initServiceExtensions], which explains when service
/// extensions can be used.
void initServiceExtensions(
_RegisterServiceExtensionCallback registerServiceExtensionCallback) {
_registerServiceExtensionCallback = registerServiceExtensionCallback;
@ -1021,33 +1023,36 @@ mixin WidgetInspectorService {
name: 'isWidgetCreationTracked',
callback: isWidgetCreationTracked,
);
registerServiceExtension(
name: 'screenshot',
callback: (Map<String, String> parameters) async {
assert(parameters.containsKey('id'));
assert(parameters.containsKey('width'));
assert(parameters.containsKey('height'));
assert(() {
registerServiceExtension(
name: 'screenshot',
callback: (Map<String, String> parameters) async {
assert(parameters.containsKey('id'));
assert(parameters.containsKey('width'));
assert(parameters.containsKey('height'));
final ui.Image image = await screenshot(
toObject(parameters['id']),
width: double.parse(parameters['width']),
height: double.parse(parameters['height']),
margin: parameters.containsKey('margin') ?
double.parse(parameters['margin']) : 0.0,
maxPixelRatio: parameters.containsKey('maxPixelRatio') ?
double.parse(parameters['maxPixelRatio']) : 1.0,
debugPaint: parameters['debugPaint'] == 'true',
);
if (image == null) {
return <String, Object>{'result': null};
}
final ByteData byteData = await image.toByteData(format:ui.ImageByteFormat.png);
final ui.Image image = await screenshot(
toObject(parameters['id']),
width: double.parse(parameters['width']),
height: double.parse(parameters['height']),
margin: parameters.containsKey('margin') ?
double.parse(parameters['margin']) : 0.0,
maxPixelRatio: parameters.containsKey('maxPixelRatio') ?
double.parse(parameters['maxPixelRatio']) : 1.0,
debugPaint: parameters['debugPaint'] == 'true',
);
if (image == null) {
return <String, Object>{'result': null};
}
final ByteData byteData = await image.toByteData(format:ui.ImageByteFormat.png);
return <String, Object>{
'result': base64.encoder.convert(Uint8List.view(byteData.buffer)),
};
},
);
return <String, Object>{
'result': base64.encoder.convert(Uint8List.view(byteData.buffer)),
};
},
);
return true;
}());
}
/// Clear all InspectorService object references.

View file

@ -359,6 +359,13 @@ void main() {
expect(binding.extensions.containsKey('exit'), isTrue);
});
test('Service extensions - frameworkPresent', () async {
Map<String, dynamic> result;
result = await binding.testExtension('frameworkPresent', <String, String>{});
expect(result, <String, String>{});
});
test('Service extensions - platformOverride', () async {
Map<String, dynamic> result;
@ -482,6 +489,29 @@ void main() {
expect(binding.frameScheduled, isFalse);
});
test('Service extensions - debugWidgetInspector', () async {
Map<String, dynamic> result;
expect(binding.frameScheduled, isFalse);
expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
result = await binding.testExtension('debugWidgetInspector', <String, String>{});
expect(result, <String, String>{ 'enabled': 'false' });
expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
result = await binding.testExtension('debugWidgetInspector', <String, String>{ 'enabled': 'true' });
expect(result, <String, String>{ 'enabled': 'true' });
expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
result = await binding.testExtension('debugWidgetInspector', <String, String>{});
expect(result, <String, String>{ 'enabled': 'true' });
expect(WidgetsApp.debugShowWidgetInspectorOverride, true);
result = await binding.testExtension('debugWidgetInspector', <String, String>{ 'enabled': 'false' });
expect(result, <String, String>{ 'enabled': 'false' });
expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
result = await binding.testExtension('debugWidgetInspector', <String, String>{});
expect(result, <String, String>{ 'enabled': 'false' });
expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
expect(binding.frameScheduled, isFalse);
});
test('Service extensions - timeDilation', () async {
Map<String, dynamic> result;
@ -511,7 +541,7 @@ void main() {
// If you add a service extension... TEST IT! :-)
// ...then increment this number.
expect(binding.extensions.length, 37);
expect(binding.extensions.length, 39);
expect(console, isEmpty);
debugPrint = debugPrintThrottled;

View file

@ -1296,6 +1296,10 @@ class Isolate extends ServiceObjectOwner {
);
}
Future<bool> flutterFrameworkPresent() async {
return await invokeFlutterExtensionRpcRaw('ext.flutter.frameworkPresent') != null;
}
Future<Map<String, dynamic>> uiWindowScheduleFrame() async {
return await invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame');
}