Unverified Commit fce2f93b authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Add a BindingBase.debugBindingType() method to enable asserts that want to...

Add a BindingBase.debugBindingType() method to enable asserts that want to verify that the binding isn't initialized (#98226)
parent eac1a548
......@@ -140,9 +140,9 @@ abstract class BindingBase {
return true;
}());
assert(!_debugInitialized);
assert(_debugInitializedType == null);
initInstances();
assert(_debugInitialized);
assert(_debugInitializedType != null);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
......@@ -154,7 +154,7 @@ abstract class BindingBase {
}
bool _debugConstructed = false;
static bool _debugInitialized = false;
static Type? _debugInitializedType;
static bool _debugServiceExtensionsRegistered = false;
/// Additional configuration used by the framework during hot reload.
......@@ -256,9 +256,9 @@ abstract class BindingBase {
@protected
@mustCallSuper
void initInstances() {
assert(!_debugInitialized);
assert(_debugInitializedType == null);
assert(() {
_debugInitialized = true;
_debugInitializedType = runtimeType;
return true;
}());
}
......@@ -277,7 +277,7 @@ abstract class BindingBase {
@protected
static T checkInstance<T extends BindingBase>(T? instance) {
assert(() {
if (!_debugInitialized && instance == null) {
if (_debugInitializedType == null && instance == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
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.'),
......@@ -298,7 +298,7 @@ abstract class BindingBase {
]);
}
if (instance == null) {
assert(_debugInitialized);
assert(_debugInitializedType == null);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding mixin instance is null but bindings are already initialized.'),
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 '
'documentation of the BaseBinding class for more details.',
),
ErrorHint(
'The binding that was initialized was of the type "$_debugInitializedType". '
),
]);
}
try {
assert(instance != null);
if (instance._debugConstructed && !_debugInitialized) {
if (instance._debugConstructed && _debugInitializedType == null) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Binding initialized without calling initInstances.'),
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) {
// 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>[
ErrorSummary('Binding did not complete initialization.'),
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!;
}
/// 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
/// extensions.
///
......
// 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);
});
}
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