diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/AndroidCleaner.kt b/uniffi_bindgen/src/bindings/kotlin/templates/AndroidCleaner.kt deleted file mode 100644 index 964ccde456..0000000000 --- a/uniffi_bindgen/src/bindings/kotlin/templates/AndroidCleaner.kt +++ /dev/null @@ -1,49 +0,0 @@ -// Android Cleaner implementation - -interface AndroidCleaner { - fun register(value: Any, cleanUpTask: Runnable): AndroidCleanable - - companion object { - fun instance(): AndroidCleaner = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - AndroidSystemCleaner() - } else { - AndroidJnaCleaner() - } - } -} - -interface AndroidCleanable { - fun clean() -} - -class AndroidSystemCleanable( - private val cleanable: Cleaner.Cleanable, -) : AndroidCleanable { - override fun clean() { - cleanable.clean() - } -} - -class AndroidJnaCleanable( - private val cleanable: JnaCleaner.Cleanable, -) : AndroidCleanable { - override fun clean() { - cleanable.clean() - } -} - -class AndroidSystemCleaner : AndroidCleaner { - val cleaner = SystemCleaner.cleaner() - - override fun register(value: Any, cleanUpTask: Runnable): AndroidCleanable { - return AndroidSystemCleanable(cleaner.register(value, cleanUpTask)) - } -} - -class AndroidJnaCleaner : AndroidCleaner { - val cleaner = JnaCleaner.getCleaner() - - override fun register(value: Any, cleanUpTask: Runnable): AndroidCleanable { - return AndroidJnaCleanable(cleaner.register(value, cleanUpTask)) - } -} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt index fd708a0e6a..160aa81814 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/NamespaceLibraryTemplate.kt @@ -29,13 +29,9 @@ internal interface UniffiLib : Library { } } {% if ci.contains_object_types() %} - // The cleaner for the whole library. - internal val CLEANER: {% if config.android_cleaner() -%} AndroidCleaner {%- else -%} Cleaner {%- endif %} by lazy { - {%- if config.android_cleaner() %} - AndroidCleaner.instance() - {%- else %} - Cleaner.create() - {%- endif %} + // The Cleaner for the whole library + internal val CLEANER: UniffiCleaner by lazy { + UniffiCleaner.create() } {%- endif %} } diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt new file mode 100644 index 0000000000..e3e85544d7 --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelper.kt @@ -0,0 +1,40 @@ + +// The cleaner interface for Object finalization code to run. +// This is the entry point to any implementation that we're using. +// +// The cleaner registers objects and returns cleanables, so now we are +// defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the +// different implmentations available at compile time. +interface UniffiCleaner { + interface Cleanable { + fun clean() + } + + fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable + + companion object +} + +// The fallback Jna cleaner, which is available for both Android, and the JVM. +private class UniffiJnaCleaner : UniffiCleaner { + private val cleaner = com.sun.jna.internal.Cleaner.getCleaner() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) +} + +private class UniffiJnaCleanable( + private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} + +// We decide at uniffi binding generation time whether we were +// using Android or not. +// There are further runtime checks to chose the correct implementation +// of the cleaner. +{% if config.android_cleaner() %} +{%- include "ObjectCleanerHelperAndroid.kt" %} +{%- else %} +{%- include "ObjectCleanerHelperJvm.kt" %} +{%- endif %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt new file mode 100644 index 0000000000..972a9a1bb8 --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperAndroid.kt @@ -0,0 +1,22 @@ +{{- self.add_import("android.os.Build") }} + +private fun UniffiCleaner.Companion.create(): UniffiCleaner = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + AndroidSystemCleaner() + } else { + UniffiJnaCleaner() + } + +// The SystemCleaner, available from API Level 33. +private class AndroidSystemCleaner : UniffiCleaner { + val cleaner = android.system.SystemCleaner.cleaner() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + AndroidSystemCleanable(cleaner.register(value, cleanUpTask)) +} + +private class AndroidSystemCleanable( + private val cleanable: java.lang.ref.Cleaner.Cleanable, +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt new file mode 100644 index 0000000000..1527234f6c --- /dev/null +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectCleanerHelperJvm.kt @@ -0,0 +1,25 @@ +private fun UniffiCleaner.Companion.create(): UniffiCleaner = + try { + // For safety's sake: if the library hasn't been run in android_cleaner = true + // mode, but is being run on Android, then we still need to think about + // Android API versions. + // So we check if java.lang.ref.Cleaner is there, and use that… + Class.`forName`("java.lang.ref.Cleaner") + JavaLangRefCleaner() + } catch (e: ClassNotFoundException) { + // … otherwise, fallback to the JNA cleaner. + UniffiJnaCleaner() + } + +private class JavaLangRefCleaner : UniffiCleaner { + val cleaner = java.lang.ref.Cleaner.create() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) +} + +private class JavaLangRefCleanable( + val cleanable: java.lang.ref.Cleaner.Cleanable +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt index a046d01968..7cddda5e2b 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectRuntime.kt @@ -1,6 +1,6 @@ -{{- self.add_import("java.lang.ref.Cleaner") }} {{- self.add_import("java.util.concurrent.atomic.AtomicLong") }} {{- self.add_import("java.util.concurrent.atomic.AtomicBoolean") }} +{%- include "ObjectCleanerHelper.kt" %} // The base class for all UniFFI Object types. // @@ -115,7 +115,7 @@ abstract class FFIObject: Disposable, AutoCloseable { } protected val pointer: Pointer? - protected abstract val cleanable: {% if config.android_cleaner() -%} AndroidCleanable {%- else -%} Cleaner.Cleanable {%- endif %} + protected abstract val cleanable: UniffiCleaner.Cleanable private val wasDestroyed = AtomicBoolean(false) private val callCounter = AtomicLong(1) diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index a5919bb12c..efc9d485f2 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -29,7 +29,7 @@ open class {{ impl_class_name }} : FFIObject, {{ interface_name }} { {%- when None %} {%- endmatch %} - override val cleanable: Cleaner.Cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + override val cleanable: UniffiCleaner.Cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) // Use a static inner class instead of a closure so as not to accidentally // capture `this` as part of the cleanable's action. diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt index 00e8d0e01b..0f924e4d63 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/wrapper.kt @@ -31,11 +31,6 @@ import java.nio.ByteOrder import java.nio.CharBuffer import java.nio.charset.CodingErrorAction import java.util.concurrent.ConcurrentHashMap -{%- if config.android_cleaner() %} -import android.os.Build -import android.system.SystemCleaner -import com.sun.jna.internal.Cleaner as JnaCleaner -{%- endif %} {%- for req in self.imports() %} {{ req.render() }}