Skip to content

Commit 95639d5

Browse files
grechkovladSpace Team
authored andcommitted
[JVM] Fix detection of $DefaultImpl bridges
Namely, we should: 1. Preserve `originalFunctionForDefaultImpl` attribute in `AddContinuationLowering` 2. Take into account that call in the bridge body might be rerouted to the synthetic accessor. Otherwise, we may fail to detect $DefaultImpl bridge. In this case, we generate excess state machine for the bridge and may end up with incorrect bytecode. ^KT-79442: Fixed (cherry picked from commit a5c4b54)
1 parent 09a5926 commit 95639d5

File tree

13 files changed

+205
-1
lines changed

13 files changed

+205
-1
lines changed

analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLBlackBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/LLReversedBlackBoxTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,13 @@ internal class AddContinuationLowering(context: JvmBackendContext) : SuspendLowe
294294

295295
val capturesCrossinline = function.isCapturingCrossinline()
296296
val view = function.suspendFunctionViewOrStub(context)
297+
298+
// if X is original for Y, then (view for X) is an original for the (view for Y)
299+
function.originalFunctionForDefaultImpl?.let { originalForDefaultImplBridge ->
300+
val viewForOriginal = originalForDefaultImplBridge.suspendFunctionOriginal().viewOfOriginalSuspendFunction
301+
if (viewForOriginal != null) view.originalFunctionForDefaultImpl = viewForOriginal
302+
}
303+
297304
val continuationParameter = view.continuationParameter()
298305
val parameterMap = function.parameters.zip(view.parameters.filter { it != continuationParameter }).toMap()
299306
view.body = function.moveBodyTo(view, parameterMap)

compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrCoroutineUtils.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private fun IrFunction.isDefaultImplsBridgeInJvmDefaultEnableMode(): Boolean =
9191
(parent as? IrClass)?.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS &&
9292
// (Note that we could've checked this directly via `context.config.jvmDefaultMode`, but that would require passing `context`
9393
// to utilities in this file, and all their call sites.)
94-
((body as? IrExpressionBody)?.expression as? IrCall)?.symbol?.owner == originalFunctionForDefaultImpl
94+
isBodyBridgeCallTo(originalFunctionForDefaultImpl)
9595

9696
fun IrFunction.shouldContainSuspendMarkers(): Boolean = !isNonBoxingSuspendDelegation() &&
9797
// These functions also contain a single `suspend` tail call, but if it returns an unboxed inline class value,

compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,20 @@ fun IrClass.findEnumValuesFunction(context: JvmBackendContext): IrSimpleFunction
529529
val IrValueParameter.isSkippedInGenericSignature: Boolean
530530
get() = origin == JvmLoweredDeclarationOrigin.FIELD_FOR_OUTER_THIS ||
531531
origin == JvmLoweredDeclarationOrigin.ENUM_CONSTRUCTOR_SYNTHETIC_PARAMETER
532+
533+
private fun IrFunction.singleCallOrNull(): IrCall? = when (body) {
534+
is IrBlockBody -> {
535+
val statements = (body as IrBlockBody).statements
536+
(statements.singleOrNull() as? IrReturn)?.value as? IrCall
537+
}
538+
is IrExpressionBody -> {
539+
val expression = (body as IrExpressionBody).expression
540+
expression as? IrCall
541+
}
542+
else -> null
543+
}
544+
545+
fun IrFunction.isBodyBridgeCallTo(target: IrSimpleFunction?): Boolean {
546+
val callee: IrSimpleFunction = singleCallOrNull()?.symbol?.owner ?: return false
547+
return callee == target || (callee.origin == IrDeclarationOrigin.SYNTHETIC_ACCESSOR && callee.isBodyBridgeCallTo(target))
548+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Corresponds to KT-79442
2+
3+
// TARGET_BACKEND: JVM
4+
5+
// WITH_STDLIB
6+
// WITH_COROUTINES
7+
// JVM_DEFAULT_MODE: enable
8+
9+
// CHECK_BYTECODE_LISTING
10+
// CHECK_BYTECODE_TEXT
11+
12+
import helpers.*
13+
import kotlin.coroutines.*
14+
15+
interface Repository {
16+
suspend fun get(): Result<String>
17+
suspend fun local(): Result<String> = get()
18+
suspend fun remote(): Result<String> = get()
19+
20+
}
21+
22+
fun box(): String {
23+
24+
var res = "Fail 1"
25+
26+
val x = object : Repository {
27+
override suspend fun get(): Result<String> {
28+
return Result.success("OK")
29+
}
30+
}
31+
32+
suspend {
33+
res = x.local().getOrDefault("Fail 2")
34+
}.startCoroutine(EmptyContinuation)
35+
36+
return res
37+
}
38+
39+
// @Repository$DefaultImpls.class:
40+
// 0 @Lkotlin/coroutines/jvm/internal/DebugMetadata;
41+
// 0 L$0
42+
// 2 ALOAD 1
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
@kotlin.coroutines.jvm.internal.DebugMetadata
2+
@kotlin.Metadata
3+
final class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$1 {
4+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
5+
enclosing method InterfaceWithTwoDefaultFunsReturnValueTypeKt.box()Ljava/lang/String;
6+
synthetic final field $res: kotlin.jvm.internal.Ref$ObjectRef
7+
synthetic final field $x: InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1
8+
field L$0: java.lang.Object
9+
field label: int
10+
inner (anonymous) class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$1
11+
inner (anonymous) class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1
12+
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1, p2: kotlin.coroutines.Continuation): void
13+
public final method create(p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
14+
public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object
15+
public final method invoke(p0: kotlin.coroutines.Continuation): java.lang.Object
16+
public final method invokeSuspend(p0: java.lang.Object): java.lang.Object
17+
public final inner class kotlin/jvm/internal/Ref$ObjectRef
18+
}
19+
20+
@kotlin.Metadata
21+
public final class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1 {
22+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
23+
enclosing method InterfaceWithTwoDefaultFunsReturnValueTypeKt.box()Ljava/lang/String;
24+
inner (anonymous) class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1
25+
method <init>(): void
26+
public method get-IoAF18A(p0: kotlin.coroutines.Continuation): java.lang.Object
27+
public method local-IoAF18A(p0: kotlin.coroutines.Continuation): java.lang.Object
28+
public method remote-IoAF18A(p0: kotlin.coroutines.Continuation): java.lang.Object
29+
public final inner class kotlin/Result$Companion
30+
}
31+
32+
@kotlin.Metadata
33+
public final class InterfaceWithTwoDefaultFunsReturnValueTypeKt {
34+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
35+
inner (anonymous) class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$1
36+
inner (anonymous) class InterfaceWithTwoDefaultFunsReturnValueTypeKt$box$x$1
37+
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
38+
public final inner class kotlin/jvm/internal/Ref$ObjectRef
39+
}
40+
41+
@kotlin.Metadata
42+
public final class Repository$DefaultImpls {
43+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
44+
public deprecated static @java.lang.Deprecated @org.jetbrains.annotations.Nullable method local-IoAF18A(@org.jetbrains.annotations.NotNull p0: Repository, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
45+
public deprecated static @java.lang.Deprecated @org.jetbrains.annotations.Nullable method remote-IoAF18A(@org.jetbrains.annotations.NotNull p0: Repository, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
46+
public final inner class Repository$DefaultImpls
47+
}
48+
49+
@kotlin.coroutines.jvm.internal.DebugMetadata
50+
@kotlin.Metadata
51+
final class Repository$local$1 {
52+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
53+
enclosing method Repository.local-IoAF18A$suspendImpl(LRepository;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
54+
field L$0: java.lang.Object
55+
field label: int
56+
synthetic field result: java.lang.Object
57+
synthetic final field this$0: Repository
58+
inner (anonymous) class Repository$local$1
59+
method <init>(p0: Repository, p1: kotlin.coroutines.Continuation): void
60+
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
61+
}
62+
63+
@kotlin.coroutines.jvm.internal.DebugMetadata
64+
@kotlin.Metadata
65+
final class Repository$remote$1 {
66+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
67+
enclosing method Repository.remote-IoAF18A$suspendImpl(LRepository;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
68+
field L$0: java.lang.Object
69+
field label: int
70+
synthetic field result: java.lang.Object
71+
synthetic final field this$0: Repository
72+
inner (anonymous) class Repository$remote$1
73+
method <init>(p0: Repository, p1: kotlin.coroutines.Continuation): void
74+
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
75+
}
76+
77+
@kotlin.Metadata
78+
public interface Repository {
79+
// source: 'interfaceWithTwoDefaultFunsReturnValueType.kt'
80+
inner (anonymous) class Repository$local$1
81+
inner (anonymous) class Repository$remote$1
82+
public synthetic static method access$local-IoAF18A$jd(p0: Repository, p1: kotlin.coroutines.Continuation): java.lang.Object
83+
public synthetic static method access$remote-IoAF18A$jd(p0: Repository, p1: kotlin.coroutines.Continuation): java.lang.Object
84+
public abstract @org.jetbrains.annotations.Nullable method get-IoAF18A(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
85+
public synthetic static method local-IoAF18A$suspendImpl(p0: Repository, p1: kotlin.coroutines.Continuation): java.lang.Object
86+
public @org.jetbrains.annotations.Nullable method local-IoAF18A(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
87+
public synthetic static method remote-IoAF18A$suspendImpl(p0: Repository, p1: kotlin.coroutines.Continuation): java.lang.Object
88+
public @org.jetbrains.annotations.Nullable method remote-IoAF18A(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
89+
public final inner class Repository$DefaultImpls
90+
}

compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/JvmAbiConsistencyTestBoxGenerated.java

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)