mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add a BindingBase.debugBindingType() method to enable asserts that want to verify that the binding isn't initialized (#98226)
This commit is contained in:
parent
eac1a548ed
commit
fce2f93b75
@ -140,9 +140,9 @@ abstract class BindingBase {
|
|||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
|
|
||||||
assert(!_debugInitialized);
|
assert(_debugInitializedType == null);
|
||||||
initInstances();
|
initInstances();
|
||||||
assert(_debugInitialized);
|
assert(_debugInitializedType != null);
|
||||||
|
|
||||||
assert(!_debugServiceExtensionsRegistered);
|
assert(!_debugServiceExtensionsRegistered);
|
||||||
initServiceExtensions();
|
initServiceExtensions();
|
||||||
@ -154,7 +154,7 @@ abstract class BindingBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _debugConstructed = false;
|
bool _debugConstructed = false;
|
||||||
static bool _debugInitialized = false;
|
static Type? _debugInitializedType;
|
||||||
static bool _debugServiceExtensionsRegistered = false;
|
static bool _debugServiceExtensionsRegistered = false;
|
||||||
|
|
||||||
/// Additional configuration used by the framework during hot reload.
|
/// Additional configuration used by the framework during hot reload.
|
||||||
@ -256,9 +256,9 @@ abstract class BindingBase {
|
|||||||
@protected
|
@protected
|
||||||
@mustCallSuper
|
@mustCallSuper
|
||||||
void initInstances() {
|
void initInstances() {
|
||||||
assert(!_debugInitialized);
|
assert(_debugInitializedType == null);
|
||||||
assert(() {
|
assert(() {
|
||||||
_debugInitialized = true;
|
_debugInitializedType = runtimeType;
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
@ -277,7 +277,7 @@ abstract class BindingBase {
|
|||||||
@protected
|
@protected
|
||||||
static T checkInstance<T extends BindingBase>(T? instance) {
|
static T checkInstance<T extends BindingBase>(T? instance) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (!_debugInitialized && instance == null) {
|
if (_debugInitializedType == null && instance == null) {
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
ErrorSummary('Binding has not yet been initialized.'),
|
ErrorSummary('Binding has not yet been initialized.'),
|
||||||
ErrorDescription('The "instance" getter on the $T binding mixin is only available once that binding has been initialized.'),
|
ErrorDescription('The "instance" getter on the $T binding mixin is only available once that binding has been initialized.'),
|
||||||
@ -298,7 +298,7 @@ abstract class BindingBase {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
assert(_debugInitialized);
|
assert(_debugInitializedType == null);
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
ErrorSummary('Binding mixin instance is null but bindings are already initialized.'),
|
ErrorSummary('Binding mixin instance is null but bindings are already initialized.'),
|
||||||
ErrorDescription(
|
ErrorDescription(
|
||||||
@ -315,11 +315,14 @@ abstract class BindingBase {
|
|||||||
'It is also possible that $T does not implement "initInstances()" to assign a value to "instance". See the '
|
'It is also possible that $T does not implement "initInstances()" to assign a value to "instance". See the '
|
||||||
'documentation of the BaseBinding class for more details.',
|
'documentation of the BaseBinding class for more details.',
|
||||||
),
|
),
|
||||||
|
ErrorHint(
|
||||||
|
'The binding that was initialized was of the type "$_debugInitializedType". '
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
assert(instance != null);
|
assert(instance != null);
|
||||||
if (instance._debugConstructed && !_debugInitialized) {
|
if (instance._debugConstructed && _debugInitializedType == null) {
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
ErrorSummary('Binding initialized without calling initInstances.'),
|
ErrorSummary('Binding initialized without calling initInstances.'),
|
||||||
ErrorDescription('An instance of $T is non-null, but BindingBase.initInstances() has not yet been called.'),
|
ErrorDescription('An instance of $T is non-null, but BindingBase.initInstances() has not yet been called.'),
|
||||||
@ -335,7 +338,7 @@ abstract class BindingBase {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
if (!instance._debugConstructed) {
|
if (!instance._debugConstructed) {
|
||||||
// The state of _debugInitialized doesn't matter in this failure mode.
|
// The state of _debugInitializedType doesn't matter in this failure mode.
|
||||||
throw FlutterError.fromParts(<DiagnosticsNode>[
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
||||||
ErrorSummary('Binding did not complete initialization.'),
|
ErrorSummary('Binding did not complete initialization.'),
|
||||||
ErrorDescription('An instance of $T is non-null, but the BindingBase() constructor has not yet been called.'),
|
ErrorDescription('An instance of $T is non-null, but the BindingBase() constructor has not yet been called.'),
|
||||||
@ -361,6 +364,36 @@ abstract class BindingBase {
|
|||||||
return instance!;
|
return instance!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// In debug builds, the type of the current binding, if any, or else null.
|
||||||
|
///
|
||||||
|
/// This may be useful in asserts to verify that the binding has not been initialized
|
||||||
|
/// before the point in the application code that wants to initialize the binding, or
|
||||||
|
/// to verify that the binding is the one that is expected.
|
||||||
|
///
|
||||||
|
/// For example, if an application uses [Zone]s to report uncaught execptions, it may
|
||||||
|
/// need to ensure that `ensureInitialized()` has not yet been invoked on any binding
|
||||||
|
/// at the point where it configures the zone and initializes the binding.
|
||||||
|
///
|
||||||
|
/// If this returns null, the binding has not been initialized.
|
||||||
|
///
|
||||||
|
/// If this returns a non-null value, it returns the type of the binding instance.
|
||||||
|
///
|
||||||
|
/// To obtain the binding itself, consider the `instance` getter on the [BindingBase]
|
||||||
|
/// subclass or mixin.
|
||||||
|
///
|
||||||
|
/// This method only returns a useful value in debug builds. In release builds, the
|
||||||
|
/// return value is always null; to improve startup performance, the type of the
|
||||||
|
/// binding is not tracked in release builds.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [BindingBase], whose class documentation describes the conventions for dealing
|
||||||
|
/// with bindings.
|
||||||
|
/// * [initInstances], whose documentation details how to create a binding mixin.
|
||||||
|
static Type? debugBindingType() {
|
||||||
|
return _debugInitializedType;
|
||||||
|
}
|
||||||
|
|
||||||
/// Called when the binding is initialized, to register service
|
/// Called when the binding is initialized, to register service
|
||||||
/// extensions.
|
/// extensions.
|
||||||
///
|
///
|
||||||
|
37
packages/flutter/test/foundation/binding_test.dart
Normal file
37
packages/flutter/test/foundation/binding_test.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// 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_test/flutter_test.dart';
|
||||||
|
|
||||||
|
// based on the sample code in foundation/binding.dart
|
||||||
|
|
||||||
|
mixin FooBinding on BindingBase {
|
||||||
|
@override
|
||||||
|
void initInstances() {
|
||||||
|
super.initInstances();
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FooBinding get instance => BindingBase.checkInstance(_instance);
|
||||||
|
static FooBinding? _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
class FooLibraryBinding extends BindingBase with FooBinding {
|
||||||
|
static FooBinding ensureInitialized() {
|
||||||
|
if (FooBinding._instance == null) {
|
||||||
|
FooLibraryBinding();
|
||||||
|
}
|
||||||
|
return FooBinding.instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('BindingBase.debugBindingType', () async {
|
||||||
|
expect(BindingBase.debugBindingType(), isNull);
|
||||||
|
FooLibraryBinding.ensureInitialized();
|
||||||
|
expect(BindingBase.debugBindingType(), FooLibraryBinding);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user