From 1eae2382eed362484d8c2f22e7dc3c6e7e22c5dd Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 6 Mar 2020 18:25:37 +0300 Subject: [PATCH] Make call to service loader in reactor integrations optimizable by R8 (#1847) Fixes #1817 --- reactive/kotlinx-coroutines-reactive/src/Await.kt | 10 ---------- .../kotlinx-coroutines-reactive/src/ReactiveFlow.kt | 9 +++++---- ui/kotlinx-coroutines-android/build.gradle | 4 ++-- .../test/R8ServiceLoaderOptimizationTest.kt | 6 +++--- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/reactive/kotlinx-coroutines-reactive/src/Await.kt b/reactive/kotlinx-coroutines-reactive/src/Await.kt index 317baab5f5..9ea2e3c50e 100644 --- a/reactive/kotlinx-coroutines-reactive/src/Await.kt +++ b/reactive/kotlinx-coroutines-reactive/src/Await.kt @@ -82,16 +82,6 @@ public suspend fun Publisher.awaitSingle(): T = awaitOne(Mode.SINGLE) // ------------------------ private ------------------------ -// ContextInjector service is implemented in `kotlinx-coroutines-reactor` module only. -// If `kotlinx-coroutines-reactor` module is not included, the list is empty. -private val contextInjectors: Array = - ServiceLoader.load(ContextInjector::class.java, ContextInjector::class.java.classLoader).iterator().asSequence().toList().toTypedArray() // R8 opto - -private fun Publisher.injectCoroutineContext(coroutineContext: CoroutineContext) = - contextInjectors.fold(this) { pub, contextInjector -> - contextInjector.injectCoroutineContext(pub, coroutineContext) - } - private enum class Mode(val s: String) { FIRST("awaitFirst"), FIRST_OR_DEFAULT("awaitFirstOrDefault"), diff --git a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt index 3a81c0696d..20e165e705 100644 --- a/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt +++ b/reactive/kotlinx-coroutines-reactive/src/ReactiveFlow.kt @@ -140,13 +140,14 @@ private class ReactiveSubscriber( // ContextInjector service is implemented in `kotlinx-coroutines-reactor` module only. // If `kotlinx-coroutines-reactor` module is not included, the list is empty. -private val contextInjectors: List = - ServiceLoader.load(ContextInjector::class.java, ContextInjector::class.java.classLoader).toList() +private val contextInjectors: Array = + ServiceLoader.load(ContextInjector::class.java, ContextInjector::class.java.classLoader) + .iterator().asSequence() + .toList().toTypedArray() // R8 opto -private fun Publisher.injectCoroutineContext(coroutineContext: CoroutineContext) = +internal fun Publisher.injectCoroutineContext(coroutineContext: CoroutineContext) = contextInjectors.fold(this) { pub, contextInjector -> contextInjector.injectCoroutineContext(pub, coroutineContext) } - /** * Adapter that transforms [Flow] into TCK-complaint [Publisher]. * [cancel] invocation cancels the original flow. diff --git a/ui/kotlinx-coroutines-android/build.gradle b/ui/kotlinx-coroutines-android/build.gradle index 68e2232cf2..51702cdbe3 100644 --- a/ui/kotlinx-coroutines-android/build.gradle +++ b/ui/kotlinx-coroutines-android/build.gradle @@ -77,7 +77,7 @@ task runR8(type: RunR8Task, dependsOn: 'jar'){ inputConfig = file('testdata/r8-test-rules.pro') } -task runR8NoOptim(type: RunR8Task, dependsOn: 'jar'){ +task runR8NoOptim(type: RunR8Task, dependsOn: 'jar') { outputDex = unOptimizedDexDir inputConfig = file('testdata/r8-test-rules-no-optim.pro') } @@ -103,4 +103,4 @@ tasks.withType(dokka.getClass()) { url = new URL("https://developer.android.com/reference/") packageListUrl = projectDir.toPath().resolve("package.list").toUri().toURL() } -} \ No newline at end of file +} diff --git a/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt b/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt index 7f03378d00..47beb85bbf 100644 --- a/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt +++ b/ui/kotlinx-coroutines-android/test/R8ServiceLoaderOptimizationTest.kt @@ -16,7 +16,7 @@ class R8ServiceLoaderOptimizationTest : TestBase() { private val r8DexNoOptim = File(System.getProperty("noOptimDexPath")!!).asDexFile() @Test - fun noServiceLoaderCalls() { + fun testNoServiceLoaderCalls() { val serviceLoaderInvocations = r8Dex.types.any { it.type == "Ljava/util/ServiceLoader;" } @@ -28,7 +28,7 @@ class R8ServiceLoaderOptimizationTest : TestBase() { } @Test - fun androidDispatcherIsKept() { + fun testAndroidDispatcherIsKept() { val hasAndroidDispatcher = r8DexNoOptim.classes.any { it.type == "Lkotlinx/coroutines/android/AndroidDispatcherFactory;" } @@ -38,7 +38,7 @@ class R8ServiceLoaderOptimizationTest : TestBase() { @Test @Ignore - fun noOptimRulesMatch() { + fun testNoOptimRulesMatch() { val paths = listOf( "META-INF/com.android.tools/proguard/coroutines.pro", "META-INF/proguard/coroutines.pro",