Unverified Commit 0a35cf95 authored by Polina Cherkasova's avatar Polina Cherkasova Committed by GitHub

Instrument State, Layer, RenderObject and Element. (#111328)

parent 6e491d88
......@@ -215,11 +215,11 @@ class ChangeNotifier implements Listenable {
void addListener(VoidCallback listener) {
assert(ChangeNotifier.debugAssertNotDisposed(this));
if (kFlutterMemoryAllocationsEnabled && !_creationDispatched) {
MemoryAllocations.instance.dispatchObjectEvent(ObjectCreated(
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterFoundationLibrary,
className: 'ChangeNotifier',
className: '$ChangeNotifier',
object: this,
));
);
_creationDispatched = true;
}
if (_count == _listeners.length) {
......@@ -326,7 +326,7 @@ class ChangeNotifier implements Listenable {
return true;
}());
if (kFlutterMemoryAllocationsEnabled && _creationDispatched) {
MemoryAllocations.instance.dispatchObjectEvent(ObjectDisposed(object: this));
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
_listeners = _emptyListeners;
_count = 0;
......@@ -464,11 +464,11 @@ class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
/// Creates a [ChangeNotifier] that wraps this value.
ValueNotifier(this._value) {
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectEvent(ObjectCreated(
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterFoundationLibrary,
className: 'ValueNotifier',
className: '$ValueNotifier',
object: this,
));
);
}
_creationDispatched = true;
}
......
......@@ -258,6 +258,34 @@ class MemoryAllocations {
_tryDefragmentListeners();
}
/// Create [ObjectCreated] and invoke [dispatchObjectEvent] if there are listeners.
///
/// This method is more efficient than [dispatchObjectEvent] if the event object is not created yet.
void dispatchObjectCreated({
required String library,
required String className,
required Object object,
}) {
if (!hasListeners) {
return;
}
dispatchObjectEvent(ObjectCreated(
library: library,
className: className,
object: object,
));
}
/// Create [ObjectDisposed] and invoke [dispatchObjectEvent] if there are listeners.
///
/// This method is more efficient than [dispatchObjectEvent] if the event object is not created yet.
void dispatchObjectDisposed({required Object object}) {
if (!hasListeners) {
return;
}
dispatchObjectEvent(ObjectDisposed(object: object));
}
void _subscribeToSdkObjects() {
assert(ui.Image.onCreate == null);
assert(ui.Image.onDispose == null);
......@@ -283,7 +311,7 @@ class MemoryAllocations {
void _imageOnCreate(ui.Image image) {
dispatchObjectEvent(ObjectCreated(
library: _dartUiLibrary,
className: 'Image',
className: '${ui.Image}',
object: image,
));
}
......@@ -291,7 +319,7 @@ class MemoryAllocations {
void _pictureOnCreate(ui.Picture picture) {
dispatchObjectEvent(ObjectCreated(
library: _dartUiLibrary,
className: 'Picture',
className: '${ui.Picture}',
object: picture,
));
}
......
......@@ -73,6 +73,8 @@ class AnnotationResult<T> {
}
}
const String _flutterRenderingLibrary = 'package:flutter/rendering.dart';
/// A composited layer.
///
/// During painting, the render tree generates a tree of composited layers that
......@@ -135,6 +137,17 @@ class AnnotationResult<T> {
/// * [RenderView.compositeFrame], which implements this recomposition protocol
/// for painting [RenderObject] trees on the display.
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
/// Creates an instance of Layer.
Layer() {
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterRenderingLibrary,
className: '$Layer',
object: this,
);
}
}
final Map<int, VoidCallback> _callbacks = <int, VoidCallback>{};
static int _nextCallbackId = 0;
......@@ -320,6 +333,9 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
_debugDisposed = true;
return true;
}());
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
_engineLayer?.dispose();
_engineLayer = null;
}
......
......@@ -1248,6 +1248,8 @@ class PipelineOwner {
}
}
const String _flutterRenderingLibrary = 'package:flutter/rendering.dart';
/// An object in the render tree.
///
/// The [RenderObject] class hierarchy is the core of the rendering
......@@ -1376,6 +1378,13 @@ class PipelineOwner {
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
/// Initializes internal fields for subclasses.
RenderObject() {
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterRenderingLibrary,
className: '$RenderObject',
object: this,
);
}
_needsCompositing = isRepaintBoundary || alwaysNeedsCompositing;
_wasRepaintBoundary = isRepaintBoundary;
}
......@@ -1436,6 +1445,9 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
@mustCallSuper
void dispose() {
assert(!_debugDisposed);
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
_layerHandle.layer = null;
assert(() {
// TODO(dnfield): Enable this assert once clients have had a chance to
......
......@@ -816,6 +816,8 @@ enum _StateLifecycle {
/// The signature of [State.setState] functions.
typedef StateSetter = void Function(VoidCallback fn);
const String _flutterWidgetsLibrary = 'package:flutter/widgets.dart';
/// The logic and internal state for a [StatefulWidget].
///
/// State is information that (1) can be read synchronously when the widget is
......@@ -999,6 +1001,13 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
@mustCallSuper
void initState() {
assert(_debugLifecycleState == _StateLifecycle.created);
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterWidgetsLibrary,
className: '$State',
object: this,
);
}
}
/// Called whenever the widget configuration changes.
......@@ -1235,6 +1244,9 @@ abstract class State<T extends StatefulWidget> with Diagnosticable {
_debugLifecycleState = _StateLifecycle.defunct;
return true;
}());
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
}
/// Describes the part of the user interface represented by this widget.
......@@ -3215,7 +3227,15 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
/// Typically called by an override of [Widget.createElement].
Element(Widget widget)
: assert(widget != null),
_widget = widget;
_widget = widget {
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectCreated(
library: _flutterWidgetsLibrary,
className: '$Element',
object: this,
);
}
}
Element? _parent;
DebugReassembleConfig? _debugReassembleConfig;
......@@ -4132,6 +4152,9 @@ abstract class Element extends DiagnosticableTree implements BuildContext {
assert(_widget != null); // Use the private property to avoid a CastError during hot reload.
assert(depth != null);
assert(owner != null);
if (kFlutterMemoryAllocationsEnabled) {
MemoryAllocations.instance.dispatchObjectDisposed(object: this);
}
// Use the private property to avoid a CastError during hot reload.
final Key? key = _widget?.key;
if (key is GlobalKey) {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final MemoryAllocations ma = MemoryAllocations.instance;
setUp(() {
assert(!ma.hasListeners);
});
test('Publishers dispatch events in debug mode', () async {
int eventCount = 0;
void listener(ObjectEvent event) => eventCount++;
ma.addListener(listener);
final int expectedEventCount = await _activateFlutterObjectsAndReturnCountOfEvents();
expect(eventCount, expectedEventCount);
ma.removeListener(listener);
expect(ma.hasListeners, isFalse);
});
}
class _TestRenderObject extends RenderObject {
@override
void debugAssertDoesMeetConstraints() {}
@override
Rect get paintBounds => throw UnimplementedError();
@override
void performLayout() {}
@override
void performResize() {}
@override
Rect get semanticBounds => throw UnimplementedError();
}
class _TestLayer extends Layer{
@override
void addToScene(ui.SceneBuilder builder) {}
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<int> _activateFlutterObjectsAndReturnCountOfEvents() async {
int count = 0;
final RenderObject renderObject = _TestRenderObject(); count++;
final Layer layer = _TestLayer(); count++;
renderObject.dispose(); count++;
layer.dispose(); count++;
return count;
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final MemoryAllocations ma = MemoryAllocations.instance;
setUp(() {
assert(!ma.hasListeners);
});
test('Publishers dispatch events in debug mode', () async {
int eventCount = 0;
void listener(ObjectEvent event) => eventCount++;
ma.addListener(listener);
final int expectedEventCount = await _activateFlutterObjectsAndReturnCountOfEvents();
expect(eventCount, expectedEventCount);
ma.removeListener(listener);
expect(ma.hasListeners, isFalse);
});
testWidgets('State dispatches events in debug mode', (WidgetTester tester) async {
bool stateCreated = false;
bool stateDisposed = false;
void listener(ObjectEvent event) {
if (event is ObjectCreated && event.object is State) {
stateCreated = true;
}
if (event is ObjectDisposed && event.object is State) {
stateDisposed = true;
}
}
ma.addListener(listener);
await tester.pumpWidget(const _TestStatefulWidget());
expect(stateCreated, isTrue);
expect(stateDisposed, isFalse);
await tester.pumpWidget(const SizedBox.shrink());
expect(stateCreated, isTrue);
expect(stateDisposed, isTrue);
ma.removeListener(listener);
expect(ma.hasListeners, isFalse);
});
}
class _TestLeafRenderObjectWidget extends LeafRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
return _TestRenderObject();
}
}
class _TestElement extends RootRenderObjectElement{
_TestElement(): super(_TestLeafRenderObjectWidget());
void makeInactive() {
assignOwner(BuildOwner(focusManager: FocusManager()));
mount(null, null);
deactivate();
}
}
class _TestRenderObject extends RenderObject {
@override
void debugAssertDoesMeetConstraints() {}
@override
Rect get paintBounds => throw UnimplementedError();
@override
void performLayout() {}
@override
void performResize() {}
@override
Rect get semanticBounds => throw UnimplementedError();
}
class _TestStatefulWidget extends StatefulWidget {
const _TestStatefulWidget();
@override
State<_TestStatefulWidget> createState() => _TestStatefulWidgetState();
}
class _TestStatefulWidgetState extends State<_TestStatefulWidget> {
@override
Widget build(BuildContext context) {
return Container();
}
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<int> _activateFlutterObjectsAndReturnCountOfEvents() async {
int count = 0;
final _TestElement element = _TestElement(); count++;
final RenderObject renderObject = _TestRenderObject(); count++;
element.makeInactive(); element.unmount(); count += 3;
renderObject.dispose(); count++;
return count;
}
......@@ -19,16 +19,17 @@ void main() {
expect(kFlutterMemoryAllocationsEnabled, isFalse);
});
test(
testWidgets(
'$MemoryAllocations is noop when kFlutterMemoryAllocationsEnabled is false.',
() async {
(WidgetTester tester) async {
ObjectEvent? recievedEvent;
ObjectEvent listener(ObjectEvent event) => recievedEvent = event;
ma.addListener(listener);
_checkSdkHandlersNotSet();
expect(ma.hasListeners, isFalse);
await _activateFlutterObjects();
await _activateFlutterObjects(tester);
_checkSdkHandlersNotSet();
expect(recievedEvent, isNull);
expect(ma.hasListeners, isFalse);
......@@ -47,16 +48,21 @@ void _checkSdkHandlersNotSet() {
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<void> _activateFlutterObjects() async {
Future<void> _activateFlutterObjects(WidgetTester tester) async {
final ValueNotifier<bool> valueNotifier = ValueNotifier<bool>(true);
final ChangeNotifier changeNotifier = ChangeNotifier()..addListener(() {});
final Image image = await _createImage();
final Picture picture = _createPicture();
valueNotifier.dispose();
changeNotifier.dispose();
image.dispose();
picture.dispose();
// TODO(polina-c): Remove the condition after
// https://github.com/flutter/flutter/issues/110599 is fixed.
if (!kIsWeb) {
final Image image = await _createImage();
image.dispose();
}
}
Future<Image> _createImage() async {
......
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final MemoryAllocations ma = MemoryAllocations.instance;
setUp(() {
assert(!ma.hasListeners);
});
testWidgets(
'$MemoryAllocations is noop when kFlutterMemoryAllocationsEnabled is false.',
(WidgetTester tester) async {
ObjectEvent? recievedEvent;
ObjectEvent listener(ObjectEvent event) => recievedEvent = event;
ma.addListener(listener);
expect(ma.hasListeners, isFalse);
await _activateFlutterObjects(tester);
expect(recievedEvent, isNull);
expect(ma.hasListeners, isFalse);
ma.removeListener(listener);
},
);
}
class _TestRenderObject extends RenderObject {
@override
void debugAssertDoesMeetConstraints() {}
@override
Rect get paintBounds => throw UnimplementedError();
@override
void performLayout() {}
@override
void performResize() {}
@override
Rect get semanticBounds => throw UnimplementedError();
}
class _TestLayer extends Layer{
@override
void addToScene(SceneBuilder builder) {}
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<void> _activateFlutterObjects(WidgetTester tester) async {
final RenderObject renderObject = _TestRenderObject();
final Layer layer = _TestLayer();
renderObject.dispose();
// It is ok to use protected members for testing.
// ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member
layer.dispose();
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
final MemoryAllocations ma = MemoryAllocations.instance;
setUp(() {
assert(!ma.hasListeners);
});
testWidgets(
'$MemoryAllocations is noop when kFlutterMemoryAllocationsEnabled is false.',
(WidgetTester tester) async {
ObjectEvent? recievedEvent;
ObjectEvent listener(ObjectEvent event) => recievedEvent = event;
ma.addListener(listener);
expect(ma.hasListeners, isFalse);
await _activateFlutterObjects(tester);
expect(recievedEvent, isNull);
expect(ma.hasListeners, isFalse);
ma.removeListener(listener);
},
);
}
class _TestLeafRenderObjectWidget extends LeafRenderObjectWidget {
@override
RenderObject createRenderObject(BuildContext context) {
return _TestRenderObject();
}
}
class _TestRenderObject extends RenderObject {
@override
void debugAssertDoesMeetConstraints() {}
@override
Rect get paintBounds => throw UnimplementedError();
@override
void performLayout() {}
@override
void performResize() {}
@override
Rect get semanticBounds => throw UnimplementedError();
}
class _TestElement extends RootRenderObjectElement{
_TestElement(): super(_TestLeafRenderObjectWidget());
void makeInactive() {
assignOwner(BuildOwner(focusManager: FocusManager()));
mount(null, null);
deactivate();
}
}
class _MyStatefulWidget extends StatefulWidget {
const _MyStatefulWidget();
@override
State<_MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<_MyStatefulWidget> {
@override
Widget build(BuildContext context) {
return Container();
}
}
/// Create and dispose Flutter objects to fire memory allocation events.
Future<void> _activateFlutterObjects(WidgetTester tester) async {
final _TestElement element = _TestElement();
element.makeInactive(); element.unmount();
// Create and dispose State:
await tester.pumpWidget(const _MyStatefulWidget());
await tester.pumpWidget(const SizedBox.shrink());
}
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