Unverified Commit dc36195c authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Reland "Remove all service extensions from release mode (#23038)" (#23291)

parent a6a16078
...@@ -93,9 +93,7 @@ abstract class BindingBase { ...@@ -93,9 +93,7 @@ abstract class BindingBase {
/// Implementations of this method must call their superclass /// Implementations of this method must call their superclass
/// implementation. /// implementation.
/// ///
/// Service extensions are only exposed when the observatory is /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
/// included in the build, which should only happen in checked mode
/// and in profile mode.
/// ///
/// See also: /// See also:
/// ///
...@@ -104,18 +102,23 @@ abstract class BindingBase { ...@@ -104,18 +102,23 @@ abstract class BindingBase {
@mustCallSuper @mustCallSuper
void initServiceExtensions() { void initServiceExtensions() {
assert(!_debugServiceExtensionsRegistered); assert(!_debugServiceExtensionsRegistered);
registerSignalServiceExtension(
name: 'reassemble', assert(() {
callback: reassembleApplication, registerSignalServiceExtension(
); name: 'reassemble',
registerSignalServiceExtension( callback: reassembleApplication,
name: 'exit', );
callback: _exitApplication, return true;
); }());
registerSignalServiceExtension(
name: 'frameworkPresent', const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
callback: () => Future<void>.value(), if (!isReleaseMode) {
); registerSignalServiceExtension(
name: 'exit',
callback: _exitApplication,
);
}
assert(() { assert(() {
registerServiceExtension( registerServiceExtension(
name: 'platformOverride', name: 'platformOverride',
...@@ -239,6 +242,8 @@ abstract class BindingBase { ...@@ -239,6 +242,8 @@ abstract class BindingBase {
/// no value. /// no value.
/// ///
/// Calls the `callback` callback when the service extension is called. /// Calls the `callback` callback when the service extension is called.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected @protected
void registerSignalServiceExtension({ void registerSignalServiceExtension({
@required String name, @required String name,
...@@ -267,6 +272,8 @@ abstract class BindingBase { ...@@ -267,6 +272,8 @@ abstract class BindingBase {
/// ///
/// Calls the `setter` callback with the new value when the /// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value. /// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected @protected
void registerBoolServiceExtension({ void registerBoolServiceExtension({
@required String name, @required String name,
...@@ -297,6 +304,8 @@ abstract class BindingBase { ...@@ -297,6 +304,8 @@ abstract class BindingBase {
/// ///
/// Calls the `setter` callback with the new value when the /// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value. /// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected @protected
void registerNumericServiceExtension({ void registerNumericServiceExtension({
@required String name, @required String name,
...@@ -326,6 +335,8 @@ abstract class BindingBase { ...@@ -326,6 +335,8 @@ abstract class BindingBase {
/// ///
/// Calls the `setter` callback with the new value when the /// Calls the `setter` callback with the new value when the
/// service extension method is called with a new value. /// service extension method is called with a new value.
///
/// {@macro flutter.foundation.bindingBase.registerServiceExtension}
@protected @protected
void registerStringServiceExtension({ void registerStringServiceExtension({
@required String name, @required String name,
...@@ -345,16 +356,51 @@ abstract class BindingBase { ...@@ -345,16 +356,51 @@ abstract class BindingBase {
); );
} }
/// Registers a service extension method with the given name (full /// Registers a service extension method with the given name (full name
/// name "ext.flutter.name"). The given callback is called when the /// "ext.flutter.name").
/// extension method is called. The callback must return a [Future] ///
/// that either eventually completes to a return value in the form /// The given callback is called when the extension method is called. The
/// of a name/value map where the values can all be converted to /// callback must return a [Future] that either eventually completes to a
/// JSON using `json.encode()` (see [JsonEncoder]), or fails. In case of failure, the /// return value in the form of a name/value map where the values can all be
/// failure is reported to the remote caller and is dumped to the /// converted to JSON using `json.encode()` (see [JsonEncoder]), or fails. In
/// logs. /// case of failure, the failure is reported to the remote caller and is
/// dumped to the logs.
/// ///
/// The returned map will be mutated. /// 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 @protected
void registerServiceExtension({ void registerServiceExtension({
@required String name, @required String name,
......
...@@ -51,7 +51,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti ...@@ -51,7 +51,7 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
super.initServiceExtensions(); super.initServiceExtensions();
assert(() { assert(() {
// these service extensions only work in checked mode // these service extensions only work in debug mode
registerBoolServiceExtension( registerBoolServiceExtension(
name: 'debugPaint', name: 'debugPaint',
getter: () async => debugPaintSizeEnabled, getter: () async => debugPaintSizeEnabled,
...@@ -60,17 +60,17 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti ...@@ -60,17 +60,17 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
return Future<void>.value(); return Future<void>.value();
debugPaintSizeEnabled = value; debugPaintSizeEnabled = value;
return _forceRepaint(); return _forceRepaint();
} },
); );
registerBoolServiceExtension( registerBoolServiceExtension(
name: 'debugPaintBaselinesEnabled', name: 'debugPaintBaselinesEnabled',
getter: () async => debugPaintBaselinesEnabled, getter: () async => debugPaintBaselinesEnabled,
setter: (bool value) { setter: (bool value) {
if (debugPaintBaselinesEnabled == value) if (debugPaintBaselinesEnabled == value)
return Future<void>.value(); return Future<void>.value();
debugPaintBaselinesEnabled = value; debugPaintBaselinesEnabled = value;
return _forceRepaint(); return _forceRepaint();
} },
); );
registerBoolServiceExtension( registerBoolServiceExtension(
name: 'repaintRainbow', name: 'repaintRainbow',
...@@ -83,28 +83,43 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti ...@@ -83,28 +83,43 @@ mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, Semanti
return Future<void>.value(); return Future<void>.value();
}, },
); );
registerSignalServiceExtension(
name: 'debugDumpLayerTree',
callback: () {
debugDumpLayerTree();
return debugPrintDone;
},
);
return true; return true;
}()); }());
registerSignalServiceExtension( const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
name: 'debugDumpRenderTree', if (!isReleaseMode) {
callback: () { debugDumpRenderTree(); return debugPrintDone; } // these service extensions work in debug or profile mode
); registerSignalServiceExtension(
name: 'debugDumpRenderTree',
registerSignalServiceExtension( callback: () {
name: 'debugDumpLayerTree', debugDumpRenderTree();
callback: () { debugDumpLayerTree(); return debugPrintDone; } return debugPrintDone;
); },
);
registerSignalServiceExtension( registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInTraversalOrder', name: 'debugDumpSemanticsTreeInTraversalOrder',
callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder); return debugPrintDone; } callback: () {
); debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder);
return debugPrintDone;
},
);
registerSignalServiceExtension( registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInInverseHitTestOrder', name: 'debugDumpSemanticsTreeInInverseHitTestOrder',
callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest); return debugPrintDone; } callback: () {
); debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest);
return debugPrintDone;
},
);
}
} }
/// Creates a [RenderView] object to be the root of the /// Creates a [RenderView] object to be the root of the
......
...@@ -203,13 +203,17 @@ mixin SchedulerBinding on BindingBase, ServicesBinding { ...@@ -203,13 +203,17 @@ mixin SchedulerBinding on BindingBase, ServicesBinding {
@override @override
void initServiceExtensions() { void initServiceExtensions() {
super.initServiceExtensions(); super.initServiceExtensions();
registerNumericServiceExtension(
name: 'timeDilation', const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
getter: () async => timeDilation, if (!isReleaseMode) {
setter: (double value) async { registerNumericServiceExtension(
timeDilation = value; name: 'timeDilation',
} getter: () async => timeDilation,
); setter: (double value) async {
timeDilation = value;
},
);
}
} }
/// Whether the application is visible, and if so, whether it is currently /// Whether the application is visible, and if so, whether it is currently
......
...@@ -86,17 +86,21 @@ mixin ServicesBinding on BindingBase { ...@@ -86,17 +86,21 @@ mixin ServicesBinding on BindingBase {
@override @override
void initServiceExtensions() { void initServiceExtensions() {
super.initServiceExtensions(); super.initServiceExtensions();
registerStringServiceExtension(
// ext.flutter.evict value=foo.png will cause foo.png to be evicted from assert(() {
// the rootBundle cache and cause the entire image cache to be cleared. registerStringServiceExtension(
// This is used by hot reload mode to clear out the cache of resources // ext.flutter.evict value=foo.png will cause foo.png to be evicted from
// that have changed. // the rootBundle cache and cause the entire image cache to be cleared.
name: 'evict', // This is used by hot reload mode to clear out the cache of resources
getter: () async => '', // that have changed.
setter: (String value) async { name: 'evict',
evict(value); getter: () async => '',
} setter: (String value) async {
); evict(value);
},
);
return true;
}());
} }
/// Called in response to the `ext.flutter.evict` service extension. /// Called in response to the `ext.flutter.evict` service extension.
......
...@@ -265,37 +265,41 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB ...@@ -265,37 +265,41 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
void initServiceExtensions() { void initServiceExtensions() {
super.initServiceExtensions(); super.initServiceExtensions();
registerSignalServiceExtension( const bool isReleaseMode = bool.fromEnvironment('dart.vm.product');
name: 'debugDumpApp', if (!isReleaseMode) {
callback: () { registerSignalServiceExtension(
debugDumpApp(); name: 'debugDumpApp',
return debugPrintDone; callback: () {
} debugDumpApp();
); return debugPrintDone;
},
registerBoolServiceExtension( );
name: 'showPerformanceOverlay',
getter: () => Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride), registerBoolServiceExtension(
setter: (bool value) { name: 'showPerformanceOverlay',
if (WidgetsApp.showPerformanceOverlayOverride == value) getter: () =>
return Future<void>.value(); Future<bool>.value(WidgetsApp.showPerformanceOverlayOverride),
WidgetsApp.showPerformanceOverlayOverride = value; setter: (bool value) {
return _forceRebuild(); 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(() { 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. // Expose the ability to send Widget rebuilds as [Timeline] events.
registerBoolServiceExtension( registerBoolServiceExtension(
name: 'profileWidgetBuilds', name: 'profileWidgetBuilds',
...@@ -303,25 +307,26 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB ...@@ -303,25 +307,26 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB
setter: (bool value) async { setter: (bool value) async {
if (debugProfileBuildsEnabled != value) if (debugProfileBuildsEnabled != value)
debugProfileBuildsEnabled = value; debugProfileBuildsEnabled = value;
} },
); );
return true;
}());
// This service extension is deprecated and will be removed by 7/1/2018. // This service extension is deprecated and will be removed by 12/1/2018.
// Use ext.flutter.inspector.show instead. // Use ext.flutter.inspector.show instead.
registerBoolServiceExtension( registerBoolServiceExtension(
name: 'debugWidgetInspector', name: 'debugWidgetInspector',
getter: () async => WidgetsApp.debugShowWidgetInspectorOverride, getter: () async => WidgetsApp.debugShowWidgetInspectorOverride,
setter: (bool value) { setter: (bool value) {
if (WidgetsApp.debugShowWidgetInspectorOverride == value) if (WidgetsApp.debugShowWidgetInspectorOverride == value)
return Future<void>.value(); return Future<void>.value();
WidgetsApp.debugShowWidgetInspectorOverride = value; WidgetsApp.debugShowWidgetInspectorOverride = value;
return _forceRebuild(); return _forceRebuild();
} }
); );
WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension); WidgetInspectorService.instance.initServiceExtensions(registerServiceExtension);
return true;
}());
} }
Future<void> _forceRebuild() { Future<void> _forceRebuild() {
......
...@@ -917,13 +917,11 @@ mixin WidgetInspectorService { ...@@ -917,13 +917,11 @@ mixin WidgetInspectorService {
/// Called to register service extensions. /// 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: /// See also:
/// ///
/// * <https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses> /// * <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( void initServiceExtensions(
_RegisterServiceExtensionCallback registerServiceExtensionCallback) { _RegisterServiceExtensionCallback registerServiceExtensionCallback) {
_registerServiceExtensionCallback = registerServiceExtensionCallback; _registerServiceExtensionCallback = registerServiceExtensionCallback;
...@@ -1023,36 +1021,33 @@ mixin WidgetInspectorService { ...@@ -1023,36 +1021,33 @@ mixin WidgetInspectorService {
name: 'isWidgetCreationTracked', name: 'isWidgetCreationTracked',
callback: isWidgetCreationTracked, callback: isWidgetCreationTracked,
); );
assert(() { registerServiceExtension(
registerServiceExtension( name: 'screenshot',
name: 'screenshot', callback: (Map<String, String> parameters) async {
callback: (Map<String, String> parameters) async { assert(parameters.containsKey('id'));
assert(parameters.containsKey('id')); assert(parameters.containsKey('width'));
assert(parameters.containsKey('width')); assert(parameters.containsKey('height'));
assert(parameters.containsKey('height'));
final ui.Image image = await screenshot(
final ui.Image image = await screenshot( toObject(parameters['id']),
toObject(parameters['id']), width: double.parse(parameters['width']),
width: double.parse(parameters['width']), height: double.parse(parameters['height']),
height: double.parse(parameters['height']), margin: parameters.containsKey('margin') ?
margin: parameters.containsKey('margin') ? double.parse(parameters['margin']) : 0.0,
double.parse(parameters['margin']) : 0.0, maxPixelRatio: parameters.containsKey('maxPixelRatio') ?
maxPixelRatio: parameters.containsKey('maxPixelRatio') ? double.parse(parameters['maxPixelRatio']) : 1.0,
double.parse(parameters['maxPixelRatio']) : 1.0, debugPaint: parameters['debugPaint'] == 'true',
debugPaint: parameters['debugPaint'] == 'true', );
); if (image == null) {
if (image == null) { return <String, Object>{'result': null};
return <String, Object>{'result': null}; }
} final ByteData byteData = await image.toByteData(format:ui.ImageByteFormat.png);
final ByteData byteData = await image.toByteData(format:ui.ImageByteFormat.png);
return <String, Object>{ return <String, Object>{
'result': base64.encoder.convert(Uint8List.view(byteData.buffer)), 'result': base64.encoder.convert(Uint8List.view(byteData.buffer)),
}; };
}, },
); );
return true;
}());
} }
/// Clear all InspectorService object references. /// Clear all InspectorService object references.
......
...@@ -359,13 +359,6 @@ void main() { ...@@ -359,13 +359,6 @@ void main() {
expect(binding.extensions.containsKey('exit'), isTrue); 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 { test('Service extensions - platformOverride', () async {
Map<String, dynamic> result; Map<String, dynamic> result;
...@@ -491,7 +484,6 @@ void main() { ...@@ -491,7 +484,6 @@ void main() {
test('Service extensions - debugWidgetInspector', () async { test('Service extensions - debugWidgetInspector', () async {
Map<String, dynamic> result; Map<String, dynamic> result;
expect(binding.frameScheduled, isFalse); expect(binding.frameScheduled, isFalse);
expect(WidgetsApp.debugShowWidgetInspectorOverride, false); expect(WidgetsApp.debugShowWidgetInspectorOverride, false);
result = await binding.testExtension('debugWidgetInspector', <String, String>{}); result = await binding.testExtension('debugWidgetInspector', <String, String>{});
...@@ -541,7 +533,7 @@ void main() { ...@@ -541,7 +533,7 @@ void main() {
// If you add a service extension... TEST IT! :-) // If you add a service extension... TEST IT! :-)
// ...then increment this number. // ...then increment this number.
expect(binding.extensions.length, 39); expect(binding.extensions.length, 38);
expect(console, isEmpty); expect(console, isEmpty);
debugPrint = debugPrintThrottled; debugPrint = debugPrintThrottled;
......
...@@ -1296,10 +1296,6 @@ class Isolate extends ServiceObjectOwner { ...@@ -1296,10 +1296,6 @@ class Isolate extends ServiceObjectOwner {
); );
} }
Future<bool> flutterFrameworkPresent() async {
return await invokeFlutterExtensionRpcRaw('ext.flutter.frameworkPresent') != null;
}
Future<Map<String, dynamic>> uiWindowScheduleFrame() async { Future<Map<String, dynamic>> uiWindowScheduleFrame() async {
return await invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame'); return await invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame');
} }
......
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