Skip to content

Commit

Permalink
[pigeon] Moves all codec logic to singular custom codec (#6600)
Browse files Browse the repository at this point in the history
[pigeon] Moves all codec logic to singular custom codec.

Also fixes a few small codec related bugs that have cropped up over time.

fixes flutter/flutter#147454
fixes flutter/flutter#147127
fixes flutter/flutter#147587
fixes flutter/flutter#148065
  • Loading branch information
tarrinneal committed Jun 6, 2024
1 parent 027c7d0 commit 2e928d7
Show file tree
Hide file tree
Showing 90 changed files with 4,712 additions and 4,841 deletions.
9 changes: 9 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 20.0.0

* Moves all codec logic to single custom codec per file.
* **Breaking Change** Limits the number of total custom types to 126.
* If more than 126 custom types are needed, consider breaking up your definition files.
* Fixes bug that prevented collection subtypes from being added properly.
* [swift] Adds `@unchecked Sendable` to codec method.
* [objc] [cpp] Fixes bug that prevented setting custom header import path.

## 19.0.2

* [kotlin] Adds the `@JvmOverloads` to the `HostApi` setUp method. This prevents the calling Java code from having to provide an empty `String` as Kotlin provides it by default
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(4);
toListResult.add(name);
toListResult.add(description);
toListResult.add(code == null ? null : code.index);
toListResult.add(code);
toListResult.add(data);
return toListResult;
}
Expand All @@ -193,13 +193,45 @@ ArrayList<Object> toList() {
Object description = __pigeon_list.get(1);
pigeonResult.setDescription((String) description);
Object code = __pigeon_list.get(2);
pigeonResult.setCode(Code.values()[(int) code]);
pigeonResult.setCode((Code) code);
Object data = __pigeon_list.get(3);
pigeonResult.setData((Map<String, String>) data);
return pigeonResult;
}
}

private static class PigeonCodec extends StandardMessageCodec {
public static final PigeonCodec INSTANCE = new PigeonCodec();

private PigeonCodec() {}

@Override
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
switch (type) {
case (byte) 129:
return MessageData.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 130:
Object value = readValue(buffer);
return value == null ? null : Code.values()[(int) value];
default:
return super.readValueOfType(type, buffer);
}
}

@Override
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
if (value instanceof MessageData) {
stream.write(129);
writeValue(stream, ((MessageData) value).toList());
} else if (value instanceof Code) {
stream.write(130);
writeValue(stream, value == null ? null : ((Code) value).index);
} else {
super.writeValue(stream, value);
}
}
}

/** Asynchronous error handling return type for non-nullable API method returns. */
public interface Result<T> {
/** Success case callback method for handling returns. */
Expand All @@ -224,33 +256,6 @@ public interface VoidResult {
/** Failure case callback method for handling errors. */
void error(@NonNull Throwable error);
}

private static class ExampleHostApiCodec extends StandardMessageCodec {
public static final ExampleHostApiCodec INSTANCE = new ExampleHostApiCodec();

private ExampleHostApiCodec() {}

@Override
protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
switch (type) {
case (byte) 128:
return MessageData.fromList((ArrayList<Object>) readValue(buffer));
default:
return super.readValueOfType(type, buffer);
}
}

@Override
protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
if (value instanceof MessageData) {
stream.write(128);
writeValue(stream, ((MessageData) value).toList());
} else {
super.writeValue(stream, value);
}
}
}

/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
public interface ExampleHostApi {

Expand All @@ -264,7 +269,7 @@ public interface ExampleHostApi {

/** The codec used by ExampleHostApi. */
static @NonNull MessageCodec<Object> getCodec() {
return ExampleHostApiCodec.INSTANCE;
return PigeonCodec.INSTANCE;
}
/** Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`. */
static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable ExampleHostApi api) {
Expand Down Expand Up @@ -382,7 +387,7 @@ public MessageFlutterApi(
/** Public interface for sending reply. */
/** The codec used by MessageFlutterApi. */
static @NonNull MessageCodec<Object> getCodec() {
return new StandardMessageCodec();
return PigeonCodec.INSTANCE;
}

public void flutterMethod(@Nullable String aStringArg, @NonNull Result<String> result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,38 +69,45 @@ data class MessageData(
fun fromList(__pigeon_list: List<Any?>): MessageData {
val name = __pigeon_list[0] as String?
val description = __pigeon_list[1] as String?
val code = Code.ofRaw(__pigeon_list[2] as Int)!!
val code = __pigeon_list[2] as Code
val data = __pigeon_list[3] as Map<String?, String?>
return MessageData(name, description, code, data)
}
}

fun toList(): List<Any?> {
return listOf<Any?>(
return listOf(
name,
description,
code.raw,
code,
data,
)
}
}

private object ExampleHostApiCodec : StandardMessageCodec() {
private object MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
128.toByte() -> {
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { MessageData.fromList(it) }
}
130.toByte() -> {
return (readValue(buffer) as Int?)?.let { Code.ofRaw(it) }
}
else -> super.readValueOfType(type, buffer)
}
}

override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is MessageData -> {
stream.write(128)
stream.write(129)
writeValue(stream, value.toList())
}
is Code -> {
stream.write(130)
writeValue(stream, value.raw)
}
else -> super.writeValue(stream, value)
}
}
Expand All @@ -116,7 +123,7 @@ interface ExampleHostApi {

companion object {
/** The codec used by ExampleHostApi. */
val codec: MessageCodec<Any?> by lazy { ExampleHostApiCodec }
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec }
/** Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(
Expand All @@ -136,7 +143,7 @@ interface ExampleHostApi {
channel.setMessageHandler { _, reply ->
val wrapped: List<Any?> =
try {
listOf<Any?>(api.getHostLanguage())
listOf(api.getHostLanguage())
} catch (exception: Throwable) {
wrapError(exception)
}
Expand All @@ -159,7 +166,7 @@ interface ExampleHostApi {
val bArg = args[1].let { num -> if (num is Int) num.toLong() else num as Long }
val wrapped: List<Any?> =
try {
listOf<Any?>(api.add(aArg, bArg))
listOf(api.add(aArg, bArg))
} catch (exception: Throwable) {
wrapError(exception)
}
Expand Down Expand Up @@ -203,7 +210,7 @@ class MessageFlutterApi(
) {
companion object {
/** The codec used by MessageFlutterApi. */
val codec: MessageCodec<Any?> by lazy { StandardMessageCodec() }
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec }
}

fun flutterMethod(aStringArg: String?, callback: (Result<String>) -> Unit) {
Expand Down
42 changes: 27 additions & 15 deletions packages/pigeon/example/app/ios/Runner/Messages.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct MessageData {
static func fromList(_ __pigeon_list: [Any?]) -> MessageData? {
let name: String? = nilOrValue(__pigeon_list[0])
let description: String? = nilOrValue(__pigeon_list[1])
let code = Code(rawValue: __pigeon_list[2] as! Int)!
let code = __pigeon_list[2] as! Code
let data = __pigeon_list[3] as! [String?: String?]

return MessageData(
Expand All @@ -103,46 +103,55 @@ struct MessageData {
return [
name,
description,
code.rawValue,
code,
data,
]
}
}

private class ExampleHostApiCodecReader: FlutterStandardReader {
private class MessagesPigeonCodecReader: FlutterStandardReader {
override func readValue(ofType type: UInt8) -> Any? {
switch type {
case 128:
case 129:
return MessageData.fromList(self.readValue() as! [Any?])
case 130:
var enumResult: Code? = nil
let enumResultAsInt: Int? = nilOrValue(self.readValue() as? Int)
if let enumResultAsInt = enumResultAsInt {
enumResult = Code(rawValue: enumResultAsInt)
}
return enumResult
default:
return super.readValue(ofType: type)
}
}
}

private class ExampleHostApiCodecWriter: FlutterStandardWriter {
private class MessagesPigeonCodecWriter: FlutterStandardWriter {
override func writeValue(_ value: Any) {
if let value = value as? MessageData {
super.writeByte(128)
super.writeByte(129)
super.writeValue(value.toList())
} else if let value = value as? Code {
super.writeByte(130)
super.writeValue(value.rawValue)
} else {
super.writeValue(value)
}
}
}

private class ExampleHostApiCodecReaderWriter: FlutterStandardReaderWriter {
private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
override func reader(with data: Data) -> FlutterStandardReader {
return ExampleHostApiCodecReader(data: data)
return MessagesPigeonCodecReader(data: data)
}

override func writer(with data: NSMutableData) -> FlutterStandardWriter {
return ExampleHostApiCodecWriter(data: data)
return MessagesPigeonCodecWriter(data: data)
}
}

class ExampleHostApiCodec: FlutterStandardMessageCodec {
static let shared = ExampleHostApiCodec(readerWriter: ExampleHostApiCodecReaderWriter())
class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
}

/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
Expand All @@ -154,8 +163,7 @@ protocol ExampleHostApi {

/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class ExampleHostApiSetup {
/// The codec used by ExampleHostApi.
static var codec: FlutterStandardMessageCodec { ExampleHostApiCodec.shared }
static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
/// Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`.
static func setUp(
binaryMessenger: FlutterBinaryMessenger, api: ExampleHostApi?, messageChannelSuffix: String = ""
Expand Down Expand Up @@ -228,12 +236,16 @@ class MessageFlutterApi: MessageFlutterApiProtocol {
self.binaryMessenger = binaryMessenger
self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
}
var codec: MessagesPigeonCodec {
return MessagesPigeonCodec.shared
}
func flutterMethod(
aString aStringArg: String?, completion: @escaping (Result<String, PigeonError>) -> Void
) {
let channelName: String =
"dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger)
let channel = FlutterBasicMessageChannel(
name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([aStringArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
Expand Down
24 changes: 14 additions & 10 deletions packages/pigeon/example/app/lib/src/messages.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MessageData {
return <Object?>[
name,
description,
code.index,
code,
data,
];
}
Expand All @@ -64,19 +64,22 @@ class MessageData {
return MessageData(
name: result[0] as String?,
description: result[1] as String?,
code: Code.values[result[2]! as int],
code: result[2]! as Code,
data: (result[3] as Map<Object?, Object?>?)!.cast<String?, String?>(),
);
}
}

class _ExampleHostApiCodec extends StandardMessageCodec {
const _ExampleHostApiCodec();
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is MessageData) {
buffer.putUint8(128);
buffer.putUint8(129);
writeValue(buffer, value.encode());
} else if (value is Code) {
buffer.putUint8(130);
writeValue(buffer, value.index);
} else {
super.writeValue(buffer, value);
}
Expand All @@ -85,8 +88,11 @@ class _ExampleHostApiCodec extends StandardMessageCodec {
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
case 129:
return MessageData.decode(readValue(buffer)!);
case 130:
final int? value = readValue(buffer) as int?;
return value == null ? null : Code.values[value];
default:
return super.readValueOfType(type, buffer);
}
Expand All @@ -104,8 +110,7 @@ class ExampleHostApi {
messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? __pigeon_binaryMessenger;

static const MessageCodec<Object?> pigeonChannelCodec =
_ExampleHostApiCodec();
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();

final String __pigeon_messageChannelSuffix;

Expand Down Expand Up @@ -198,8 +203,7 @@ class ExampleHostApi {
}

abstract class MessageFlutterApi {
static const MessageCodec<Object?> pigeonChannelCodec =
StandardMessageCodec();
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();

String flutterMethod(String? aString);

Expand Down
Loading

0 comments on commit 2e928d7

Please sign in to comment.