Skip to content

Commit

Permalink
Remove some `dynamic's. (#76239)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hixie committed Feb 18, 2021
1 parent c66e694 commit 00630d0
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 67 deletions.
29 changes: 24 additions & 5 deletions packages/flutter/lib/src/services/message_codec.dart
Expand Up @@ -29,7 +29,7 @@ abstract class MessageCodec<T> {
/// Decodes the specified [message] from binary.
///
/// Returns null if the message is null.
T decodeMessage(ByteData? message);
T? decodeMessage(ByteData? message);
}

/// An command object representing the invocation of a named method.
Expand All @@ -46,6 +46,10 @@ class MethodCall {
/// The arguments for the method.
///
/// Must be a valid value for the [MethodCodec] used.
///
/// This property is `dynamic`, which means type-checking is skipped when accessing
/// this property. To minimize the risk of type errors at runtime, the value should
/// be cast to `Object?` when accessed.
final dynamic arguments;

@override
Expand Down Expand Up @@ -73,16 +77,22 @@ abstract class MethodCodec {
///
/// Throws [PlatformException], if [envelope] represents an error, otherwise
/// returns the enveloped result.
///
/// The type returned from [decodeEnvelope] is `dynamic` (not `Object?`),
/// which means *no type checking is performed on its return value*. It is
/// strongly recommended that the return value be immediately cast to a known
/// type to prevent runtime errors due to typos that the type checker could
/// otherwise catch.
dynamic decodeEnvelope(ByteData envelope);

/// Encodes a successful [result] into a binary envelope.
ByteData encodeSuccessEnvelope(dynamic result);
ByteData encodeSuccessEnvelope(Object? result);

/// Encodes an error result into a binary envelope.
///
/// The specified error [code], human-readable error [message] and error
/// [details] correspond to the fields of [PlatformException].
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details});
ByteData encodeErrorEnvelope({ required String code, String? message, Object? details});
}


Expand Down Expand Up @@ -117,16 +127,25 @@ class PlatformException implements Exception {
final String? message;

/// Error details, possibly null.
///
/// This property is `dynamic`, which means type-checking is skipped when accessing
/// this property. To minimize the risk of type errors at runtime, the value should
/// be cast to `Object?` when accessed.
final dynamic details;

/// Native stacktrace for the error, possibly null.
/// This is strictly for native platform stacktrace.
/// The stacktrace info on dart platform can be found within the try-catch block for example:
///
/// This contains the native platform stack trace, not the Dart stack trace.
///
/// The stack trace for Dart exceptions can be obtained using try-catch blocks, for example:
///
/// ```dart
/// try {
/// ...
/// } catch (e, stacktrace) {
/// print(stacktrace);
/// }
/// ```
final String? stacktrace;

@override
Expand Down
98 changes: 57 additions & 41 deletions packages/flutter/lib/src/services/message_codecs.dart
Expand Up @@ -17,7 +17,7 @@ import 'message_codec.dart';
/// When sending outgoing messages from Android, be sure to use direct `ByteBuffer`
/// as opposed to indirect. The `wrap()` API provides indirect buffers by default
/// and you will get empty `ByteData` objects in Dart.
class BinaryCodec implements MessageCodec<ByteData?> {
class BinaryCodec implements MessageCodec<ByteData> {
/// Creates a [MessageCodec] with unencoded binary messages represented using
/// [ByteData].
const BinaryCodec();
Expand All @@ -33,7 +33,7 @@ class BinaryCodec implements MessageCodec<ByteData?> {
///
/// On Android, messages will be represented using `java.util.String`.
/// On iOS, messages will be represented using `NSString`.
class StringCodec implements MessageCodec<String?> {
class StringCodec implements MessageCodec<String> {
/// Creates a [MessageCodec] with UTF-8 encoded String messages.
const StringCodec();

Expand Down Expand Up @@ -71,7 +71,13 @@ class StringCodec implements MessageCodec<String?> {
/// null/nil for null, and identical to what would result from decoding a
/// singleton JSON array with a Boolean, number, or string value, and then
/// extracting its single element.
class JSONMessageCodec implements MessageCodec<dynamic> {
///
/// The type returned from [decodeMessage] is `dynamic` (not `Object?`), which
/// means *no type checking is performed on its return value*. It is strongly
/// recommended that the return value be immediately cast to a known type to
/// prevent runtime errors due to typos that the type checker could otherwise
/// catch.
class JSONMessageCodec implements MessageCodec<Object?> {
// The codec serializes messages as defined by the JSON codec of the
// dart:convert package. The format used must match the Android and
// iOS counterparts.
Expand All @@ -80,7 +86,7 @@ class JSONMessageCodec implements MessageCodec<dynamic> {
const JSONMessageCodec();

@override
ByteData? encodeMessage(dynamic message) {
ByteData? encodeMessage(Object? message) {
if (message == null)
return null;
return const StringCodec().encodeMessage(json.encode(message));
Expand Down Expand Up @@ -118,27 +124,27 @@ class JSONMethodCodec implements MethodCodec {

@override
ByteData encodeMethodCall(MethodCall call) {
return const JSONMessageCodec().encodeMessage(<String, dynamic>{
return const JSONMessageCodec().encodeMessage(<String, Object?>{
'method': call.method,
'args': call.arguments,
})!;
}

@override
MethodCall decodeMethodCall(ByteData? methodCall) {
final dynamic decoded = const JSONMessageCodec().decodeMessage(methodCall);
final Object? decoded = const JSONMessageCodec().decodeMessage(methodCall);
if (decoded is! Map)
throw FormatException('Expected method call Map, got $decoded');
final dynamic method = decoded['method'];
final dynamic arguments = decoded['args'];
final Object? method = decoded['method'];
final Object? arguments = decoded['args'];
if (method is String)
return MethodCall(method, arguments);
throw FormatException('Invalid method call: $decoded');
}

@override
dynamic decodeEnvelope(ByteData envelope) {
final dynamic decoded = const JSONMessageCodec().decodeMessage(envelope);
final Object? decoded = const JSONMessageCodec().decodeMessage(envelope);
if (decoded is! List)
throw FormatException('Expected envelope List, got $decoded');
if (decoded.length == 1)
Expand All @@ -165,14 +171,14 @@ class JSONMethodCodec implements MethodCodec {
}

@override
ByteData encodeSuccessEnvelope(dynamic result) {
return const JSONMessageCodec().encodeMessage(<dynamic>[result])!;
ByteData encodeSuccessEnvelope(Object? result) {
return const JSONMessageCodec().encodeMessage(<Object?>[result])!;
}

@override
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details}) {
ByteData encodeErrorEnvelope({ required String code, String? message, Object? details}) {
assert(code != null);
return const JSONMessageCodec().encodeMessage(<dynamic>[code, message, details])!;
return const JSONMessageCodec().encodeMessage(<Object?>[code, message, details])!;
}
}

Expand All @@ -188,9 +194,20 @@ class JSONMethodCodec implements MethodCodec {
/// * [List]s of supported values
/// * [Map]s from supported values to supported values
///
/// Decoded values will use `List<dynamic>` and `Map<dynamic, dynamic>`
/// Decoded values will use `List<Object?>` and `Map<Object?, Object?>`
/// irrespective of content.
///
/// The type returned from [decodeMessage] is `dynamic` (not `Object?`), which
/// means *no type checking is performed on its return value*. It is strongly
/// recommended that the return value be immediately cast to a known type to
/// prevent runtime errors due to typos that the type checker could otherwise
/// catch.
///
/// The codec is extensible by subclasses overriding [writeValue] and
/// [readValueOfType].
///
/// ## Android specifics
///
/// On Android, messages are represented as follows:
///
/// * null: null
Expand All @@ -206,6 +223,14 @@ class JSONMethodCodec implements MethodCodec {
/// * [List]\: `java.util.ArrayList`
/// * [Map]\: `java.util.HashMap`
///
/// When sending a `java.math.BigInteger` from Java, it is converted into a
/// [String] with the hexadecimal representation of the integer. (The value is
/// tagged as being a big integer; subclasses of this class could be made to
/// support it natively; see the discussion at [writeValue].) This codec does
/// not support sending big integers from Dart.
///
/// ## iOS specifics
///
/// On iOS, messages are represented as follows:
///
/// * null: nil
Expand All @@ -218,16 +243,7 @@ class JSONMethodCodec implements MethodCodec {
/// `FlutterStandardTypedData`
/// * [List]\: `NSArray`
/// * [Map]\: `NSDictionary`
///
/// When sending a `java.math.BigInteger` from Java, it is converted into a
/// [String] with the hexadecimal representation of the integer. (The value is
/// tagged as being a big integer; subclasses of this class could be made to
/// support it natively; see the discussion at [writeValue].) This codec does
/// not support sending big integers from Dart.
///
/// The codec is extensible by subclasses overriding [writeValue] and
/// [readValueOfType].
class StandardMessageCodec implements MessageCodec<dynamic> {
class StandardMessageCodec implements MessageCodec<Object?> {
/// Creates a [MessageCodec] using the Flutter standard binary encoding.
const StandardMessageCodec();

Expand Down Expand Up @@ -289,7 +305,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
static const int _valueMap = 13;

@override
ByteData? encodeMessage(dynamic message) {
ByteData? encodeMessage(Object? message) {
if (message == null)
return null;
final WriteBuffer buffer = WriteBuffer();
Expand All @@ -302,7 +318,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
if (message == null)
return null;
final ReadBuffer buffer = ReadBuffer(message);
final dynamic result = readValue(buffer);
final Object? result = readValue(buffer);
if (buffer.hasRemaining)
throw const FormatException('Message corrupted');
return result;
Expand Down Expand Up @@ -344,7 +360,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
/// string's length as encoded by [writeSize] followed by the string bytes. On
/// Android, that would get converted to a `java.math.BigInteger` object. On
/// iOS, the string representation is returned.
void writeValue(WriteBuffer buffer, dynamic value) {
void writeValue(WriteBuffer buffer, Object? value) {
if (value == null) {
buffer.putUint8(_valueNull);
} else if (value is bool) {
Expand Down Expand Up @@ -389,13 +405,13 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
} else if (value is List) {
buffer.putUint8(_valueList);
writeSize(buffer, value.length);
for (final dynamic item in value) {
for (final Object? item in value) {
writeValue(buffer, item);
}
} else if (value is Map) {
buffer.putUint8(_valueMap);
writeSize(buffer, value.length);
value.forEach((dynamic key, dynamic value) {
value.forEach((Object? key, Object? value) {
writeValue(buffer, key);
writeValue(buffer, value);
});
Expand All @@ -408,7 +424,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
///
/// This method is intended for use by subclasses overriding
/// [readValueOfType].
dynamic readValue(ReadBuffer buffer) {
Object? readValue(ReadBuffer buffer) {
if (!buffer.hasRemaining)
throw const FormatException('Message corrupted');
final int type = buffer.getUint8();
Expand All @@ -420,7 +436,7 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
/// The codec can be extended by overriding this method, calling super for
/// types that the extension does not handle. See the discussion at
/// [writeValue].
dynamic readValueOfType(int type, ReadBuffer buffer) {
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case _valueNull:
return null;
Expand Down Expand Up @@ -452,13 +468,13 @@ class StandardMessageCodec implements MessageCodec<dynamic> {
return buffer.getFloat64List(length);
case _valueList:
final int length = readSize(buffer);
final List<dynamic> result = List<dynamic>.filled(length, null, growable: false);
final List<Object?> result = List<Object?>.filled(length, null, growable: false);
for (int i = 0; i < length; i++)
result[i] = readValue(buffer);
return result;
case _valueMap:
final int length = readSize(buffer);
final Map<dynamic, dynamic> result = <dynamic, dynamic>{};
final Map<Object?, Object?> result = <Object?, Object?>{};
for (int i = 0; i < length; i++)
result[readValue(buffer)] = readValue(buffer);
return result;
Expand Down Expand Up @@ -539,24 +555,24 @@ class StandardMethodCodec implements MethodCodec {
@override
MethodCall decodeMethodCall(ByteData? methodCall) {
final ReadBuffer buffer = ReadBuffer(methodCall!);
final dynamic method = messageCodec.readValue(buffer);
final dynamic arguments = messageCodec.readValue(buffer);
final Object? method = messageCodec.readValue(buffer);
final Object? arguments = messageCodec.readValue(buffer);
if (method is String && !buffer.hasRemaining)
return MethodCall(method, arguments);
else
throw const FormatException('Invalid method call');
}

@override
ByteData encodeSuccessEnvelope(dynamic result) {
ByteData encodeSuccessEnvelope(Object? result) {
final WriteBuffer buffer = WriteBuffer();
buffer.putUint8(0);
messageCodec.writeValue(buffer, result);
return buffer.done();
}

@override
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details}) {
ByteData encodeErrorEnvelope({ required String code, String? message, Object? details}) {
final WriteBuffer buffer = WriteBuffer();
buffer.putUint8(1);
messageCodec.writeValue(buffer, code);
Expand All @@ -573,10 +589,10 @@ class StandardMethodCodec implements MethodCodec {
final ReadBuffer buffer = ReadBuffer(envelope);
if (buffer.getUint8() == 0)
return messageCodec.readValue(buffer);
final dynamic errorCode = messageCodec.readValue(buffer);
final dynamic errorMessage = messageCodec.readValue(buffer);
final dynamic errorDetails = messageCodec.readValue(buffer);
final String? errorStacktrace = (buffer.hasRemaining) ? messageCodec.readValue(buffer) as String : null;
final Object? errorCode = messageCodec.readValue(buffer);
final Object? errorMessage = messageCodec.readValue(buffer);
final Object? errorDetails = messageCodec.readValue(buffer);
final String? errorStacktrace = (buffer.hasRemaining) ? messageCodec.readValue(buffer) as String? : null;
if (errorCode is String && (errorMessage == null || errorMessage is String) && !buffer.hasRemaining)
throw PlatformException(code: errorCode, message: errorMessage as String?, details: errorDetails, stacktrace: errorStacktrace);
else
Expand Down
6 changes: 3 additions & 3 deletions packages/flutter/lib/src/services/platform_channel.dart
Expand Up @@ -52,7 +52,7 @@ class BasicMessageChannel<T> {
///
/// Returns a [Future] which completes to the received response, which may
/// be null.
Future<T> send(T message) async {
Future<T?> send(T message) async {
return codec.decodeMessage(await binaryMessenger.send(name, codec.encodeMessage(message)));
}

Expand All @@ -65,7 +65,7 @@ class BasicMessageChannel<T> {
///
/// The handler's return value is sent back to the platform plugins as a
/// message reply. It may be null.
void setMessageHandler(Future<T> Function(T message)? handler) {
void setMessageHandler(Future<T> Function(T? message)? handler) {
if (handler == null) {
binaryMessenger.setMessageHandler(name, null);
} else {
Expand All @@ -86,7 +86,7 @@ class BasicMessageChannel<T> {
///
/// This is intended for testing. Messages intercepted in this manner are not
/// sent to platform plugins.
void setMockMessageHandler(Future<T> Function(T message)? handler) {
void setMockMessageHandler(Future<T> Function(T? message)? handler) {
if (handler == null) {
binaryMessenger.setMockMessageHandler(name, null);
} else {
Expand Down

0 comments on commit 00630d0

Please sign in to comment.