Skip to content

Commit

Permalink
[pigeon] Kotlin implementation for ProxyApis (#6371)
Browse files Browse the repository at this point in the history
Kotlin portion of flutter/flutter#134777
  • Loading branch information
bparrishMines authored Aug 30, 2024
1 parent d472256 commit c9c0004
Show file tree
Hide file tree
Showing 26 changed files with 8,733 additions and 189 deletions.
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 22.2.0

* [kotlin] Adds implementation for `@ProxyApi`.

## 22.1.0

* Allows generation of classes that aren't referenced in an API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ data class MessageData(
}
}

private object MessagesPigeonCodec : StandardMessageCodec() {
private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
129.toByte() -> {
Expand Down Expand Up @@ -122,7 +122,7 @@ interface ExampleHostApi {

companion object {
/** The codec used by ExampleHostApi. */
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec }
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec() }
/** Sets up an instance of `ExampleHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(
Expand Down Expand Up @@ -209,7 +209,7 @@ class MessageFlutterApi(
) {
companion object {
/** The codec used by MessageFlutterApi. */
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec }
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec() }
}

fun flutterMethod(aStringArg: String?, callback: (Result<String>) -> Unit) {
Expand Down
24 changes: 23 additions & 1 deletion packages/pigeon/lib/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import 'package:collection/collection.dart' show ListEquality;
import 'package:meta/meta.dart';

import 'generator_tools.dart';
import 'kotlin_generator.dart' show KotlinProxyApiOptions;
import 'pigeon_lib.dart';

typedef _ListEquals = bool Function(List<Object?>, List<Object?>);
Expand Down Expand Up @@ -140,6 +142,7 @@ class AstProxyApi extends Api {
required this.fields,
this.superClass,
this.interfaces = const <TypeDeclaration>{},
this.kotlinOptions,
});

/// List of constructors inside the API.
Expand All @@ -154,6 +157,10 @@ class AstProxyApi extends Api {
/// Name of the classes this class considers to be implemented.
Set<TypeDeclaration> interfaces;

/// Options that control how Kotlin code will be generated for a specific
/// ProxyApi.
final KotlinProxyApiOptions? kotlinOptions;

/// Methods implemented in the host platform language.
Iterable<Method> get hostMethods => methods.where(
(Method method) => method.location == ApiLocation.host,
Expand Down Expand Up @@ -253,7 +260,7 @@ class AstProxyApi extends Api {
}
}

/// Whether the api has a method that callbacks to Dart to add a new instance
/// Whether the API has a method that callbacks to Dart to add a new instance
/// to the InstanceManager.
///
/// This is possible as long as no callback methods are required to
Expand All @@ -265,6 +272,21 @@ class AstProxyApi extends Api {
.every((Method method) => !method.isRequired);
}

/// Whether the API has any message calls from Dart to host.
bool hasAnyHostMessageCalls() =>
constructors.isNotEmpty ||
attachedFields.isNotEmpty ||
hostMethods.isNotEmpty;

/// Whether the API has any message calls from host to Dart.
bool hasAnyFlutterMessageCalls() =>
hasCallbackConstructor() || flutterMethods.isNotEmpty;

/// Whether the host proxy API class will have methods that need to be
/// implemented.
bool hasMethodsRequiringImplementation() =>
hasAnyHostMessageCalls() || unattachedFields.isNotEmpty;

// Recursively search for all the interfaces apis from a list of names of
// interfaces.
//
Expand Down
48 changes: 31 additions & 17 deletions packages/pigeon/lib/dart/templates.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@

import '../generator_tools.dart';

/// Name for the generated InstanceManager for ProxyApis.
///
/// This lowers the chances of variable name collisions with user defined
/// parameters.
const String dartInstanceManagerClassName =
'${proxyApiClassNamePrefix}InstanceManager';

/// Name for the generated InstanceManager API for ProxyApis.
///
/// This lowers the chances of variable name collisions with user defined
/// parameters.
const String dartInstanceManagerApiClassName =
'_${classNamePrefix}InstanceManagerApi';

/// Creates the `InstanceManager` with the passed string values.
String instanceManagerTemplate({
required Iterable<String> allProxyApiNames,
Expand All @@ -30,9 +44,9 @@ String instanceManagerTemplate({
/// is added as a weak reference with the same identifier. This prevents a
/// scenario where the weak referenced instance was released and then later
/// returned by the host platform.
class $instanceManagerClassName {
/// Constructs a [$instanceManagerClassName].
$instanceManagerClassName({required void Function(int) onWeakReferenceRemoved}) {
class $dartInstanceManagerClassName {
/// Constructs a [$dartInstanceManagerClassName].
$dartInstanceManagerClassName({required void Function(int) onWeakReferenceRemoved}) {
this.onWeakReferenceRemoved = (int identifier) {
_weakInstances.remove(identifier);
onWeakReferenceRemoved(identifier);
Expand All @@ -46,12 +60,12 @@ class $instanceManagerClassName {
// 0 <= n < 2^16.
static const int _maxDartCreatedIdentifier = 65536;
/// The default [$instanceManagerClassName] used by ProxyApis.
/// The default [$dartInstanceManagerClassName] used by ProxyApis.
///
/// On creation, this manager makes a call to clear the native
/// InstanceManager. This is to prevent identifier conflicts after a host
/// restart.
static final $instanceManagerClassName instance = _initInstance();
static final $dartInstanceManagerClassName instance = _initInstance();
// Expando is used because it doesn't prevent its keys from becoming
// inaccessible. This allows the manager to efficiently retrieve an identifier
Expand All @@ -72,17 +86,17 @@ class $instanceManagerClassName {
/// or becomes inaccessible.
late final void Function(int) onWeakReferenceRemoved;
static $instanceManagerClassName _initInstance() {
static $dartInstanceManagerClassName _initInstance() {
WidgetsFlutterBinding.ensureInitialized();
final _${instanceManagerClassName}Api api = _${instanceManagerClassName}Api();
// Clears the native `$instanceManagerClassName` on the initial use of the Dart one.
final $dartInstanceManagerApiClassName api = $dartInstanceManagerApiClassName();
// Clears the native `$dartInstanceManagerClassName` on the initial use of the Dart one.
api.clear();
final $instanceManagerClassName instanceManager = $instanceManagerClassName(
final $dartInstanceManagerClassName instanceManager = $dartInstanceManagerClassName(
onWeakReferenceRemoved: (int identifier) {
api.removeStrongReference(identifier);
},
);
_${instanceManagerClassName}Api.setUpMessageHandlers(instanceManager: instanceManager);
$dartInstanceManagerApiClassName.setUpMessageHandlers(instanceManager: instanceManager);
${apiHandlerSetUps.join('\n\t\t')}
return instanceManager;
}
Expand Down Expand Up @@ -229,9 +243,9 @@ abstract class $proxyApiBaseClassName {
/// Construct a [$proxyApiBaseClassName].
$proxyApiBaseClassName({
this.$_proxyApiBaseClassMessengerVarName,
$instanceManagerClassName? $_proxyApiBaseClassInstanceManagerVarName,
$dartInstanceManagerClassName? $_proxyApiBaseClassInstanceManagerVarName,
}) : $_proxyApiBaseClassInstanceManagerVarName =
$_proxyApiBaseClassInstanceManagerVarName ?? $instanceManagerClassName.instance;
$_proxyApiBaseClassInstanceManagerVarName ?? $dartInstanceManagerClassName.instance;
/// Sends and receives binary data across the Flutter platform barrier.
///
Expand All @@ -242,12 +256,12 @@ abstract class $proxyApiBaseClassName {
/// Maintains instances stored to communicate with native language objects.
@protected
final $instanceManagerClassName $_proxyApiBaseClassInstanceManagerVarName;
final $dartInstanceManagerClassName $_proxyApiBaseClassInstanceManagerVarName;
/// Instantiates and returns a functionally identical object to oneself.
///
/// Outside of tests, this method should only ever be called by
/// [$instanceManagerClassName].
/// [$dartInstanceManagerClassName].
///
/// Subclasses should always override their parent's implementation of this
/// method.
Expand All @@ -264,11 +278,11 @@ abstract class $proxyApiBaseClassName {
const String proxyApiBaseCodec = '''
class $_proxyApiCodecName extends _PigeonCodec {
const $_proxyApiCodecName(this.instanceManager);
final $instanceManagerClassName instanceManager;
final $dartInstanceManagerClassName instanceManager;
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is $proxyApiBaseClassName) {
buffer.putUint8(128);
buffer.putUint8($proxyApiCodecInstanceManagerKey);
writeValue(buffer, instanceManager.getIdentifier(value));
} else {
super.writeValue(buffer, value);
Expand All @@ -277,7 +291,7 @@ class $_proxyApiCodecName extends _PigeonCodec {
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
case $proxyApiCodecInstanceManagerKey:
return instanceManager
.getInstanceWithWeakReference(readValue(buffer)! as int);
default:
Expand Down
Loading

0 comments on commit c9c0004

Please sign in to comment.