Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pigeon] Kotlin implementation for ProxyApis #6371

Merged
merged 115 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
7d66bf7
add other stuff
bparrishMines Mar 21, 2024
d64d2a2
add partial generator code
bparrishMines Mar 21, 2024
b64d723
add generated file
bparrishMines Mar 21, 2024
89d3c92
finish additions
bparrishMines Mar 21, 2024
1f7d0ad
some fixes
bparrishMines Mar 21, 2024
fa3c943
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 10, 2024
2665c22
add onWriteBody
bparrishMines Apr 11, 2024
cadf14c
use a common method for api math
bparrishMines Apr 15, 2024
2bf6738
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 15, 2024
6437fc9
add all stuff back
bparrishMines Apr 15, 2024
aea71cd
changelogg
bparrishMines Apr 15, 2024
0c09e91
formatting
bparrishMines Apr 16, 2024
f114c8c
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 16, 2024
d57ac0d
fix proxy api separated suffix
bparrishMines Apr 16, 2024
ff1e210
switch to a registrar
bparrishMines Apr 16, 2024
7e345ac
docs
bparrishMines Apr 16, 2024
db2a198
formatting
bparrishMines Apr 16, 2024
a7c94e5
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 16, 2024
7af4343
fix unit tests
bparrishMines Apr 16, 2024
a7b20a0
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 16, 2024
f2a1d5b
improve helper method and add test
bparrishMines Apr 17, 2024
f76265e
add a setter for clearFinalizedWeakReferencesInterval
bparrishMines Apr 17, 2024
b8e70f6
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 17, 2024
4f2327d
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Apr 24, 2024
5ade010
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 7, 2024
3b3a91b
return empty list in removeStrongReference
bparrishMines May 7, 2024
ec37d62
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 9, 2024
186fe47
update generated file
bparrishMines May 9, 2024
894e642
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 15, 2024
118606a
make private codec accessible and use open for pigeonRegistrar
bparrishMines May 20, 2024
4e821cb
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 20, 2024
4205418
nits
bparrishMines May 20, 2024
258653b
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 24, 2024
5070f6e
shared channel code for instancemanagerapi
bparrishMines May 29, 2024
06d163b
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 29, 2024
79e994b
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines May 30, 2024
1b9dfc1
make fullclassname nullable
bparrishMines May 30, 2024
f91a51f
use name constants and add a computation to the proxy api
bparrishMines May 30, 2024
84e0a0c
fix unit tests
bparrishMines May 30, 2024
bdf1aae
test api registrar
bparrishMines May 30, 2024
2ff9075
support a default api implementation
bparrishMines May 30, 2024
b0569f5
update instancemanager docs
bparrishMines Jun 3, 2024
4f47756
stops
bparrishMines Jun 3, 2024
03d1100
switch to format
bparrishMines Jun 4, 2024
c49aff2
regen and no ps
bparrishMines Jun 4, 2024
c7e5ad0
use addDocumenetationCommetns instead
bparrishMines Jun 4, 2024
d1883be
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 4, 2024
3025438
use write scoped
bparrishMines Jun 4, 2024
9af2d08
use indentscoped
bparrishMines Jun 4, 2024
78128b8
use format
bparrishMines Jun 4, 2024
84879d2
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 4, 2024
90f38b6
fix api check probably
bparrishMines Jun 4, 2024
505b500
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 11, 2024
172ef36
update codecs to be compatible
bparrishMines Jun 11, 2024
c294d49
analyze error fix
bparrishMines Jun 12, 2024
eaf1302
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 12, 2024
03f8a9e
dont use raw when passing enums and test parent codec
bparrishMines Jun 12, 2024
6a57229
formatting
bparrishMines Jun 12, 2024
38ab4a1
verify api requirements work
bparrishMines Jun 17, 2024
78ade11
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 17, 2024
fcb3aa5
use helper method
bparrishMines Jun 21, 2024
61579bc
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jun 28, 2024
c981d97
update format trim indentation
bparrishMines Jul 12, 2024
60be3da
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 12, 2024
0b7de44
also check nullable can return nonnull
bparrishMines Jul 12, 2024
8974f28
update to use file specific prefixes
bparrishMines Jul 16, 2024
4ae228d
fix test
bparrishMines Jul 16, 2024
e5ad0ce
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 16, 2024
77ac326
logging has name limits
bparrishMines Jul 16, 2024
33e3742
add file prefix
bparrishMines Jul 16, 2024
927aeea
test fix hopefullY
bparrishMines Jul 16, 2024
ce65169
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 17, 2024
1dd1eda
gen without formatting
bparrishMines Jul 19, 2024
bd08aa0
same code no gens
bparrishMines Jul 19, 2024
b2ae858
formatting
bparrishMines Jul 19, 2024
b2b82f6
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 19, 2024
4627f4a
formatting
bparrishMines Jul 19, 2024
abe2c0d
check supported types first
bparrishMines Jul 29, 2024
92a7370
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 29, 2024
6d5b581
maybe fix kotlin
bparrishMines Jul 29, 2024
770b24c
M`erge branch 'main' of github.com:flutter/packages into pigeon_kotli…
bparrishMines Jul 29, 2024
9e59393
add ignoreCallsToDart
bparrishMines Jul 31, 2024
bc60c15
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Jul 31, 2024
cb10073
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 9, 2024
3b3284a
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 15, 2024
cae65e8
fix kotlin tests and add kotlin prefixes
bparrishMines Aug 15, 2024
82f0cee
fix tools version
bparrishMines Aug 15, 2024
233acf5
fix lint
bparrishMines Aug 15, 2024
e810a8f
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 15, 2024
b95997e
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 16, 2024
24daaa1
try fix instance manager api name
bparrishMines Aug 17, 2024
f0aa519
fix tests
bparrishMines Aug 17, 2024
fed5b58
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 17, 2024
ccb1783
fix unit tests
bparrishMines Aug 17, 2024
0a2a643
fix log name
bparrishMines Aug 17, 2024
942918b
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 23, 2024
89abc69
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 26, 2024
cc55be5
regen code
bparrishMines Aug 26, 2024
41fca50
try lowering version
bparrishMines Aug 26, 2024
78e1e48
fix codec name in instancemanagerapi
bparrishMines Aug 27, 2024
e844875
fix kotlin api too
bparrishMines Aug 27, 2024
af2965f
regen
bparrishMines Aug 27, 2024
25d4515
raise version again
bparrishMines Aug 27, 2024
c5a083e
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 27, 2024
676eb05
some docs
bparrishMines Aug 27, 2024
f7b662a
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 29, 2024
d79c8f0
review comments
bparrishMines Aug 29, 2024
f4ac560
fix other comments locations
bparrishMines Aug 29, 2024
f70c2f1
include gen file actually
bparrishMines Aug 29, 2024
c1f23ae
analyze error
bparrishMines Aug 29, 2024
b958879
codec is not private since it is used in registrar
bparrishMines Aug 29, 2024
ecf079c
Merge branch 'main' of github.com:flutter/packages into pigeon_kotlin…
bparrishMines Aug 29, 2024
98d088f
fix unit test
bparrishMines Aug 29, 2024
923e21e
use messagecodec instead
bparrishMines Aug 29, 2024
f9d5b78
version bump
bparrishMines Aug 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 19.1.0

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

## 19.0.1

* [dart] Updates `PigeonInstanceMangerApi` to use the shared api channel code.
Expand Down
18 changes: 17 additions & 1 deletion packages/pigeon/lib/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

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

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need Kotlin options in a class that's currently unaware of specific generators?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the generated code references the native class, each ProxyApi needs to provide the full class name with the package. I also think Method.swiftFunction is similar since it provides additional instructions for the Swift generator.

});

/// List of constructors inside the API.
Expand All @@ -153,6 +155,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 @@ -252,7 +258,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 @@ -264,6 +270,16 @@ 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;

// Recursively search for all the interfaces apis from a list of names of
// interfaces.
//
Expand Down
50 changes: 49 additions & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '19.0.1';
const String pigeonVersion = '19.1.0';

/// Prefix for all local variables in methods.
///
Expand Down Expand Up @@ -307,6 +307,12 @@ const String seeAlsoWarning = 'See also: https://pub.dev/packages/pigeon';
/// parameters.
const String classNamePrefix = 'Pigeon';

/// Prefix for APIs generated for ProxyApi.
///
/// Since ProxyApis are intended to wrap a class and will often share the name
/// of said class, host APIs should prefix the API with this protected name.
const String hostProxyApiPrefix = '${classNamePrefix}Api';

/// Name for the generated InstanceManager for ProxyApis.
///
/// This lowers the chances of variable name collisions with user defined
Expand Down Expand Up @@ -494,6 +500,48 @@ Map<TypeDeclaration, List<int>> getReferencedTypes(
return references.map;
}

/// Find the [TypeDeclaration] that has the highest API requirement and its
/// version, [T].
///
/// [T] depends on the language. For example, Android uses an int while iOS uses
/// semantic versioning.
({TypeDeclaration type, T version})?
findHighestApiRequirement<T extends Object>(
Iterable<TypeDeclaration> types, {
required T? Function(TypeDeclaration) onGetApiRequirement,
required Comparator<T> onCompare,
}) {
Iterable<TypeDeclaration> addAllRecursive(TypeDeclaration type) sync* {
yield type;
if (type.typeArguments.isNotEmpty) {
for (final TypeDeclaration typeArg in type.typeArguments) {
yield* addAllRecursive(typeArg);
}
}
}

final Iterable<TypeDeclaration> allReferencedTypes = types
.expand(addAllRecursive)
.where((TypeDeclaration type) => onGetApiRequirement(type) != null);

if (allReferencedTypes.isEmpty) {
return null;
}

final TypeDeclaration typeWithHighestRequirement = allReferencedTypes.reduce(
(TypeDeclaration one, TypeDeclaration two) {
return onCompare(onGetApiRequirement(one)!, onGetApiRequirement(two)!) > 0
? one
: two;
tarrinneal marked this conversation as resolved.
Show resolved Hide resolved
},
);

return (
type: typeWithHighestRequirement,
version: onGetApiRequirement(typeWithHighestRequirement)!,
);
}

/// Returns true if the concrete type cannot be determined at compile-time.
bool _isConcreteTypeAmbiguous(TypeDeclaration type) {
return (type.baseName == 'List' && type.typeArguments.isEmpty) ||
Expand Down
256 changes: 256 additions & 0 deletions packages/pigeon/lib/kotlin/templates.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// 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 '../generator_tools.dart';

/// The Kotlin `InstanceManager`.
const String instanceManagerTemplate = '''
/**
* Maintains instances used to communicate with the corresponding objects in Dart.
*
* <p>Objects stored in this container are represented by an object in Dart that is also stored in
* an InstanceManager with the same identifier.
*
* <p>When an instance is added with an identifier, either can be used to retrieve the other.
*
* <p>Added instances are added as a weak reference and a strong reference. When the strong
* reference is removed with [remove] and the weak reference is deallocated, the
* `finalizationListener` is made with the instance's identifier. However, if the strong reference
* is removed and then the identifier is retrieved with the intention to pass the identifier to Dart
* (e.g. calling [getIdentifierForStrongReference]), the strong reference to the
* instance is recreated. The strong reference will then need to be removed manually again.
*/
@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused")
bparrishMines marked this conversation as resolved.
Show resolved Hide resolved
class $instanceManagerClassName(private val finalizationListener: $_finalizationListenerClassName) {
/** Interface for listening when a weak reference of an instance is removed from the manager. */
interface $_finalizationListenerClassName {
fun onFinalize(identifier: Long)
}

private val identifiers = java.util.WeakHashMap<Any, Long>()
private val weakInstances = HashMap<Long, java.lang.ref.WeakReference<Any>>()
private val strongInstances = HashMap<Long, Any>()
private val referenceQueue = java.lang.ref.ReferenceQueue<Any>()
private val weakReferencesToIdentifiers = HashMap<java.lang.ref.WeakReference<Any>, Long>()
Comment on lines +45 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure you've already figured out this is the best way, but why is it that we need the java Weak version of these data structures? Is there no kotlin version that's usable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the Kotlin->Java converter from Android Studio and this is what it used. I tried using the Kotlin version of WeakReference, but it seems to not be supported by this version of Kotlin. Same goes for the other java.util and java.lang classes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What version do we need to use the kotlin version? Maybe we need a todo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like it was created in 1.3. This plugin uses 1.7.10. But it looks like the api is still experimental: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.ref/-weak-reference/.

No version, including the latest 2.0, has WeakReference as not experimental. And it looks like experimental apis require an opt-in, which I think is something we wouldn't want to do: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.experimental/-experimental-native-api/.

private val handler = android.os.Handler(android.os.Looper.getMainLooper())
private var nextIdentifier: Long = minHostCreatedIdentifier
private var hasFinalizationListenerStopped = false

/**
* Modifies the time interval used to define how often this instance removes garbage collected
* weak references to native Android objects that this instance was managing.
*/
var clearFinalizedWeakReferencesInterval: Long = 3000
set(value) {
handler.removeCallbacks { this.releaseAllFinalizedInstances() }
field = value
releaseAllFinalizedInstances()
}

init {
handler.postDelayed(
{ releaseAllFinalizedInstances() },
clearFinalizedWeakReferencesInterval
)
}

companion object {
// Identifiers are locked to a specific range to avoid collisions with objects
// created simultaneously from Dart.
// Host uses identifiers >= 2^16 and Dart is expected to use values n where,
// 0 <= n < 2^16.
private const val minHostCreatedIdentifier: Long = 65536
private const val tag = "$instanceManagerClassName"

/**
* Instantiate a new manager.
*
*
* When the manager is no longer needed, [stopFinalizationListener] must be called.
*
* @param finalizationListener the listener for garbage collected weak references.
* @return a new `$instanceManagerClassName`.
bparrishMines marked this conversation as resolved.
Show resolved Hide resolved
*/
fun create(finalizationListener: $_finalizationListenerClassName): $instanceManagerClassName {
return $instanceManagerClassName(finalizationListener)
}
}

/**
* Removes `identifier` and its associated strongly referenced instance, if present, from the
* manager.
*
* @param identifier the identifier paired to an instance.
* @param <T> the expected return type.
* @return the removed instance if the manager contains the given identifier, otherwise `null` if
* the manager doesn't contain the value.
</T> */
fun <T> remove(identifier: Long): T? {
logWarningIfFinalizationListenerHasStopped()
return strongInstances.remove(identifier) as T?
}

/**
* Retrieves the identifier paired with an instance.
*
*
* If the manager contains a strong reference to `instance`, it will return the identifier
* associated with `instance`. If the manager contains only a weak reference to `instance`, a new
* strong reference to `instance` will be added and will need to be removed again with [remove].
*
*
* If this method returns a nonnull identifier, this method also expects the Dart
* `$instanceManagerClassName` to have, or recreate, a weak reference to the Dart instance the
* identifier is associated with.
*
* @param instance an instance that may be stored in the manager.
* @return the identifier associated with `instance` if the manager contains the value, otherwise
* `null` if the manager doesn't contain the value.
*/
fun getIdentifierForStrongReference(instance: Any?): Long? {
logWarningIfFinalizationListenerHasStopped()
val identifier = identifiers[instance]
if (identifier != null) {
strongInstances[identifier] = instance!!
}
return identifier
}

/**
* Adds a new instance that was instantiated from Dart.
*
*
* The same instance can be added multiple times, but each identifier must be unique. This
* allows two objects that are equivalent (e.g. the `equals` method returns true and their
* hashcodes are equal) to both be added.
*
* @param instance the instance to be stored.
* @param identifier the identifier to be paired with instance. This value must be >= 0 and
* unique.
*/
fun addDartCreatedInstance(instance: Any, identifier: Long) {
logWarningIfFinalizationListenerHasStopped()
addInstance(instance, identifier)
}

/**
* Adds a new instance that was instantiated from the host platform.
*
* @param instance the instance to be stored. This must be unique to all other added instances.
* @return the unique identifier (>= 0) stored with instance.
*/
fun addHostCreatedInstance(instance: Any): Long {
logWarningIfFinalizationListenerHasStopped()
require(!containsInstance(instance)) { "Instance of \${instance.javaClass} has already been added." }
val identifier = nextIdentifier++
addInstance(instance, identifier)
return identifier
}

/**
* Retrieves the instance associated with identifier.
*
* @param identifier the identifier associated with an instance.
* @param <T> the expected return type.
* @return the instance associated with `identifier` if the manager contains the value, otherwise
* `null` if the manager doesn't contain the value.
</T> */
fun <T> getInstance(identifier: Long): T? {
logWarningIfFinalizationListenerHasStopped()
val instance = weakInstances[identifier] as java.lang.ref.WeakReference<T>?
return instance?.get()
}

/**
* Returns whether this manager contains the given `instance`.
*
* @param instance the instance whose presence in this manager is to be tested.
* @return whether this manager contains the given `instance`.
*/
fun containsInstance(instance: Any?): Boolean {
logWarningIfFinalizationListenerHasStopped()
return identifiers.containsKey(instance)
}

/**
* Stop the periodic run of the [$_finalizationListenerClassName] for instances that have been garbage
bparrishMines marked this conversation as resolved.
Show resolved Hide resolved
* collected.
*
*
* The InstanceManager can continue to be used, but the [$_finalizationListenerClassName] will no
* longer be called and methods will log a warning.
*/
fun stopFinalizationListener() {
handler.removeCallbacks { this.releaseAllFinalizedInstances() }
hasFinalizationListenerStopped = true
}

/**
* Removes all of the instances from this manager.
*
*
* The manager will be empty after this call returns.
*/
fun clear() {
identifiers.clear()
weakInstances.clear()
strongInstances.clear()
weakReferencesToIdentifiers.clear()
}

/**
* Whether the [$_finalizationListenerClassName] is still being called for instances that are garbage
* collected.
*
*
* See [stopFinalizationListener].
*/
fun hasFinalizationListenerStopped(): Boolean {
return hasFinalizationListenerStopped
}

private fun releaseAllFinalizedInstances() {
if (hasFinalizationListenerStopped()) {
return
}
var reference: java.lang.ref.WeakReference<Any>?
while ((referenceQueue.poll() as java.lang.ref.WeakReference<Any>?).also { reference = it } != null) {
val identifier = weakReferencesToIdentifiers.remove(reference)
if (identifier != null) {
weakInstances.remove(identifier)
strongInstances.remove(identifier)
finalizationListener.onFinalize(identifier)
}
}
handler.postDelayed(
{ releaseAllFinalizedInstances() },
clearFinalizedWeakReferencesInterval
)
}

private fun addInstance(instance: Any, identifier: Long) {
require(identifier >= 0) { "Identifier must be >= 0: \$identifier" }
require(!weakInstances.containsKey(identifier)) {
"Identifier has already been added: \$identifier"
}
val weakReference = java.lang.ref.WeakReference(instance, referenceQueue)
identifiers[instance] = identifier
weakInstances[identifier] = weakReference
weakReferencesToIdentifiers[weakReference] = identifier
strongInstances[identifier] = instance
}

private fun logWarningIfFinalizationListenerHasStopped() {
if (hasFinalizationListenerStopped()) {
Log.w(
tag,
"The manager was used after calls to the $_finalizationListenerClassName has been stopped."
)
}
}
}
''';

const String _finalizationListenerClassName =
'${classNamePrefix}FinalizationListener';
Loading