And*_*lli 6 bytecode suspend kotlin kotlin-coroutines
给定两个函数,foo()
and foo()
,第一个是标准的,第二个是可暂停的
fun foo(x: Int): Int {
return 2*x
}
suspend fun foo(x: Int): Int {
return 4*x
}
Run Code Online (Sandbox Code Playgroud)
以下代码无法编译,因为具有相同签名的两个函数存在冲突。
冲突重载:public fun foo(x: Int): Int 在文件 t.kt 中定义,public suspend fun foo(x: Int): Int 在文件 t.kt 中定义
如果我对挂起函数的理解是正确的,那么:
Continuation
参数被添加到挂起函数中,状态机使用它来停止和启动挂起代码Any
(因此Object
对于 java)这两个副作用理论上应该足以改变第二个foo()
函数签名,因此以不同于第一个的方式看待挂起标记的函数。
起初,我认为函数签名检查可以在实际将代码编译成字节码之前进行。然而,将两个呈现的函数变成实际的字节码实际上会导致 2 个具有 2 个不同签名的方法。
@Metadata(
mv = {1, 1, 16},
bv = {1, 0, 3},
k = 2,
d1 = {"\u0000\n\n\u0000\n\u0002\u0010\b\n\u0002\b\u0003\u001a\u000e\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001\u001a\u0019\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0003\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u0004"},
d2 = {"foo", "", "x", "(ILkotlin/coroutines/Continuation;)Ljava/lang/Object;", "app"}
)
public final class TKt {
public static final int foo(int x) {
return 2 * x;
}
@Nullable
public static final Object foo(int x, @NotNull Continuation $completion) {
return Boxing.boxInt(4 * x);
}
}
Run Code Online (Sandbox Code Playgroud)
// access flags 0x19
public final static foo(I)I
// annotable parameter count: 1 (visible)
// annotable parameter count: 1 (invisible)
L0
LINENUMBER 4 L0
ICONST_2
ILOAD 0
IMUL
IRETURN
L1
LOCALVARIABLE x I L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1
Run Code Online (Sandbox Code Playgroud)
// access flags 0x19
// signature (ILkotlin/coroutines/Continuation<-Ljava/lang/Integer;>;)Ljava/lang/Object;
// declaration: foo(int, kotlin.coroutines.Continuation<? super java.lang.Integer>)
public final static foo(ILkotlin/coroutines/Continuation;)Ljava/lang/Object; @Lorg/jetbrains/annotations/Nullable;() // invisible
// annotable parameter count: 2 (visible)
// annotable parameter count: 2 (invisible)
@Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
L0
LINENUMBER 8 L0
ICONST_4
ILOAD 0
IMUL
INVOKESTATIC kotlin/coroutines/jvm/internal/Boxing.boxInt (I)Ljava/lang/Integer;
ARETURN
L1
LOCALVARIABLE x I L0 L1 0
LOCALVARIABLE $completion Lkotlin/coroutines/Continuation; L0 L1 1
MAXSTACK = 2
MAXLOCALS = 2
Run Code Online (Sandbox Code Playgroud)
在这一点上,我认为 kotlin 并不总是可以决定调用哪个函数。当然,这两个函数是完全不同且独立的,它们的签名甚至没有部分匹配(不同的返回类型和参数)
重点是在kotlin这个词中,挂起函数只能在协程作用域内部调用,而普通函数在两个地方都可以调用。下表可以作为一个很好的例子,以图形方式分析情况。
+---------------+---------------+-----------------+
| | Default Scope | Coroutine Scope |
+---------------+---------------+-----------------+
| foo() | ? | ? |
+---------------+---------------+-----------------+
| suspend foo() | ? | ? |
+---------------+---------------+-----------------+
Run Code Online (Sandbox Code Playgroud)
可能涉及这两个实体之间的定义冲突的唯一场景如下。
fun foo(x: Int): Int {
return 2*x
}
suspend fun foo(x: Int): Int {
return 4*x
}
GlobalScope.launch {
println(foo(7))
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,如果没有一个假设的(又名 Existing only in my head)运算符让 Kotlin 知道要调用哪个函数,如果是可挂起的函数或标准函数,您就无法确定正在调用哪个函数。
这个分析是正确的还是我错过了介于两者之间的东西?
此问题将在具有类似内容的 YouTrack 问题中链接,这可能是编译器改进的起点(也许区分重载错误与标准函数错误的可挂起冲突),或者对于新的 Kotlin 功能,扩展可挂起函数与普通函数的互操作性(我想象一种类似扩展的运算符,它以函数调用为前缀,并且运算符的存在将一个调用与另一个调用区分开来)。
您在字节码方面是对的 - 签名是不同的。
然而,它无法从 Kotlin 语言方面明确地确定功能。比如下面应该调用什么方法?
fun main() {
runBlocking {
println(foo(1)) // which one should be called here?
}
}
fun foo(x: Int): Int {
return 2*x
}
suspend fun foo(x: Int): Int {
return 4*x
}
Run Code Online (Sandbox Code Playgroud)
Java Synthetic 方法也有相同的行为。请检查此答案- 您可以在字节码中定义两个方法,但是在 Java 语言语法中是不允许的。
归档时间: |
|
查看次数: |
844 次 |
最近记录: |