From eba6a4a00028993203b39f3192eec6b43d9e8aa1 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Fri, 26 Nov 2021 16:56:28 +0300 Subject: [PATCH] KT-44089 Reflect Android SDK version to determine whether platform implementatoins are eligible SDK version sanity check, suppress all reflection errors, duplicate SDK reflection code for more robustness --- .../jdk7/JDK7PlatformImplementations.kt | 26 ++++++++++++++++--- .../jdk8/JDK8PlatformImplementations.kt | 18 ++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt b/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt index b3269e7b93d..623a3250979 100644 --- a/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt +++ b/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt @@ -10,8 +10,28 @@ import kotlin.internal.PlatformImplementations internal open class JDK7PlatformImplementations : PlatformImplementations() { - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - override fun addSuppressed(cause: Throwable, exception: Throwable) = (cause as java.lang.Throwable).addSuppressed(exception) + private object ReflectSdkVersion { + @JvmField + public val sdkVersion: Int? = try { + Class.forName("android.os.Build\$VERSION").getField("SDK_INT").get(null) as? Int + } catch (e: Throwable) { + null + }?.takeIf { it > 0 } + } - override fun getSuppressed(exception: Throwable): List = exception.suppressed.asList() + private fun sdkIsNullOrAtLeast(version: Int): Boolean = ReflectSdkVersion.sdkVersion == null || ReflectSdkVersion.sdkVersion >= version + + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + override fun addSuppressed(cause: Throwable, exception: Throwable) = + if (sdkIsNullOrAtLeast(19)) + (cause as java.lang.Throwable).addSuppressed(exception) + else + super.addSuppressed(cause, exception) + + override fun getSuppressed(exception: Throwable): List = + if (sdkIsNullOrAtLeast(19)) + exception.suppressed.asList() + else + super.getSuppressed(exception) } diff --git a/libraries/stdlib/jdk8/src/kotlin/internal/jdk8/JDK8PlatformImplementations.kt b/libraries/stdlib/jdk8/src/kotlin/internal/jdk8/JDK8PlatformImplementations.kt index 329e5950699..2c35969ce54 100644 --- a/libraries/stdlib/jdk8/src/kotlin/internal/jdk8/JDK8PlatformImplementations.kt +++ b/libraries/stdlib/jdk8/src/kotlin/internal/jdk8/JDK8PlatformImplementations.kt @@ -26,6 +26,19 @@ import kotlin.random.jdk8.PlatformThreadLocalRandom internal open class JDK8PlatformImplementations : JDK7PlatformImplementations() { + // the same SDK version check as in the base class is duplicated here, + // to avoid having a non-public cross-module dependency + private object ReflectSdkVersion { + @JvmField + public val sdkVersion: Int? = try { + Class.forName("android.os.Build\$VERSION").getField("SDK_INT").get(null) as? Int + } catch (e: Throwable) { + null + }?.takeIf { it > 0 } + } + + private fun sdkIsNullOrAtLeast(version: Int): Boolean = ReflectSdkVersion.sdkVersion == null || ReflectSdkVersion.sdkVersion >= version + override fun getMatchResultNamedGroup(matchResult: MatchResult, name: String): MatchGroup? { val matcher = matchResult as? Matcher ?: throw UnsupportedOperationException("Retrieving groups by name is not supported on this platform.") @@ -36,6 +49,9 @@ internal open class JDK8PlatformImplementations : JDK7PlatformImplementations() null } - override fun defaultPlatformRandom(): Random = PlatformThreadLocalRandom() + override fun defaultPlatformRandom(): Random = + // while ThreadLocalRandom is available since SDK 21 (as documented), it has bugs in the implementation, + // so we don't use it for the same reasons as why we don't use it in JDK7. + if (sdkIsNullOrAtLeast(24)) PlatformThreadLocalRandom() else super.defaultPlatformRandom() }