Kotlin内联功能不能正常工作。

时间:2021-07-01 21:29:11

While working on a project with QtJambi as the GUI toolkit, I was trying to code a library to make connecting signals and slots easier. I found the following problem.

在使用QtJambi作为GUI工具包的项目时,我正在尝试编写一个库,以使连接信号和插槽变得更容易。我发现了以下问题。

Here is my code:

这是我的代码:

inline fun QSignalEmitter.Signal0.connect(
        inlineOptions(InlineOption.ONLY_LOCAL_RETURN) action: () -> Unit) {
    connect(object {
        fun execute() {
            action()
        }
    }, "execute()")
}

inline fun <reified A> QSignalEmitter.Signal1<A>.connect(
        inlineOptions(InlineOption.ONLY_LOCAL_RETURN) action: (A) -> Unit) {
    connect(object {
        fun execute(a: A) {
            action(a)
        }
    }, "execute(" + javaClass<A>().getCanonicalName() + ")")
}

This works for the first connect function when there are no generic parameters expected. But for the second connect function when i do something like:

当没有预期的泛型参数时,这适用于第一个连接函数。但对于第二个连接函数,当我做类似的事情时:

QCheckBox().toggled.connect({ print(it) })

I get the following error:

我得到了以下错误:

Exception in thread "main" com.trolltech.qt.QNoSuchSlotException: 

Could not find slot with signature: execute(java.lang.Boolean)
Possible matching methods:
   com.TestPackage$Test$f1214d02$main$$inlined$connect$1.execute(java.lang.Object)

    at com.trolltech.qt.QSignalEmitter$AbstractSignal.connect(QSignalEmitter.java:72)
    at com.trolltech.qt.QSignalEmitter$AbstractSignal.connect(QSignalEmitter.java:132)
    at com.TestPackage$Test$f1214d02.main(Test.kt:9)
    at com.TestPackage.main(Test.kt:1)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

What is the possible cause and how can I get around it?

可能的原因是什么?我该如何克服它?

Edit: Usage example

编辑:使用例子

inline fun <reified A> QSignalEmitter.Signal1<A>.connect(noinline action: (A) -> Unit) {
    connect(action, "invoke(${javaClass<A>()})")
}

fun main(args: Array<String>) {
    QApplication.initialize(args)

    val widget = QWidget()
    val button = QPushButton("Say Hello")
    button.clicked.connect {
        println("Hello World")
    }
    val layout = QFormLayout(widget)
    layout.addWidget(button)

    widget.show()
    QApplication.execStatic()
}

Kotlin Bytecode

芬兰湾的科特林字节码

// ================_DefaultPackage$QtExtensions$7d5c0ac6.class =================
// class version 50.0 (50)
// access flags 0x11
public final class _DefaultPackage$QtExtensions$7d5c0ac6 {


    // access flags 0x19
    // signature <A:Ljava/lang/Object;>(Lcom/trolltech/qt/QSignalEmitter$Signal1<TA;>;Lkotlin/jvm/functions/Function1<-TA;+Lkotlin/Unit;>;)V
    // declaration: void connect<A>(com.trolltech.qt.QSignalEmitter$Signal1<A>, kotlin.jvm.functions.Function1<? super A, ? extends kotlin.Unit>)
    public final static connect(Lcom/trolltech/qt/QSignalEmitter$Signal1;Lkotlin/jvm/functions/Function1;)V
    @Lkotlin/inline;() // invisible
    @Ljet/runtime/typeinfo/JetValueParameter;(name="$receiver") // parameter 0
    @Ljet/runtime/typeinfo/JetValueParameter;(name="action") // parameter 1
    @Lkotlin/noinline;() // invisible, parameter 1
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
    L0
    L1
    ALOAD 0
    LDC "$receiver"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    ALOAD 1
    LDC "action"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    L2
    LINENUMBER 9 L2
    ALOAD 0
    ALOAD 1
    NEW java/lang/StringBuilder
        DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
        LDC "invoke("
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC "A"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.reifyJavaClass (Ljava/lang/String;)V
    LDC Ljava/lang/Object;.class
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;
    LDC ")"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL com/trolltech/qt/QSignalEmitter$Signal1.connect (Ljava/lang/Object;Ljava/lang/String;)V
    L3
    LINENUMBER 10 L3
    RETURN
    L4
    LOCALVARIABLE $receiver Lcom/trolltech/qt/QSignalEmitter$Signal1; L0 L4 0
    LOCALVARIABLE action Lkotlin/jvm/functions/Function1; L0 L4 1
    MAXSTACK = 4
    MAXLOCALS = 2

    // access flags 0x19
    public final static main([Ljava/lang/String;)V
    @Ljet/runtime/typeinfo/JetValueParameter;(name="args") // parameter 0
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
    L0
    L1
    ALOAD 0
    LDC "args"
    INVOKESTATIC kotlin/jvm/internal/Intrinsics.checkParameterIsNotNull (Ljava/lang/Object;Ljava/lang/String;)V
    L2
    LINENUMBER 13 L2
    ALOAD 0
    INVOKESTATIC com/trolltech/qt/gui/QApplication.initialize ([Ljava/lang/String;)V
    L3
    LINENUMBER 15 L3
    NEW com/trolltech/qt/gui/QWidget
        DUP
    INVOKESPECIAL com/trolltech/qt/gui/QWidget.<init> ()V
        ASTORE 1
    L4
    L5
    LINENUMBER 16 L5
    NEW com/trolltech/qt/gui/QPushButton
        DUP
    LDC "Say Hello"
    INVOKESPECIAL com/trolltech/qt/gui/QPushButton.<init> (Ljava/lang/String;)V
    ASTORE 2
    L6
    L7
    LINENUMBER 17 L7
    ALOAD 2
    CHECKCAST com/trolltech/qt/gui/QAbstractButton
        GETFIELD com/trolltech/qt/gui/QAbstractButton.clicked : Lcom/trolltech/qt/QSignalEmitter$Signal1;
    ASTORE 3
    GETSTATIC _DefaultPackage$QtExtensions$7d5c0ac6$main$1.INSTANCE$ : L_DefaultPackage$QtExtensions$7d5c0ac6$main$1;
    CHECKCAST kotlin/jvm/functions/Function1
        ASTORE 4
    NOP
    L8
    L9
    L10
    LINENUMBER 9 L10
    ALOAD 3
    ALOAD 4
    NEW java/lang/StringBuilder
        DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
        LDC "invoke("
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    LDC Ljava/lang/Boolean;.class
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/Object;)Ljava/lang/StringBuilder;
    LDC ")"
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    INVOKEVIRTUAL com/trolltech/qt/QSignalEmitter$Signal1.connect (Ljava/lang/Object;Ljava/lang/String;)V
    L11
    LINENUMBER 10 L11
    L12
    L13
    L14
    LINENUMBER 20 L14
    NEW com/trolltech/qt/gui/QFormLayout
        DUP
    ALOAD 1
    INVOKESPECIAL com/trolltech/qt/gui/QFormLayout.<init> (Lcom/trolltech/qt/gui/QWidget;)V
    ASTORE 3
    L15
    L16
    LINENUMBER 21 L16
    ALOAD 3
    ALOAD 2
    CHECKCAST com/trolltech/qt/gui/QWidget
        INVOKEVIRTUAL com/trolltech/qt/gui/QFormLayout.addWidget (Lcom/trolltech/qt/gui/QWidget;)V
    L17
    LINENUMBER 23 L17
    ALOAD 1
    INVOKEVIRTUAL com/trolltech/qt/gui/QWidget.show ()V
        L18
    LINENUMBER 24 L18
    INVOKESTATIC com/trolltech/qt/gui/QApplication.execStatic ()I
        POP
    L19
    LINENUMBER 25 L19
    RETURN
    L20
    LOCALVARIABLE $receiver Lcom/trolltech/qt/QSignalEmitter$Signal1; L8 L12 3
    LOCALVARIABLE action Lkotlin/jvm/functions/Function1; L8 L12 4
    LOCALVARIABLE layout Lcom/trolltech/qt/gui/QFormLayout; L15 L20 3
    LOCALVARIABLE button Lcom/trolltech/qt/gui/QPushButton; L6 L20 2
    LOCALVARIABLE widget Lcom/trolltech/qt/gui/QWidget; L4 L20 1
    LOCALVARIABLE args [Ljava/lang/String; L0 L20 0
    MAXSTACK = 4
    MAXLOCALS = 5

    @Lkotlin/jvm/internal/KotlinSyntheticClass;(abiVersion=23, kind=Lkotlin/jvm/internal/KotlinSyntheticClass$Kind;.PACKAGE_PART)
    // access flags 0x19
    public final static INNERCLASS _DefaultPackage$QtExtensions$7d5c0ac6$main$1 null null
    // compiled from: QtExtensions.kt
    // debug info: SMAP
    QtExtensions.kt
    Kotlin
    *S Kotlin
    *F
        + 1 QtExtensions.kt
    _DefaultPackage
    *L
        1#1,25:1
    *E

}


// ================_DefaultPackage$QtExtensions$7d5c0ac6$main$1.class =================
// class version 50.0 (50)
// access flags 0x30
// signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function1<Ljava/lang/Boolean;Lkotlin/Unit;>;
// declaration: _DefaultPackage$QtExtensions$7d5c0ac6$main$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function1<java.lang.Boolean, kotlin.Unit>
final class _DefaultPackage$QtExtensions$7d5c0ac6$main$1 extends kotlin/jvm/internal/Lambda  implements kotlin/jvm/functions/Function1  {


    // access flags 0x41
    public bridge invoke(Ljava/lang/Object;)Ljava/lang/Object;
    ALOAD 0
    ALOAD 1
    CHECKCAST java/lang/Boolean
        INVOKEVIRTUAL _DefaultPackage$QtExtensions$7d5c0ac6$main$1.invoke (Ljava/lang/Boolean;)V
    GETSTATIC kotlin/Unit.INSTANCE$ : Lkotlin/Unit;
    ARETURN
    MAXSTACK = 2
    MAXLOCALS = 2

    // access flags 0x11
    public final invoke(Ljava/lang/Boolean;)V
    @Ljet/runtime/typeinfo/JetValueParameter;(name="it") // parameter 0
    L0
    L1
    L2
    LINENUMBER 18 L2
        LDC "Hello World"
    INVOKESTATIC kotlin/io/IoPackage.println (Ljava/lang/Object;)V
    L3
    RETURN
    L4
    LOCALVARIABLE this L_DefaultPackage$QtExtensions$7d5c0ac6$main$1; L0 L4 0
    LOCALVARIABLE it Ljava/lang/Boolean; L0 L4 1
    MAXSTACK = 1
    MAXLOCALS = 2

    // access flags 0x0
    <init>()V
        ALOAD 0
        ICONST_1
        INVOKESPECIAL kotlin/jvm/internal/Lambda.<init> (I)V
        RETURN
        MAXSTACK = 2
        MAXLOCALS = 1

        // access flags 0x1008
        static synthetic <clinit>()V
            NEW _DefaultPackage$QtExtensions$7d5c0ac6$main$1
            DUP
            INVOKESPECIAL _DefaultPackage$QtExtensions$7d5c0ac6$main$1.<init> ()V
                PUTSTATIC _DefaultPackage$QtExtensions$7d5c0ac6$main$1.INSTANCE$ : L_DefaultPackage$QtExtensions$7d5c0ac6$main$1;
                RETURN
                MAXSTACK = 2
                MAXLOCALS = 0

                // access flags 0x19
                public final static L_DefaultPackage$QtExtensions$7d5c0ac6$main$1; INSTANCE$

                @Lkotlin/jvm/internal/KotlinSyntheticClass;(abiVersion=23, kind=Lkotlin/jvm/internal/KotlinSyntheticClass$Kind;.ANONYMOUS_FUNCTION)
                OUTERCLASS _DefaultPackage$QtExtensions$7d5c0ac6 main ([Ljava/lang/String;)V
                // access flags 0x19
                public final static INNERCLASS _DefaultPackage$QtExtensions$7d5c0ac6$main$1 null null
                // compiled from: QtExtensions.kt
                }


                // ================_DefaultPackage.class =================
                // class version 50.0 (50)
                // access flags 0x11
                public final class _DefaultPackage {


                // access flags 0x8
                static <clinit>()V
                    LDC L_DefaultPackage;.class
                    INVOKESTATIC kotlin/jvm/internal/Reflection.createKotlinPackage (Ljava/lang/Class;)Lkotlin/reflect/KPackage;
                    PUTSTATIC _DefaultPackage.$kotlinPackage : Lkotlin/reflect/KPackage;
                    RETURN
                    MAXSTACK = 1
                    MAXLOCALS = 0

                    // access flags 0x1019
                    public final static synthetic Lkotlin/reflect/KPackage; $kotlinPackage

                    // access flags 0x19
                    public final static main([Ljava/lang/String;)V
                    @Ljet/runtime/typeinfo/JetValueParameter;(name="args") // parameter 0
                    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
                    L0
                    L1
                    LINENUMBER 1 L1
                    ALOAD 0
                    INVOKESTATIC _DefaultPackage$QtExtensions$7d5c0ac6.main ([Ljava/lang/String;)V
                    RETURN
                    L2
                    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
                    MAXSTACK = 1
                    MAXLOCALS = 1

                    // access flags 0x19
                    // signature <A:Ljava/lang/Object;>(Lcom/trolltech/qt/QSignalEmitter$Signal1<TA;>;Lkotlin/jvm/functions/Function1<-TA;+Lkotlin/Unit;>;)V
                        // declaration: void connect<A>(com.trolltech.qt.QSignalEmitter$Signal1<A>, kotlin.jvm.functions.Function1<? super A, ? extends kotlin.Unit>)
                            public final static connect(Lcom/trolltech/qt/QSignalEmitter$Signal1;Lkotlin/jvm/functions/Function1;)V
                            @Lkotlin/inline;() // invisible
                            @Ljet/runtime/typeinfo/JetValueParameter;(name="$receiver") // parameter 0
                            @Ljet/runtime/typeinfo/JetValueParameter;(name="action") // parameter 1
                            @Lkotlin/noinline;() // invisible, parameter 1
                            @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 1
                            L0
                            L1
                            LINENUMBER 1 L1
                            ALOAD 0
                            ALOAD 1
                            INVOKESTATIC _DefaultPackage$QtExtensions$7d5c0ac6.connect (Lcom/trolltech/qt/QSignalEmitter$Signal1;Lkotlin/jvm/functions/Function1;)V
                            RETURN
                            L2
                            LOCALVARIABLE $receiver Lcom/trolltech/qt/QSignalEmitter$Signal1; L0 L2 0
                            LOCALVARIABLE action Lkotlin/jvm/functions/Function1; L0 L2 1
                            MAXSTACK = 2
                            MAXLOCALS = 2

                            @Lkotlin/jvm/internal/KotlinPackage;(abiVersion=23, data={"V\u0004)!Q.Y5o\u0015\u0011\u0009'oZ:\u000b\u000b\u0005\u0013(/Y=\u000b\r-|G\u000f\\5o\u0015\u0019\u0019FO]5oO*!QK\\5u\u0015\u0011Q\u0017M^1\u000b\u00091\u000cgn\u001a\u0006&?\u0012+g-Y;miB\u000b7m[1hK\u0012\nF/\u0012=uK:\u001c\u0018n\u001c8tI]\"Wg\u0019\u0019bGZR\u0011!\u0011\u0006\u0004\u0003:L(bB*jO:\u000cG.\r\u0006\u000f#NKwM\\1m\u000b6LG\u000f^3s\u0015\r\u0019w.\u001c\u0006\niJ|G\u000e\u001c;fG\"T!!\u001d;\u000b\u000f\r|gN\\3di*1\u0011m\u0019;j_:T\u0011BR;oGRLwN\\\u0019\u000b-E\u001b\u0016n\u001a8bY\u0016k\u0017\u000e\u001e;fe\u0012\u001a\u0016n\u001a8bYFR1A\u001b<n\u0015%1WO\\2uS>t7\u000f\u001e\u0006\u0003!\rQa\u0001\u0003\u0001\u0011\u0005a\u0001!B\u0002\u0005\u0001!\u0011A\u0002A\u0003\u0004\u0009\u0001A)\u0001\u0004\u0001\u0006\u0003!\u0019QA\u0001\u0003\u0003\u0011\u000f)!\u0001\"\u0002\u0009\u0005\u0015\u0019A\u0001\u0001\u0005\u0006\u0019\u0001)\u0011\u0001#\u0004\u0006\u0005\u0011!\u0001bB\u0003\u0003\u0009\u0013Ay!B\u0002\u0005\u000b!1A\u0002A\u0003\u0004\u0009\u0017AY\u0001\u0004\u0001\u0006\u0007\u0011\u0001\u0001\"\u0003\u0007\u0001\u000b\u0009!Q\u0001c\u0005\u0006\u0005\u0011\u0001\u0001BC\u0003\u0003\u0009\u001fA)\"\u0002\u0002\u0005\u0011!IQ1\u0007\u0003\u00011\u0001i*\u0002\u0002\u0001\u0009\u000251Q!\u0001E\u0001\u0013\rI!!B\u0001\u0009\u0003A\u001b\u0001!\u0009\u0002\u0006\u0003!\r\u0011k\u0001\u0004\u0005\u0001%\u0009A\u0001A\u0007\u0003\u0011\ra\u0009\u0001W\u0002\u0005\u000b?\"\u0009!E\u0004\u0005\u0001!%A\u0012A\u000b\u0004\u000b\u0005A9\u0001$\u0001\u0016\u000f\u0015\u0009\u0001BB\u0005\u0005\u0013\r)!\u0001\"\u0001\u0009\u0001aAQt\u0004C\u0001\u0011#i1\"B\u0001\u0009\u000e%!\u0011bA\u0003\u0003\u0009\u0003A\u0001!C\u0002\n\u0005\u0015\u0009\u00012\u0001)\u0004\u0002\u0005\u0012Q!\u0001E\u0002#\u000e9A\u0001C\u0005\u0002\u0009\u0001i\u0011\u0001C\u0004\u000e\u0003!E\u0001l\u0001\u0003"})
                            // compiled from: QtExtensions.kt
                            }

1 个解决方案

#1


1  

Classes inside inline function not specialize so execute of your object in bytecode has next signature execute(java.lang.Object). But javaClass<A>() specialize and in connect you provide another signature: execute(java.lang.Boolean).

内联函数内的类不是专门化的,所以在字节码中执行对象是下一个签名执行(java.lang.Object)。但是javaClass()专门化,并在连接中提供另一个签名:execute(java.lang.Boolean)。

Try to write:

试着写:

fun <A> QSignalEmitter.Signal1<A>.connect(action: (A) -> Unit) {
    connect(action, "invoke(java.lang.Object)")
}

solution 2:

解决方案2:

inline fun <reified A> S<A>.connect(noinline action: (A) -> Unit) {
    connect(action, "invoke(${javaClass<A>()})")
}

#1


1  

Classes inside inline function not specialize so execute of your object in bytecode has next signature execute(java.lang.Object). But javaClass<A>() specialize and in connect you provide another signature: execute(java.lang.Boolean).

内联函数内的类不是专门化的,所以在字节码中执行对象是下一个签名执行(java.lang.Object)。但是javaClass()专门化,并在连接中提供另一个签名:execute(java.lang.Boolean)。

Try to write:

试着写:

fun <A> QSignalEmitter.Signal1<A>.connect(action: (A) -> Unit) {
    connect(action, "invoke(java.lang.Object)")
}

solution 2:

解决方案2:

inline fun <reified A> S<A>.connect(noinline action: (A) -> Unit) {
    connect(action, "invoke(${javaClass<A>()})")
}