Parcourir la source

Support Nothing type for EventHandler; Add Java tests

Him188 il y a 5 ans
Parent
commit
9a57f35890

+ 3 - 1
mirai-core/build.gradle.kts

@@ -34,7 +34,9 @@ kotlin {
         )
     }
 
-    jvm()
+    jvm() {
+        withJava()
+    }
 
     sourceSets {
         all {

+ 44 - 6
mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/event/JvmMethodListeners.kt

@@ -7,6 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:JvmMultifileClass
 @file:JvmName("Events")
 @file:Suppress("unused", "INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "NOTHING_TO_INLINE")
 
@@ -37,18 +38,26 @@ import kotlin.reflect.jvm.kotlinFunction
  *
  * 支持的函数类型:
  * ```
+ * // 所有函数参数, 函数返回值都不允许标记为可空 (带有 '?' 符号)
+ * // T 表示任何 Event 类型.
  * suspend fun T.onEvent(T)
  * suspend fun T.onEvent(T): ListeningStatus
+ * suspend fun T.onEvent(T): Nothing
  * suspend fun onEvent(T)
  * suspend fun onEvent(T): ListeningStatus
+ * suspend fun onEvent(T): Nothing
  * suspend fun T.onEvent()
  * suspend fun T.onEvent(): ListeningStatus
+ * suspend fun T.onEvent(): Nothing
  * fun T.onEvent(T)
  * fun T.onEvent(T): ListeningStatus
+ * fun T.onEvent(T): Nothing
  * fun onEvent(T)
  * fun onEvent(T): ListeningStatus
+ * fun onEvent(T): Nothing
  * fun T.onEvent()
  * fun T.onEvent(): ListeningStatus
+ * fun T.onEvent(): Nothing
  * ```
  *
  * Kotlin 使用示例:
@@ -57,6 +66,8 @@ import kotlin.reflect.jvm.kotlinFunction
  * object MyEvents : ListenerHost {
  *     override val coroutineContext = SupervisorJob()
  *
+ *
+ *     // 可以抛出任何异常, 将在 this.coroutineContext 或 registerEvents 时提供的 CoroutineScope.coroutineContext 中的 CoroutineExceptionHandler 处理.
  *     @EventHandler
  *     suspend fun MessageEvent.onMessage() {
  *         reply("received")
@@ -76,8 +87,17 @@ import kotlin.reflect.jvm.kotlinFunction
  *     }
  *
  *     @EventHandler
- *     suspend fun MessageEvent.onMessage() {
+ *     suspend fun MessageEvent.onMessage() { // 可以抛出任何异常, 将在 handleException 处理
  *         reply("received")
+ *         // 无返回值 (或者返回 Unit), 表示一直监听事件.
+ *     }
+ *
+ *     @EventHandler
+ *     suspend fun MessageEvent.onMessage(): ListeningStatus { // 可以抛出任何异常, 将在 handleException 处理
+ *         reply("received")
+ *
+ *         return ListeningStatus.LISTENING // 表示继续监听事件
+ *         // return ListeningStatus.STOPPED // 表示停止监听事件
  *     }
  * }
  *
@@ -90,8 +110,10 @@ import kotlin.reflect.jvm.kotlinFunction
  *
  * 支持的方法类型
  * ```
+ * // T 表示任何 Event 类型.
  * void onEvent(T)
- * ListeningStatus onEvent(T)
+ * Void onEvent(T)
+ * @NotNull ListeningStatus onEvent(T) // 返回 null 时将抛出异常
  * ```
  *
  *
@@ -99,13 +121,23 @@ import kotlin.reflect.jvm.kotlinFunction
  * ```
  * public class MyEventHandlers extends SimpleListenerHost {
  *     @Override
- *     public void handleException(CoroutineContext context, Throwable exception){
+ *     public void handleException(@NotNull CoroutineContext context, @NotNull Throwable exception){
  *         // 处理事件处理时抛出的异常
  *     }
  *
  *     @EventHandler
- *     public void onMessage(MessageEvent event) throws Exception {
- *         event.subject.sendMessage("received")
+ *     public void onMessage(@NotNull MessageEvent event) throws Exception { // 可以抛出任何异常, 将在 handleException 处理
+ *         event.subject.sendMessage("received");
+ *         // 无返回值, 表示一直监听事件.
+ *     }
+ *
+ *     @NotNull
+ *     @EventHandler
+ *     public ListeningStatus onMessage(@NotNull MessageEvent event) throws Exception { // 可以抛出任何异常, 将在 handleException 处理
+ *         event.subject.sendMessage("received");
+ *
+ *         return ListeningStatus.LISTENING; // 表示继续监听事件
+ *         // return ListeningStatus.STOPPED; // 表示停止监听事件
  *     }
  * }
  *
@@ -260,6 +292,12 @@ private fun Method.registerEvent(
                 return ListeningStatus.STOPPED
             }
         }
+        require(!kotlinFunction.returnType.isMarkedNullable) {
+            "Kotlin event handlers cannot have nullable return type."
+        }
+        require(kotlinFunction.parameters.any { it.type.isMarkedNullable }) {
+            "Kotlin event handlers cannot have nullable parameter type."
+        }
         when (kotlinFunction.returnType.classifier) {
             Unit::class, Nothing::class -> {
                 scope.subscribeAlways(
@@ -299,7 +337,7 @@ private fun Method.registerEvent(
             "Illegal method parameter. Required one exact Event subclass. found $paramType"
         }
         when (this.returnType) {
-            Void::class.java, Void.TYPE -> {
+            Void::class.java, Void.TYPE, Nothing::class.java -> {
                 scope.subscribeAlways(
                     paramType.kotlin as KClass<out Event>,
                     priority = annotation.priority,

+ 61 - 0
mirai-core/src/jvmTest/java/net/mamoe/mirai/event/JvmMethodEventsTestJava.java

@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package net.mamoe.mirai.event;
+
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static kotlin.test.AssertionsKt.assertEquals;
+
+public class JvmMethodEventsTestJava extends SimpleListenerHost {
+    private final AtomicInteger called = new AtomicInteger(0);
+
+    @EventHandler
+    public void ev(TestEvent event) {
+        called.incrementAndGet();
+    }
+
+    @EventHandler
+    public Void ev2(TestEvent event) {
+        called.incrementAndGet();
+        return null;
+    }
+
+    @EventHandler
+    public ListeningStatus ev3(TestEvent event) {
+        called.incrementAndGet();
+        return ListeningStatus.LISTENING;
+    }
+
+    @EventHandler
+    public void ev(TestEvent event, TestEvent event2) {
+        called.incrementAndGet();
+    }
+
+    @EventHandler
+    public Void ev2(TestEvent event, TestEvent event2) {
+        called.incrementAndGet();
+        return null;
+    }
+
+    @EventHandler
+    public ListeningStatus ev3(TestEvent event, TestEvent event2) {
+        called.incrementAndGet();
+        return ListeningStatus.LISTENING;
+    }
+
+    @Test
+    public void test() {
+        Events.registerEvents(this);
+        EventKt.broadcast(new TestEvent());
+        assertEquals(6, called.get(), null);
+    }
+}

+ 8 - 1
mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/event/JvmMethodEventsTest.kt

@@ -46,6 +46,13 @@ internal class JvmMethodEventsTest {
                 called.getAndIncrement()
             }
 
+            @Suppress("unused")
+            @EventHandler
+            suspend fun `suspend param Void`(event: TestEvent): Void? {
+                called.getAndIncrement()
+                return null
+            }
+
             @EventHandler
             @Suppress("unused")
             fun TestEvent.`receiver param Unit`(event: TestEvent) {
@@ -88,7 +95,7 @@ internal class JvmMethodEventsTest {
                 TestEvent().broadcast()
             }
 
-            assertEquals(8, this.getCalled())
+            assertEquals(9, this.getCalled())
         }
     }