Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
[plugin_platform_interface] Switch PlatformInterface._instanceToken
Browse files Browse the repository at this point in the history
… to an expando. (#6411)

This change replaces `PlatformInterface._instanceToken` (an instance
field that points from a `PlatformInterface` to its corresponding
token) with an expando that maps from `PlatformInterface` to `Object`.
There is no change to the public API, and no change to the behavior of
users' production code.

This change ensures that if a customer tries to implement
`PlatformInterface` using `implements` rather than `extends`, the code
in `PlatformInterface._verify` won't try to access the private
`_instanceToken` field in `PlatformInterface`.  This is important
because an upcoming change to the dart language is going to cause such
accesses to throw exceptions rather than deferring to `noSuchMethod`
(see dart-lang/language#2020 for details).

Fixes flutter/flutter#109339.
  • Loading branch information
stereotype441 authored Sep 15, 2022
1 parent dc3bfc6 commit feed66b
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 6 deletions.
6 changes: 5 additions & 1 deletion packages/plugin_platform_interface/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
## NEXT
## 2.1.3

* Minor fixes for new analysis options.
* Adds additional tests for `PlatformInterface` and `MockPlatformInterfaceMixin`.
* Modifies `PlatformInterface` to use an expando for detecting if a customer
tries to implement PlatformInterface using `implements` rather than `extends`.
This ensures that `verify` will continue to work as advertized after
https://github.com/dart-lang/language/issues/2020 is implemented.

## 2.1.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,20 @@ abstract class PlatformInterface {
/// derived classes.
///
/// @param token The same, non-`const` `Object` that will be passed to `verify`.
PlatformInterface({required Object token}) : _instanceToken = token;
PlatformInterface({required Object token}) {
_instanceTokens[this] = token;
}

final Object? _instanceToken;
/// Expando mapping instances of PlatformInterface to their associated tokens.
/// The reason this is not simply a private field of type `Object?` is because
/// as of the implementation of field promotion in Dart
/// (https://github.com/dart-lang/language/issues/2020), it is a runtime error
/// to invoke a private member that is mocked in another library. The expando
/// approach prevents [_verify] from triggering this runtime exception when
/// encountering an implementation that uses `implements` rather than
/// `extends`. This in turn allows [_verify] to throw an [AssertionError] (as
/// documented).
static final Expando<Object> _instanceTokens = Expando<Object>();

/// Ensures that the platform instance was constructed with a non-`const` token
/// that matches the provided token and throws [AssertionError] if not.
Expand Down Expand Up @@ -89,10 +100,10 @@ abstract class PlatformInterface {
return;
}
if (preventConstObject &&
identical(instance._instanceToken, const Object())) {
identical(_instanceTokens[instance], const Object())) {
throw AssertionError('`const Object()` cannot be used as the token.');
}
if (!identical(token, instance._instanceToken)) {
if (!identical(token, _instanceTokens[instance])) {
throw AssertionError(
'Platform interfaces must not be implemented with `implements`');
}
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin_platform_interface/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+
# be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version
# that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on:
# `plugin_platform_interface: >=2.X.Y <4.0.0`).
version: 2.1.2
version: 2.1.3

environment:
sdk: ">=2.12.0 <3.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ class SamplePluginPlatform extends PlatformInterface {
class ImplementsSamplePluginPlatform extends Mock
implements SamplePluginPlatform {}

class ImplementsSamplePluginPlatformUsingNoSuchMethod
implements SamplePluginPlatform {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}

class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock
with MockPlatformInterfaceMixin
implements SamplePluginPlatform {}
Expand Down Expand Up @@ -98,6 +104,13 @@ void main() {
}, throwsA(isA<AssertionError>()));
});

test('prevents implmentation with `implements` and `noSuchMethod`', () {
expect(() {
SamplePluginPlatform.instance =
ImplementsSamplePluginPlatformUsingNoSuchMethod();
}, throwsA(isA<AssertionError>()));
});

test('allows mocking with `implements`', () {
final SamplePluginPlatform mock =
ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
Expand Down

0 comments on commit feed66b

Please sign in to comment.