Wie kann man unnötige Nullprüfungen abdecken, die von Kotlin generiert wurden?

9

Betrachten Sie das folgende minimale Kotlin-Beispiel:

fun <U> someWrapper(supplier: () -> U): () -> (U) {
    return { supplier() }
}

fun foo(taskExecutor: TaskExecutor): Int {
    val future = CompletableFuture.supplyAsync(someWrapper {
        42
    }, taskExecutor::execute)
    return future.join()
}

@Test
public void shouldFoo() {
    assertThat(foo(), is(42));
}

Ich habe in Jacoco Regeln für die Zweigstellenabdeckung, die für den obigen Code fehlschlagen und besagen, dass 1 von 2 Zweigstellen in der Anrufleitung nicht abgedeckt ist someWrapper. Leider ist es für mich nicht möglich, alle someWrapperaufgerufenen Klassen auszuschließen .

Betrachten Sie den dekompilierten Java-Code:

public final int foo(TaskExecutor taskExecutor) {
    Object var10000 = WrappersKt.someWrapper((Function0)null.INSTANCE);
    if (var10000 != null) {
        Object var2 = var10000;
        var10000 = new Foo$sam$java_util_function_Supplier$0((Function0)var2);
    }

    Supplier var3 = (Supplier)var10000;
    Function1 var4 = (Function1)(new Function1(this.taskExecutor) {
        // $FF: synthetic method
        // $FF: bridge method
        public Object invoke(Object var1) {
        this.invoke((Runnable)var1);
        return Unit.INSTANCE;
        }

        public final void invoke(Runnable p1) {
        ((TaskExecutor)this.receiver).execute(p1);
        }

        public final KDeclarationContainer getOwner() {
        return Reflection.getOrCreateKotlinClass(TaskExecutor.class);
        }

        public final String getName() {
        return "execute";
        }

        public final String getSignature() {
        return "execute(Ljava/lang/Runnable;)V";
        }
    });
    CompletableFuture future = CompletableFuture.supplyAsync(var3, (Executor)(new Foo$sam$java_util_concurrent_Executor$0(var4)));
    var10000 = future.join();
    Intrinsics.checkExpressionValueIsNotNull(var10000, "future.join()");
    return ((Number)var10000).intValue();
}

Ich denke, das Problem ist der if (var10000 != null)Zweig, der sogar von der IDE als unnötig markiert wird (immer wahr).

Ist es irgendwie möglich, den Code so anzupassen, dass alle Zweige abgedeckt werden können, z. indem Sie sicherstellen, dass der Compiler diese zusätzliche Nullprüfung nicht generiert? Ich kann den Code von beiden ändern foo(..)und someWrapper(..)solange ich ein dekoriertes Lambda liefern kann.

Ich benutze Kotlin 1.3.50 und Jacoco 0.8.4.

BEARBEITEN.

Eine offensichtliche Problemumgehung besteht darin, supplyAsync(someWrapper { ... })in eine Utils-Klasse zu extrahieren und nur diese Klasse auszuschließen, dh:

fun <U> supplyAsync(supplier: () -> U, executor: TaskExecutor): CompletableFuture<U> {
    return CompletableFuture.supplyAsync(someWrapper { supplier() }, executor::execute)
}

Das wäre gut genug für mich, obwohl ich immer noch neugierig bin, warum der Zweig von Kotlin hinzugefügt wird, wo kein Zweig sein muss.

BKE
quelle
Ich bekomme, Type inference failedwenn ich versuche, Ihren Beispielcode kompilieren zu lassen. Wäre großartig, wenn Sie Beispielcode bereitstellen könnten, der sofort funktioniert! Zum Beispiel taskExecutorund controllersind Unbekannte.
Enselic
@Enselic hat eine kleine Bearbeitung hinzugefügt, um störende Fehler zu entfernen. Ich werde es nicht weiter auf vollwertigen Code ausweiten, da dies ausreichen sollte, um die Idee zu vermitteln.
BKE
1
Wenn ich mir anschaue, wie sich JaCoCo schrittweise an die Unterstützung von Kotlin anpasst (siehe github.com/jacoco/jacoco/releases und nach "vom Kotlin-Compiler hinzugefügt" suchen), denke ich, dass dies nur eine weitere Lücke ist, die früher oder später behoben wird. Wenn Sie es ernst meinen, Ihre Berichterstattung zu übertreffen, schlage ich vor, ein Problem zu melden.
PiotrK

Antworten:

1

Wenn der Rückgabewert von someWrappernur als Instanz von verwendet werden soll Supplier, können Sie die unnötige Nullprüfung entfernen, indem Sie sie explizit Supplierals Rückgabetyp verwenden.

fun <U> someWrapper(supplier: () -> U): Supplier<U> {
    return Supplier { supplier() }
}
Leo Aso
quelle