[pigeon] moved to a custom codec (#399)

diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index b365f8b..5bca85d 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -3,6 +3,7 @@
 * [front-end] Added a more explicit error if generic fields are used.
 * [front-end] Added a more explicit error for static fields.
 * [front-end] Added more errors for incorrect usage of Pigeon (previously they were just ignored).
+* Moved Pigeon to using a custom codec which allows collection types to contain custom classes.
 
 ## 0.3.0
 
diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart
index de6ca68..8e6d59b 100644
--- a/packages/pigeon/lib/dart_generator.dart
+++ b/packages/pigeon/lib/dart_generator.dart
@@ -49,8 +49,54 @@
       .replaceAll(r"'", r"\'");
 }
 
+String _getCodecName(Api api) => '_${api.name}Codec';
+
+void _writeCodec(Indent indent, String codecName, Api api) {
+  indent.write('class $codecName extends StandardMessageCodec ');
+  indent.scoped('{', '}', () {
+    indent.writeln('const $codecName();');
+    if (getCodecClasses(api).isNotEmpty) {
+      indent.writeln('@override');
+      indent.write('void writeValue(WriteBuffer buffer, Object? value) ');
+      indent.scoped('{', '}', () {
+        for (final EnumeratedClass customClass in getCodecClasses(api)) {
+          indent.write('if (value is ${customClass.name}) ');
+          indent.scoped('{', '} else ', () {
+            indent.writeln('buffer.putUint8(${customClass.enumeration});');
+            indent.writeln('writeValue(buffer, value.encode());');
+          });
+        }
+        indent.scoped('{', '}', () {
+          indent.writeln('super.writeValue(buffer, value);');
+        });
+      });
+      indent.writeln('@override');
+      indent.write('Object? readValueOfType(int type, ReadBuffer buffer) ');
+      indent.scoped('{', '}', () {
+        indent.write('switch (type) ');
+        indent.scoped('{', '}', () {
+          for (final EnumeratedClass customClass in getCodecClasses(api)) {
+            indent.write('case ${customClass.enumeration}: ');
+            indent.writeScoped('', '', () {
+              indent.writeln(
+                  'return ${customClass.name}.decode(readValue(buffer)!);');
+            });
+          }
+          indent.write('default:');
+          indent.writeScoped('', '', () {
+            indent.writeln('return super.readValueOfType(type, buffer);');
+          });
+        });
+      });
+    }
+  });
+}
+
 void _writeHostApi(DartOptions opt, Indent indent, Api api) {
   assert(api.location == ApiLocation.host);
+  final String codecName = _getCodecName(api);
+  _writeCodec(indent, codecName, api);
+  indent.addln('');
   final String nullTag = opt.isNullSafe ? '?' : '';
   final String unwrapOperator = opt.isNullSafe ? '!' : '';
   bool first = true;
@@ -65,6 +111,8 @@
 final BinaryMessenger$nullTag _binaryMessenger;
 ''');
 
+    indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
+    indent.addln('');
     for (final Method func in api.methods) {
       if (!first) {
         indent.writeln('');
@@ -91,7 +139,7 @@
             'final BasicMessageChannel<Object$nullTag> channel = BasicMessageChannel<Object$nullTag>(');
         indent.nest(2, () {
           indent.writeln(
-            '\'$channelName\', const StandardMessageCodec(), binaryMessenger: _binaryMessenger);',
+            '\'$channelName\', codec, binaryMessenger: _binaryMessenger);',
           );
         });
         final String returnStatement = func.returnType == 'void'
@@ -128,10 +176,14 @@
   bool isMockHandler = false,
 }) {
   assert(api.location == ApiLocation.flutter);
+  final String codecName = _getCodecName(api);
+  _writeCodec(indent, codecName, api);
   final String nullTag = opt.isNullSafe ? '?' : '';
   final String unwrapOperator = opt.isNullSafe ? '!' : '';
   indent.write('abstract class ${api.name} ');
   indent.scoped('{', '}', () {
+    indent.writeln('static const MessageCodec<Object?> codec = $codecName();');
+    indent.addln('');
     for (final Method func in api.methods) {
       final bool isAsync = func.isAsynchronous;
       final String returnType =
@@ -153,7 +205,7 @@
               : channelNameFunc(func);
           indent.nest(2, () {
             indent.writeln(
-              '\'$channelName\', StandardMessageCodec());',
+              '\'$channelName\', codec);',
             );
           });
           final String messageHandlerSetter =
@@ -243,14 +295,16 @@
   indent.writeln('// $generatedCodeWarning');
   indent.writeln('// $seeAlsoWarning');
   indent.writeln(
-    '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types',
+    '// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name',
   );
   indent.writeln('// @dart = ${opt.isNullSafe ? '2.12' : '2.8'}');
   indent.writeln('import \'dart:async\';');
   indent.writeln(
     'import \'dart:typed_data\' show Uint8List, Int32List, Int64List, Float64List;',
   );
-  indent.writeln('');
+  indent.addln('');
+  indent.writeln(
+      'import \'package:flutter/foundation.dart\' show WriteBuffer, ReadBuffer;');
   indent.writeln('import \'package:flutter/services.dart\';');
   for (final Enum anEnum in root.enums) {
     indent.writeln('');
@@ -356,6 +410,8 @@
   indent.writeln(
     'import \'dart:typed_data\' show Uint8List, Int32List, Int64List, Float64List;',
   );
+  indent.writeln(
+      'import \'package:flutter/foundation.dart\' show WriteBuffer, ReadBuffer;');
   indent.writeln('import \'package:flutter/services.dart\';');
   indent.writeln('import \'package:flutter_test/flutter_test.dart\';');
   indent.writeln('');
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 1fe3386..e5f07d9 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -249,3 +249,42 @@
   }
   return result;
 }
+
+/// A class name that is enumerated.
+class EnumeratedClass {
+  /// Constructor.
+  EnumeratedClass(this.name, this.enumeration);
+
+  /// The name of the class.
+  final String name;
+
+  /// The enumeration of the class.
+  final int enumeration;
+}
+
+/// Custom codecs' custom types are enumerated from 255 down to this number to
+/// avoid collisions with the StandardMessageCodec.
+const int _minimumCodecFieldKey = 128;
+
+/// Given an [Api], return the enumerated classes that must exist in the codec
+/// where the enumeration should be the key used in the buffer.
+Iterable<EnumeratedClass> getCodecClasses(Api api) sync* {
+  final Set<String> names = <String>{};
+  for (final Method method in api.methods) {
+    names.add(method.returnType);
+    names.add(method.argType);
+  }
+  final List<String> sortedNames =
+      names.where((String element) => element != 'void').toList();
+  sortedNames.sort();
+  int enumeration = _minimumCodecFieldKey;
+  const int maxCustomClassesPerApi = 255 - _minimumCodecFieldKey;
+  if (sortedNames.length > maxCustomClassesPerApi) {
+    throw Exception(
+        'Pigeon doesn\'t support more than $maxCustomClassesPerApi referenced custom classes per API, try splitting up your APIs.');
+  }
+  for (final String name in sortedNames) {
+    yield EnumeratedClass(name, enumeration);
+    enumeration += 1;
+  }
+}
diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart
index 625e5f0..d53c829 100644
--- a/packages/pigeon/lib/java_generator.dart
+++ b/packages/pigeon/lib/java_generator.dart
@@ -64,6 +64,55 @@
   }
 }
 
+String _getCodecName(Api api) => '${api.name}Codec';
+
+void _writeCodec(Indent indent, Api api) {
+  final String codecName = _getCodecName(api);
+  indent.write('private static class $codecName extends StandardMessageCodec ');
+  indent.scoped('{', '}', () {
+    indent
+        .writeln('public static final $codecName INSTANCE = new $codecName();');
+    indent.writeln('private $codecName() {}');
+    if (getCodecClasses(api).isNotEmpty) {
+      indent.writeln('@Override');
+      indent.write(
+          'protected Object readValueOfType(byte type, ByteBuffer buffer) ');
+      indent.scoped('{', '}', () {
+        indent.write('switch (type) ');
+        indent.scoped('{', '}', () {
+          for (final EnumeratedClass customClass in getCodecClasses(api)) {
+            indent.write('case (byte)${customClass.enumeration}: ');
+            indent.writeScoped('', '', () {
+              indent.writeln(
+                  'return ${customClass.name}.fromMap((Map<String, Object>) readValue(buffer));');
+            });
+          }
+          indent.write('default:');
+          indent.writeScoped('', '', () {
+            indent.writeln('return super.readValueOfType(type, buffer);');
+          });
+        });
+      });
+      indent.writeln('@Override');
+      indent.write(
+          'protected void writeValue(ByteArrayOutputStream stream, Object value) ');
+      indent.writeScoped('{', '}', () {
+        for (final EnumeratedClass customClass in getCodecClasses(api)) {
+          indent.write('if (value instanceof ${customClass.name}) ');
+          indent.scoped('{', '} else ', () {
+            indent.writeln('stream.write(${customClass.enumeration});');
+            indent.writeln(
+                'writeValue(stream, ((${customClass.name}) value).toMap());');
+          });
+        }
+        indent.scoped('{', '}', () {
+          indent.writeln('super.writeValue(stream, value);');
+        });
+      });
+    }
+  });
+}
+
 void _writeHostApi(Indent indent, Api api) {
   assert(api.location == ApiLocation.host);
 
@@ -86,6 +135,13 @@
       indent.writeln('$returnType ${method.name}(${argSignature.join(', ')});');
     }
     indent.addln('');
+    final String codecName = _getCodecName(api);
+    indent.format('''
+/** The codec used by ${api.name}. */
+static MessageCodec<Object> getCodec() {
+\treturn $codecName.INSTANCE;
+}
+''');
     indent.writeln(
         '/** Sets up an instance of `${api.name}` to handle messages through the `binaryMessenger`. */');
     indent.write(
@@ -99,7 +155,7 @@
           indent.inc();
           indent.inc();
           indent.writeln(
-              'new BasicMessageChannel<>(binaryMessenger, "$channelName", new StandardMessageCodec());');
+              'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
           indent.dec();
           indent.dec();
           indent.write('if (api != null) ');
@@ -178,6 +234,12 @@
     indent.scoped('{', '}', () {
       indent.writeln('void reply(T reply);');
     });
+    final String codecName = _getCodecName(api);
+    indent.format('''
+static MessageCodec<Object> getCodec() {
+\treturn $codecName.INSTANCE;
+}
+''');
     for (final Method func in api.methods) {
       final String channelName = makeChannelName(api, func);
       final String returnType =
@@ -196,7 +258,7 @@
         indent.inc();
         indent.inc();
         indent.writeln(
-            'new BasicMessageChannel<>(binaryMessenger, "$channelName", new StandardMessageCodec());');
+            'new BasicMessageChannel<>(binaryMessenger, "$channelName", getCodec());');
         indent.dec();
         indent.dec();
         if (func.argType != 'void') {
@@ -269,7 +331,10 @@
   indent.addln('');
   indent.writeln('import io.flutter.plugin.common.BasicMessageChannel;');
   indent.writeln('import io.flutter.plugin.common.BinaryMessenger;');
+  indent.writeln('import io.flutter.plugin.common.MessageCodec;');
   indent.writeln('import io.flutter.plugin.common.StandardMessageCodec;');
+  indent.writeln('import java.io.ByteArrayOutputStream;');
+  indent.writeln('import java.nio.ByteBuffer;');
   indent.writeln('import java.util.List;');
   indent.writeln('import java.util.Map;');
   indent.writeln('import java.util.HashMap;');
@@ -368,6 +433,7 @@
     }
 
     for (final Api api in root.apis) {
+      _writeCodec(indent, api);
       indent.addln('');
       if (api.location == ApiLocation.host) {
         _writeHostApi(indent, api);
diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart
index 283b672..c46708f 100644
--- a/packages/pigeon/lib/objc_generator.dart
+++ b/packages/pigeon/lib/objc_generator.dart
@@ -134,6 +134,85 @@
   }
 }
 
+String _getCodecName(String? prefix, String className) =>
+    '${_className(prefix, className)}Codec';
+
+String _getCodecGetterName(String? prefix, String className) =>
+    '${_className(prefix, className)}GetCodec';
+
+void _writeCodec(Indent indent, String name, ObjcOptions options, Api api) {
+  final String readerWriterName = '${name}ReaderWriter';
+  final String readerName = '${name}Reader';
+  final String writerName = '${name}Writer';
+  indent.writeln('@interface $readerName : FlutterStandardReader');
+  indent.writeln('@end');
+  indent.writeln('@implementation $readerName');
+  if (getCodecClasses(api).isNotEmpty) {
+    indent.writeln('- (nullable id)readValueOfType:(UInt8)type ');
+    indent.scoped('{', '}', () {
+      indent.write('switch (type) ');
+      indent.scoped('{', '}', () {
+        for (final EnumeratedClass customClass in getCodecClasses(api)) {
+          indent.write('case ${customClass.enumeration}: ');
+          indent.writeScoped('', '', () {
+            indent.writeln(
+                'return [${_className(options.prefix, customClass.name)} fromMap:[self readValue]];');
+          });
+        }
+        indent.write('default:');
+        indent.writeScoped('', '', () {
+          indent.writeln('return [super readValueOfType:type];');
+        });
+      });
+    });
+  }
+  indent.writeln('@end');
+  indent.addln('');
+  indent.writeln('@interface $writerName : FlutterStandardWriter');
+  indent.writeln('@end');
+  indent.writeln('@implementation $writerName');
+  if (getCodecClasses(api).isNotEmpty) {
+    indent.writeln('- (void)writeValue:(id)value ');
+    indent.scoped('{', '}', () {
+      for (final EnumeratedClass customClass in getCodecClasses(api)) {
+        indent.write(
+            'if ([value isKindOfClass:[${_className(options.prefix, customClass.name)} class]]) ');
+        indent.scoped('{', '} else ', () {
+          indent.writeln('[self writeByte:${customClass.enumeration}];');
+          indent.writeln('[self writeValue:[value toMap]];');
+        });
+      }
+      indent.scoped('{', '}', () {
+        indent.writeln('[super writeValue:value];');
+      });
+    });
+  }
+  indent.writeln('@end');
+  indent.addln('');
+  indent.format('''
+@interface $readerWriterName : FlutterStandardReaderWriter
+@end
+@implementation $readerWriterName
+- (FlutterStandardWriter*)writerWithData:(NSMutableData*)data {
+\treturn [[$writerName alloc] initWithData:data];
+}
+- (FlutterStandardReader*)readerWithData:(NSData*)data {
+\treturn [[$readerName alloc] initWithData:data];
+}
+@end
+
+NSObject<FlutterMessageCodec>* ${_getCodecGetterName(options.prefix, api.name)}() {
+\tstatic dispatch_once_t s_pred = 0;
+\tstatic FlutterStandardMessageCodec* s_sharedObject = nil;
+\tdispatch_once(&s_pred, ^{
+\t\t$readerWriterName* readerWriter = [[$readerWriterName alloc] init];
+\t\ts_sharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];
+\t});
+\treturn s_sharedObject;
+}
+''');
+}
+
 void _writeHostApiDeclaration(Indent indent, Api api, ObjcOptions options) {
   final String apiName = _className(options.prefix, api.name);
   indent.writeln('@protocol $apiName');
@@ -209,6 +288,7 @@
   indent.writeln('// $seeAlsoWarning');
   indent.writeln('#import <Foundation/Foundation.h>');
   indent.writeln('@protocol FlutterBinaryMessenger;');
+  indent.writeln('@protocol FlutterMessageCodec;');
   indent.writeln('@class FlutterError;');
   indent.writeln('@class FlutterStandardTypedData;');
   indent.writeln('');
@@ -240,6 +320,11 @@
   _writeClassDeclarations(indent, root.classes, root.enums, options.prefix);
 
   for (final Api api in root.apis) {
+    indent.writeln(
+        '/// The codec used by ${_className(options.prefix, api.name)}.');
+    indent.writeln(
+        'NSObject<FlutterMessageCodec>* ${_getCodecGetterName(options.prefix, api.name)}(void);');
+    indent.addln('');
     if (api.location == ApiLocation.host) {
       _writeHostApiDeclaration(indent, api, options);
     } else if (api.location == ApiLocation.flutter) {
@@ -289,7 +374,9 @@
         indent.inc();
         indent
             .writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
-        indent.writeln('binaryMessenger:binaryMessenger];');
+        indent.writeln('binaryMessenger:binaryMessenger');
+        indent.writeln(
+            'codec:${_getCodecGetterName(options.prefix, api.name)}()];');
         indent.dec();
         indent.dec();
 
@@ -402,7 +489,9 @@
       indent.writeln('[FlutterBasicMessageChannel');
       indent.inc();
       indent.writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
-      indent.writeln('binaryMessenger:self.binaryMessenger];');
+      indent.writeln('binaryMessenger:self.binaryMessenger');
+      indent.writeln(
+          'codec:${_getCodecGetterName(options.prefix, api.name)}()];');
       indent.dec();
       indent.dec();
       if (func.argType != 'void') {
@@ -510,6 +599,9 @@
   }
 
   for (final Api api in root.apis) {
+    final String codecName = _getCodecName(options.prefix, api.name);
+    _writeCodec(indent, codecName, options, api);
+    indent.addln('');
     if (api.location == ApiLocation.host) {
       _writeHostApiSource(indent, options, api);
     } else if (api.location == ApiLocation.flutter) {
diff --git a/packages/pigeon/pigeons/all_void.dart b/packages/pigeon/pigeons/all_void.dart
new file mode 100644
index 0000000..ce80a55
--- /dev/null
+++ b/packages/pigeon/pigeons/all_void.dart
@@ -0,0 +1,15 @@
+// Copyright 2013 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:pigeon/pigeon.dart';
+
+@FlutterApi()
+abstract class AllVoidFlutterApi {
+  void doit();
+}
+
+@HostApi()
+abstract class AllVoidHostApi {
+  void doit();
+}
diff --git a/packages/pigeon/pigeons/flutter_unittests.dart b/packages/pigeon/pigeons/flutter_unittests.dart
index bf3576b..cb99095 100644
--- a/packages/pigeon/pigeons/flutter_unittests.dart
+++ b/packages/pigeon/pigeons/flutter_unittests.dart
@@ -27,4 +27,5 @@
 abstract class Api {
   SearchReply search(SearchRequest request);
   SearchReplies doSearches(SearchRequests request);
+  SearchRequests echo(SearchRequests requests);
 }
diff --git a/packages/pigeon/pigeons/list.dart b/packages/pigeon/pigeons/list.dart
index 9af7025..11359e7 100644
--- a/packages/pigeon/pigeons/list.dart
+++ b/packages/pigeon/pigeons/list.dart
@@ -13,3 +13,8 @@
 abstract class TestApi {
   void test(TestMessage msg);
 }
+
+@FlutterApi()
+abstract class EchoApi {
+  TestMessage echo(TestMessage msg);
+}
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/ListTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/ListTest.java
new file mode 100644
index 0000000..45093df
--- /dev/null
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/ListTest.java
@@ -0,0 +1,48 @@
+// Copyright 2013 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.
+
+package com.example.android_unit_tests;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.example.android_unit_tests.PigeonList.EchoApi;
+import com.example.android_unit_tests.PigeonList.TestMessage;
+import io.flutter.plugin.common.BinaryMessenger;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import org.junit.Test;
+
+public class ListTest {
+  @Test
+  public void listInList() {
+    TestMessage top = new TestMessage();
+    TestMessage inside = new TestMessage();
+    inside.setTestList(Arrays.asList(1, 2, 3));
+    top.setTestList(Arrays.asList(inside));
+    BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
+    doAnswer(
+            invocation -> {
+              ByteBuffer message = invocation.getArgument(1);
+              BinaryMessenger.BinaryReply reply = invocation.getArgument(2);
+              message.position(0);
+              reply.reply(message);
+              return null;
+            })
+        .when(binaryMessenger)
+        .send(anyString(), any(), any());
+    EchoApi api = new EchoApi(binaryMessenger);
+    boolean[] didCall = {false};
+    api.echo(
+        top,
+        (result) -> {
+          didCall[0] = true;
+          assertEquals(result.getTestList().size(), 1);
+          assertTrue(result.getTestList().get(0) instanceof TestMessage);
+          TestMessage readInside = (TestMessage) result.getTestList().get(0);
+          assertEquals(readInside.getTestList().size(), 3);
+        });
+    assertTrue(didCall[0]);
+  }
+}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
index 7550dc2..5ad97e5 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
@@ -1,14 +1,15 @@
 // Copyright 2013 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.
-
-// Autogenerated from Pigeon (v0.2.1), do not edit directly.
+//
+// Autogenerated from Pigeon (v0.3.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
-// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
 import 'dart:async';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
 
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
 import 'package:flutter/services.dart';
 
 class Everything {
@@ -54,38 +55,26 @@
   }
 }
 
-abstract class FlutterEverything {
-  Everything giveMeEverything();
-  Everything echo(Everything arg);
-  static void setup(FlutterEverything? api) {
-    {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.FlutterEverything.giveMeEverything',
-          StandardMessageCodec());
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          // ignore message
-          final Everything output = api.giveMeEverything();
-          return output.encode();
-        });
-      }
+class _HostEverythingCodec extends StandardMessageCodec {
+  const _HostEverythingCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is Everything) {
+      buffer.putUint8(255);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
     }
-    {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.FlutterEverything.echo', StandardMessageCodec());
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.FlutterEverything.echo was null. Expected Everything.');
-          final Everything input = Everything.decode(message!);
-          final Everything output = api.echo(input);
-          return output.encode();
-        });
-      }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 255:
+        return Everything.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
     }
   }
 }
@@ -99,10 +88,11 @@
 
   final BinaryMessenger? _binaryMessenger;
 
+  static const MessageCodec<Object?> codec = _HostEverythingCodec();
+
   Future<Everything> giveMeEverything() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.HostEverything.giveMeEverything',
-        const StandardMessageCodec(),
+        'dev.flutter.pigeon.HostEverything.giveMeEverything', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(null) as Map<Object?, Object?>?;
@@ -128,7 +118,7 @@
   Future<Everything> echo(Everything arg) async {
     final Object encoded = arg.encode();
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.HostEverything.echo', const StandardMessageCodec(),
+        'dev.flutter.pigeon.HostEverything.echo', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(encoded) as Map<Object?, Object?>?;
@@ -151,3 +141,64 @@
     }
   }
 }
+
+class _FlutterEverythingCodec extends StandardMessageCodec {
+  const _FlutterEverythingCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is Everything) {
+      buffer.putUint8(255);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 255:
+        return Everything.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
+    }
+  }
+}
+
+abstract class FlutterEverything {
+  static const MessageCodec<Object?> codec = _FlutterEverythingCodec();
+
+  Everything giveMeEverything();
+  Everything echo(Everything arg);
+  static void setup(FlutterEverything? api) {
+    {
+      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.FlutterEverything.giveMeEverything', codec);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          // ignore message
+          final Everything output = api.giveMeEverything();
+          return output.encode();
+        });
+      }
+    }
+    {
+      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.FlutterEverything.echo', codec);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.FlutterEverything.echo was null. Expected Everything.');
+          final Everything input = Everything.decode(message!);
+          final Everything output = api.echo(input);
+          return output.encode();
+        });
+      }
+    }
+  }
+}
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
index 8dce342..5445ab2 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
@@ -1,16 +1,32 @@
 // Copyright 2013 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.
-
-// Autogenerated from Pigeon (v0.2.0), do not edit directly.
+//
+// Autogenerated from Pigeon (v0.3.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
-// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
 import 'dart:async';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
 
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
 import 'package:flutter/services.dart';
 
+class SearchRequest {
+  String? query;
+
+  Object encode() {
+    final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
+    pigeonMap['query'] = query;
+    return pigeonMap;
+  }
+
+  static SearchRequest decode(Object message) {
+    final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
+    return SearchRequest()..query = pigeonMap['query'] as String?;
+  }
+}
+
 class SearchReply {
   String? result;
   String? error;
@@ -30,18 +46,18 @@
   }
 }
 
-class SearchRequest {
-  String? query;
+class SearchRequests {
+  List<Object?>? requests;
 
   Object encode() {
     final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
-    pigeonMap['query'] = query;
+    pigeonMap['requests'] = requests;
     return pigeonMap;
   }
 
-  static SearchRequest decode(Object message) {
+  static SearchRequests decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest()..query = pigeonMap['query'] as String?;
+    return SearchRequests()..requests = pigeonMap['requests'] as List<Object?>?;
   }
 }
 
@@ -60,18 +76,45 @@
   }
 }
 
-class SearchRequests {
-  List<Object?>? requests;
-
-  Object encode() {
-    final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
-    pigeonMap['requests'] = requests;
-    return pigeonMap;
+class _ApiCodec extends StandardMessageCodec {
+  const _ApiCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is SearchReplies) {
+      buffer.putUint8(255);
+      writeValue(buffer, value.encode());
+    } else if (value is SearchReply) {
+      buffer.putUint8(254);
+      writeValue(buffer, value.encode());
+    } else if (value is SearchRequest) {
+      buffer.putUint8(253);
+      writeValue(buffer, value.encode());
+    } else if (value is SearchRequests) {
+      buffer.putUint8(252);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
   }
 
-  static SearchRequests decode(Object message) {
-    final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequests()..requests = pigeonMap['requests'] as List<Object?>?;
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 255:
+        return SearchReplies.decode(readValue(buffer)!);
+
+      case 254:
+        return SearchReply.decode(readValue(buffer)!);
+
+      case 253:
+        return SearchRequest.decode(readValue(buffer)!);
+
+      case 252:
+        return SearchRequests.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
+    }
   }
 }
 
@@ -83,10 +126,12 @@
 
   final BinaryMessenger? _binaryMessenger;
 
+  static const MessageCodec<Object?> codec = _ApiCodec();
+
   Future<SearchReply> search(SearchRequest arg) async {
     final Object encoded = arg.encode();
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.search', const StandardMessageCodec(),
+        'dev.flutter.pigeon.Api.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(encoded) as Map<Object?, Object?>?;
@@ -112,7 +157,7 @@
   Future<SearchReplies> doSearches(SearchRequests arg) async {
     final Object encoded = arg.encode();
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.doSearches', const StandardMessageCodec(),
+        'dev.flutter.pigeon.Api.doSearches', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(encoded) as Map<Object?, Object?>?;
@@ -134,4 +179,30 @@
       return SearchReplies.decode(replyMap['result']!);
     }
   }
+
+  Future<SearchRequests> echo(SearchRequests arg) async {
+    final Object encoded = arg.encode();
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.Api.echo', codec,
+        binaryMessenger: _binaryMessenger);
+    final Map<Object?, Object?>? replyMap =
+        await channel.send(encoded) as Map<Object?, Object?>?;
+    if (replyMap == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+        details: null,
+      );
+    } else if (replyMap['error'] != null) {
+      final Map<Object?, Object?> error =
+          (replyMap['error'] as Map<Object?, Object?>?)!;
+      throw PlatformException(
+        code: (error['code'] as String?)!,
+        message: error['message'] as String?,
+        details: error['details'],
+      );
+    } else {
+      return SearchRequests.decode(replyMap['result']!);
+    }
+  }
 }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
index acea867..b886c77 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/all_datatypes_test.dart
@@ -53,10 +53,10 @@
     final BinaryMessenger mockMessenger = MockBinaryMessenger();
     when(mockMessenger.send('dev.flutter.pigeon.HostEverything.echo', any))
         .thenAnswer((Invocation realInvocation) async {
-      const StandardMessageCodec codec = StandardMessageCodec();
-      final Object input =
+      final MessageCodec<Object?> codec = HostEverything.codec;
+      final Object? input =
           codec.decodeMessage(realInvocation.positionalArguments[1]);
-      return codec.encodeMessage(<String, Object>{'result': input});
+      return codec.encodeMessage(<String, Object>{'result': input!});
     });
     final HostEverything api = HostEverything(binaryMessenger: mockMessenger);
     final Everything result = await api.echo(everything);
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
index 43aab25..769625c 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
@@ -37,10 +37,9 @@
     final SearchRequest request = SearchRequest()..query = 'hey';
     final SearchReply reply = SearchReply()..result = 'ho';
     final BinaryMessenger mockMessenger = MockBinaryMessenger();
-    const MessageCodec<Object?> codec = StandardMessageCodec();
     final Completer<ByteData?> completer = Completer<ByteData?>();
     completer.complete(
-        codec.encodeMessage(<String, Object>{'result': reply.encode()}));
+        Api.codec.encodeMessage(<String, Object>{'result': reply.encode()}));
     final Future<ByteData?> sendResult = completer.future;
     when(mockMessenger.send('dev.flutter.pigeon.Api.search', any))
         .thenAnswer((Invocation realInvocation) => sendResult);
@@ -50,25 +49,21 @@
     expect(reply.result, readReply.result);
   });
 
-  // TODO(gaaclarke): This test is a companion for the fix to https://github.com/flutter/flutter/issues/80538
-  // test('send/receive list classes', () async {
-  //   final SearchRequest request = SearchRequest()
-  //       ..query = 'hey';
-  //   final SearchReply reply = SearchReply()
-  //       ..result = 'ho';
-  //   final SearchRequests requests = SearchRequests()
-  //       ..requests = <SearchRequest>[request];
-  //   final SearchReplies replies = SearchReplies()
-  //       ..replies = <SearchReply>[reply];
-  //   final BinaryMessenger mockMessenger = MockBinaryMessenger();
-  //   const MessageCodec<Object?> codec = StandardMessageCodec();
-  //   final Completer<ByteData?> completer = Completer<ByteData?>();
-  //   completer.complete(codec.encodeMessage(<String, Object>{'result' : replies.encode()}));
-  //   final Future<ByteData?> sendResult = completer.future;
-  //   when(mockMessenger.send('dev.flutter.pigeon.Api.search', any)).thenAnswer((Invocation realInvocation) => sendResult);
-  //   final Api api = Api(binaryMessenger: mockMessenger);
-  //   final SearchReplies readReplies = await api.doSearches(requests);
-  //   expect(readReplies, isNotNull);
-  //   expect(reply.result, (readReplies.replies![0] as SearchReply?)!.result);
-  // });
+  test('send/receive list classes', () async {
+    final SearchRequest request = SearchRequest()..query = 'hey';
+    final SearchRequests requests = SearchRequests()
+      ..requests = <SearchRequest>[request];
+    final BinaryMessenger mockMessenger = MockBinaryMessenger();
+    when(mockMessenger.send('dev.flutter.pigeon.Api.echo', any))
+        .thenAnswer((Invocation realInvocation) async {
+      final MessageCodec<Object?> codec = Api.codec;
+      final Object? input =
+          codec.decodeMessage(realInvocation.positionalArguments[1]);
+      return codec.encodeMessage(<String, Object>{'result': input!});
+    });
+    final Api api = Api(binaryMessenger: mockMessenger);
+    final SearchRequests echo = await api.echo(requests);
+    expect(echo.requests!.length, 1);
+    expect((echo.requests![0] as SearchRequest?)!.query, 'hey');
+  });
 }
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj
index 1c9dd58..1e35915 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner.xcodeproj/project.pbxproj
@@ -8,6 +8,8 @@
 
 /* Begin PBXBuildFile section */
 		0D50127523FF75B100CD5B95 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D50127423FF75B100CD5B95 /* RunnerTests.m */; };
+		0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A9109268D4A050056B5E1 /* ListTest.m */; };
+		0D7A910D268E5D700056B5E1 /* all_void.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A910C268E5D700056B5E1 /* all_void.gen.m */; };
 		0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D8C35EA25D45A7900B76435 /* AsyncHandlersTest.m */; };
 		0DD2E6BA2684031300A7D764 /* void_arg_host.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD2E6A62684031200A7D764 /* void_arg_host.gen.m */; };
 		0DD2E6BB2684031300A7D764 /* list.gen.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DD2E6A72684031200A7D764 /* list.gen.m */; };
@@ -58,6 +60,9 @@
 		0D50127223FF75B100CD5B95 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		0D50127423FF75B100CD5B95 /* RunnerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RunnerTests.m; sourceTree = "<group>"; };
 		0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		0D7A9109268D4A050056B5E1 /* ListTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ListTest.m; sourceTree = "<group>"; };
+		0D7A910B268E5D700056B5E1 /* all_void.gen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = all_void.gen.h; sourceTree = "<group>"; };
+		0D7A910C268E5D700056B5E1 /* all_void.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = all_void.gen.m; sourceTree = "<group>"; };
 		0D8C35EA25D45A7900B76435 /* AsyncHandlersTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AsyncHandlersTest.m; sourceTree = "<group>"; };
 		0DD2E6A62684031200A7D764 /* void_arg_host.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = void_arg_host.gen.m; sourceTree = "<group>"; };
 		0DD2E6A72684031200A7D764 /* list.gen.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = list.gen.m; sourceTree = "<group>"; };
@@ -127,6 +132,7 @@
 				0DF4E5C6266ED80900AEA855 /* EchoMessenger.h */,
 				0DF4E5C7266ED80900AEA855 /* EchoMessenger.m */,
 				0DF4E5CA266FDAE300AEA855 /* EnumTest.m */,
+				0D7A9109268D4A050056B5E1 /* ListTest.m */,
 			);
 			path = RunnerTests;
 			sourceTree = "<group>";
@@ -164,6 +170,8 @@
 		97C146F01CF9000F007C117D /* Runner */ = {
 			isa = PBXGroup;
 			children = (
+				0D7A910B268E5D700056B5E1 /* all_void.gen.h */,
+				0D7A910C268E5D700056B5E1 /* all_void.gen.m */,
 				0DD2E6AA2684031300A7D764 /* all_datatypes.gen.h */,
 				0DD2E6B22684031300A7D764 /* all_datatypes.gen.m */,
 				0DD2E6AE2684031300A7D764 /* async_handlers.gen.h */,
@@ -346,6 +354,7 @@
 				0DF4E5C8266ED80900AEA855 /* EchoMessenger.m in Sources */,
 				0DF4E5CB266FDAE300AEA855 /* EnumTest.m in Sources */,
 				0D8C35EB25D45A7900B76435 /* AsyncHandlersTest.m in Sources */,
+				0D7A910A268D4A050056B5E1 /* ListTest.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -354,6 +363,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				0DD2E6BD2684031300A7D764 /* async_handlers.gen.m in Sources */,
+				0D7A910D268E5D700056B5E1 /* all_void.gen.m in Sources */,
 				0DD2E6C12684031300A7D764 /* voidhost.gen.m in Sources */,
 				0DD2E6BB2684031300A7D764 /* list.gen.m in Sources */,
 				978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/ListTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/ListTest.m
new file mode 100644
index 0000000..c50cd14
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/ListTest.m
@@ -0,0 +1,35 @@
+// Copyright 2013 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 <Flutter/Flutter.h>
+#import <XCTest/XCTest.h>
+#import "EchoMessenger.h"
+#import "list.gen.h"
+
+///////////////////////////////////////////////////////////////////////////////////////////
+@interface ListTest : XCTestCase
+@end
+
+///////////////////////////////////////////////////////////////////////////////////////////
+@implementation ListTest
+
+- (void)testListInList {
+  LSTTestMessage* top = [[LSTTestMessage alloc] init];
+  LSTTestMessage* inside = [[LSTTestMessage alloc] init];
+  inside.testList = @[ @1, @2, @3 ];
+  top.testList = @[ inside ];
+  EchoBinaryMessenger* binaryMessenger = [[EchoBinaryMessenger alloc] init];
+  LSTEchoApi* api = [[LSTEchoApi alloc] initWithBinaryMessenger:binaryMessenger];
+  XCTestExpectation* expectation = [self expectationWithDescription:@"callback"];
+  [api echo:top
+      completion:^(LSTTestMessage* _Nonnull result, NSError* _Nullable err) {
+        XCTAssertEqual(1u, result.testList.count);
+        XCTAssertTrue([result.testList[0] isKindOfClass:[LSTTestMessage class]]);
+        XCTAssertEqualObjects(inside.testList, [result.testList[0] testList]);
+        [expectation fulfill];
+      }];
+  [self waitForExpectations:@[ expectation ] timeout:1.0];
+}
+
+@end
diff --git a/packages/pigeon/run_tests.sh b/packages/pigeon/run_tests.sh
index 0b8edd6..af121e4 100755
--- a/packages/pigeon/run_tests.sh
+++ b/packages/pigeon/run_tests.sh
@@ -230,6 +230,7 @@
   cd e2e_tests/test_objc/
   flutter pub get
   popd
+  test_pigeon_dart ./pigeons/all_void.dart
   test_pigeon_dart ./pigeons/async_handlers.dart
   test_pigeon_dart ./pigeons/host2flutter.dart
   test_pigeon_dart ./pigeons/list.dart
@@ -241,6 +242,7 @@
 }
 
 run_ios_unittests() {
+  gen_ios_unittests_code ./pigeons/all_void.dart ""
   gen_ios_unittests_code ./pigeons/all_datatypes.dart ""
   gen_ios_unittests_code ./pigeons/async_handlers.dart ""
   gen_ios_unittests_code ./pigeons/enum.dart "AC"
@@ -293,6 +295,7 @@
 run_android_unittests() {
   pushd $PWD
   gen_android_unittests_code ./pigeons/all_datatypes.dart AllDatatypes
+  gen_android_unittests_code ./pigeons/all_void.dart AllVoid
   gen_android_unittests_code ./pigeons/android_unittests.dart Pigeon
   gen_android_unittests_code ./pigeons/async_handlers.dart AsyncHandlers
   gen_android_unittests_code ./pigeons/host2flutter.dart Host2Flutter
diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart
index 156603c..c26e85d 100644
--- a/packages/pigeon/test/dart_generator_test.dart
+++ b/packages/pigeon/test/dart_generator_test.dart
@@ -215,7 +215,6 @@
     // we mention "doSomething" in the assertion message.
     expect(code, isNot(matches('[^!]=.*doSomething')));
     expect(code, contains('doSomething('));
-    expect(code, isNot(contains('.encode()')));
   });
 
   test('flutter void argument', () {
diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart
index 9167aa9..84276b0 100644
--- a/packages/pigeon/test/java_generator_test.dart
+++ b/packages/pigeon/test/java_generator_test.dart
@@ -210,7 +210,6 @@
     generateJava(javaOptions, root, sink);
     final String code = sink.toString();
     expect(code, contains('Reply<Void>'));
-    expect(code, isNot(contains('.fromMap(')));
     expect(code, contains('callback.reply(null)'));
   });