Skip to content

Commit

Permalink
Add native stacktrace field for PlatformException (flutter#62828)
Browse files Browse the repository at this point in the history
* Add native stacktrace field for PlatformException

* Mute the readValue check for stacktrace.

* polish

* Add unit test and further polish

* Added more comments

* remove unnecessary import

* fill in stacktrace to JSONMethodCodec and fix tests

Co-authored-by: Ben Li <libe@google.com>
  • Loading branch information
lidongze91 and Ben Li committed Aug 10, 2020
1 parent a49ba95 commit 74b5051
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 10 deletions.
20 changes: 16 additions & 4 deletions packages/flutter/lib/src/services/message_codec.dart
Expand Up @@ -80,9 +80,10 @@ abstract class MethodCodec {

/// 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 });
/// The specified error [code], human-readable error [message], error
/// [details] correspond to the fields of [PlatformException] and error
/// [stacktrace] correspond to stacktrace from native platforms.
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details, String? stacktrace});
}


Expand All @@ -107,6 +108,7 @@ class PlatformException implements Exception {
required this.code,
this.message,
this.details,
this.stacktrace,
}) : assert(code != null);

/// An error code.
Expand All @@ -118,8 +120,18 @@ class PlatformException implements Exception {
/// Error details, possibly null.
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:
/// try {
/// ...
/// } catch (e, stacktrace) {
/// print(stacktrace);
/// }
final String? stacktrace;

@override
String toString() => 'PlatformException($code, $message, $details)';
String toString() => 'PlatformException($code, $message, $details, $stacktrace)';
}

/// Thrown to indicate that a platform interaction failed to find a handling
Expand Down
18 changes: 12 additions & 6 deletions packages/flutter/lib/src/services/message_codecs.dart
Expand Up @@ -144,13 +144,15 @@ class JSONMethodCodec implements MethodCodec {
throw FormatException('Expected envelope List, got $decoded');
if (decoded.length == 1)
return decoded[0];
if (decoded.length == 3
if (decoded.length == 4
&& decoded[0] is String
&& (decoded[1] == null || decoded[1] is String))
&& (decoded[1] == null || decoded[1] is String)
&& (decoded[3] == null || decoded[3] is String))
throw PlatformException(
code: decoded[0] as String,
message: decoded[1] as String,
details: decoded[2],
stacktrace: decoded[3] as String,
);
throw FormatException('Invalid envelope: $decoded');
}
Expand All @@ -161,9 +163,9 @@ class JSONMethodCodec implements MethodCodec {
}

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

Expand Down Expand Up @@ -547,12 +549,13 @@ class StandardMethodCodec implements MethodCodec {
}

@override
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details }) {
ByteData encodeErrorEnvelope({ required String code, String? message, dynamic details, String? stacktrace}) {
final WriteBuffer buffer = WriteBuffer();
buffer.putUint8(1);
messageCodec.writeValue(buffer, code);
messageCodec.writeValue(buffer, message);
messageCodec.writeValue(buffer, details);
messageCodec.writeValue(buffer, stacktrace);
return buffer.done();
}

Expand All @@ -567,8 +570,11 @@ class StandardMethodCodec implements MethodCodec {
final dynamic errorCode = messageCodec.readValue(buffer);
final dynamic errorMessage = messageCodec.readValue(buffer);
final dynamic errorDetails = messageCodec.readValue(buffer);
/// TODO(libe, b/158148913): Mute the readValue check for stacktrace for now.
/// Remove this once flutter engine is ready with the stacktrace fields.
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);
throw PlatformException(code: errorCode, message: errorMessage as String, details: errorDetails, stacktrace: errorStacktrace);
else
throw const FormatException('Invalid envelope');
}
Expand Down
12 changes: 12 additions & 0 deletions packages/flutter/test/services/message_codecs_test.dart
Expand Up @@ -9,7 +9,9 @@

import 'dart:typed_data';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:matcher/matcher.dart';
import '../flutter_test_alternative.dart';
import 'message_codecs_testing.dart';

Expand Down Expand Up @@ -44,6 +46,15 @@ void main() {
expect(string.decodeMessage(offsetByteData), ' world');
});
});
group('Decode envelope', () {
const MethodCodec method = StandardMethodCodec();
const MessageCodec<String> string = StringCodec();
const StandardMessageCodec standard = StandardMessageCodec();
test('should decode native stacktrace.', () {
final ByteData errorData = method.encodeErrorEnvelope(code: 'errorCode', message: 'errorMessage', details: 'errorDetails', stacktrace: 'errorStacktrace',);
expect(() => method.decodeEnvelope(errorData), throwsA(predicate((e) => e is PlatformException && e.stacktrace == 'errorStacktrace')));
});
});
group('JSON message codec', () {
const MessageCodec<dynamic> json = JSONMessageCodec();
test('should encode and decode simple messages', () {
Expand Down Expand Up @@ -158,3 +169,4 @@ void main() {
});
});
}

2 changes: 2 additions & 0 deletions packages/flutter/test/services/platform_channel_test.dart
Expand Up @@ -131,6 +131,7 @@ void main() {
'bad',
'Something happened',
<String, dynamic>{'a': 42, 'b': 3.14},
'errorStacktrace',
]);
},
);
Expand All @@ -141,6 +142,7 @@ void main() {
expect(e.code, equals('bad'));
expect(e.message, equals('Something happened'));
expect(e.details, equals(<String, dynamic>{'a': 42, 'b': 3.14}));
expect(e.stacktrace, equals('errorStacktrace'));
} catch (e) {
fail('PlatformException expected');
}
Expand Down

0 comments on commit 74b5051

Please sign in to comment.