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>()})")
}