From 21e8852e0fb04d66f313881dff7e10278037735e Mon Sep 17 00:00:00 2001 From: AdoptOSS <adoptoss@outlook.com> Date: Mon, 5 Apr 2021 22:35:38 +0800 Subject: [PATCH 1/3] buildscript: fix task not found errors when android sdk not available (#1161) * buildscript: fix task not found errors when android sdk not available * fix: this shadowed by run * cleanup: code style * improve: avoid breaking index map --- .../kotlin/analyzes/CompiledCodeVerify.kt | 16 ++++++++++---- mirai-core-api/build.gradle.kts | 22 ++++++++++--------- mirai-core-utils/build.gradle.kts | 22 ++++++++++--------- mirai-core/build.gradle.kts | 22 ++++++++++--------- 4 files changed, 48 insertions(+), 34 deletions(-) diff --git a/buildSrc/src/main/kotlin/analyzes/CompiledCodeVerify.kt b/buildSrc/src/main/kotlin/analyzes/CompiledCodeVerify.kt index ed9eb770a..b40d14f4c 100644 --- a/buildSrc/src/main/kotlin/analyzes/CompiledCodeVerify.kt +++ b/buildSrc/src/main/kotlin/analyzes/CompiledCodeVerify.kt @@ -42,12 +42,14 @@ object CompiledCodeVerify { sequenceOf("kotlin/main") }.map { sequenceOf(project.buildDir.resolve("classes").resolve(it)) } - private fun getLibraries(project: Project, info: ProjectInfo): Sequence<Sequence<File>> = + private fun getLibraries(project: Project, info: ProjectInfo): Sequence<Sequence<File>?> = if (info.isMpp) { sequenceOf("jvmCompileClasspath", "androidCompileClasspath") } else { sequenceOf("compileClasspath") - }.map { project.configurations.getByName(it).files.asSequence() } + }.map { + project.configurations.findByName(it)?.files?.asSequence() + } fun Project.registerVerifyTask(taskName: String, action: VerifyAction) { @@ -55,12 +57,18 @@ object CompiledCodeVerify { tasks.register(taskName) { group = VERIFICATION_GROUP_NAME - mustRunAfter(*projectInfo.compileTasks) + projectInfo.compileTasks.forEach { + tasks.findByPath(it)?.also { compileTask -> + mustRunAfter(compileTask) + } + } doFirst { getCompiledClassesPath(project, projectInfo).zip(getLibraries(project, projectInfo)) .forEach { (compiledClasses, libraries) -> - action(compiledClasses, libraries) + if (libraries != null) { + action(compiledClasses, libraries) + } } } } diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index 5959feda1..80b5447bc 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -101,18 +101,20 @@ kotlin { } } -tasks.register("checkAndroidApiLevel") { - doFirst { - analyzes.AndroidApiLevelCheck.check( - buildDir.resolve("classes/kotlin/android/main"), - project.property("mirai.android.target.api.level")!!.toString().toInt(), - project - ) +if (isAndroidSDKAvailable) { + tasks.register("checkAndroidApiLevel") { + doFirst { + analyzes.AndroidApiLevelCheck.check( + buildDir.resolve("classes/kotlin/android/main"), + project.property("mirai.android.target.api.level")!!.toString().toInt(), + project + ) + } + group = "verification" + this.mustRunAfter("androidMainClasses") } - group = "verification" - this.mustRunAfter("androidMainClasses") + tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") } -tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.implementation1(dependencyNotation: String) = implementation(dependencyNotation) { diff --git a/mirai-core-utils/build.gradle.kts b/mirai-core-utils/build.gradle.kts index 647f696c8..a13f9aea1 100644 --- a/mirai-core-utils/build.gradle.kts +++ b/mirai-core-utils/build.gradle.kts @@ -89,18 +89,20 @@ kotlin { } } -tasks.register("checkAndroidApiLevel") { - doFirst { - analyzes.AndroidApiLevelCheck.check( - buildDir.resolve("classes/kotlin/android/main"), - project.property("mirai.android.target.api.level")!!.toString().toInt(), - project - ) +if (isAndroidSDKAvailable) { + tasks.register("checkAndroidApiLevel") { + doFirst { + analyzes.AndroidApiLevelCheck.check( + buildDir.resolve("classes/kotlin/android/main"), + project.property("mirai.android.target.api.level")!!.toString().toInt(), + project + ) + } + group = "verification" + this.mustRunAfter("androidMainClasses") } - group = "verification" - this.mustRunAfter("androidMainClasses") + tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") } -tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.implementation1(dependencyNotation: String) = implementation(dependencyNotation) { diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index bc9aec17e..73f690045 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -114,18 +114,20 @@ kotlin { } } -tasks.register("checkAndroidApiLevel") { - doFirst { - analyzes.AndroidApiLevelCheck.check( - buildDir.resolve("classes/kotlin/android/main"), - project.property("mirai.android.target.api.level")!!.toString().toInt(), - project - ) +if (isAndroidSDKAvailable) { + tasks.register("checkAndroidApiLevel") { + doFirst { + analyzes.AndroidApiLevelCheck.check( + buildDir.resolve("classes/kotlin/android/main"), + project.property("mirai.android.target.api.level")!!.toString().toInt(), + project + ) + } + group = "verification" + this.mustRunAfter("androidMainClasses") } - group = "verification" - this.mustRunAfter("androidMainClasses") + tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") } -tasks.getByName("androidTest").dependsOn("checkAndroidApiLevel") fun org.jetbrains.kotlin.gradle.plugin.KotlinDependencyHandler.implementation1(dependencyNotation: String) = implementation(dependencyNotation) { From 45aca5f37dc351268f1712d460fb72321f6e0e1f Mon Sep 17 00:00:00 2001 From: Him188 <Him188@mamoe.net> Date: Mon, 5 Apr 2021 22:35:52 +0800 Subject: [PATCH 2/3] Add `ForwardMessageBuilder.toRawForwardMessage` and make `RawForwardMessage` stable (#1162) * Add `ForwardMessageBuilder.toRawForwardMessage` and make RawForwardMessage stable. * apiDump --- ...binary-compatibility-validator-android.api | 1 + .../api/binary-compatibility-validator.api | 1 + .../kotlin/message/data/ForwardMessage.kt | 21 ++++++++++--------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api index ccab21a3a..11a8811aa 100644 --- a/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api +++ b/binary-compatibility-validator/android/api/binary-compatibility-validator-android.api @@ -4301,6 +4301,7 @@ public final class net/mamoe/mirai/message/data/ForwardMessageBuilder : java/uti public fun subList (II)Ljava/util/List; public fun toArray ()[Ljava/lang/Object; public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + public final fun toRawForwardMessage ()Lnet/mamoe/mirai/message/data/RawForwardMessage; } public final class net/mamoe/mirai/message/data/ForwardMessageBuilder$BuilderNode : net/mamoe/mirai/message/data/ForwardMessage$INode { diff --git a/binary-compatibility-validator/api/binary-compatibility-validator.api b/binary-compatibility-validator/api/binary-compatibility-validator.api index f10ea180f..bad95cc60 100644 --- a/binary-compatibility-validator/api/binary-compatibility-validator.api +++ b/binary-compatibility-validator/api/binary-compatibility-validator.api @@ -4301,6 +4301,7 @@ public final class net/mamoe/mirai/message/data/ForwardMessageBuilder : java/uti public fun subList (II)Ljava/util/List; public fun toArray ()[Ljava/lang/Object; public fun toArray ([Ljava/lang/Object;)[Ljava/lang/Object; + public final fun toRawForwardMessage ()Lnet/mamoe/mirai/message/data/RawForwardMessage; } public final class net/mamoe/mirai/message/data/ForwardMessageBuilder$BuilderNode : net/mamoe/mirai/message/data/ForwardMessage$INode { diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt index d74bd35f9..fad96e514 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/ForwardMessage.kt @@ -31,7 +31,6 @@ import net.mamoe.mirai.utils.toLongUnsigned * [RawForwardMessage] 可以序列化保存, 也可以被多次[渲染][RawForwardMessage.render]产生不同格式的 [ForwardMessage]. */ @Serializable -@MiraiExperimentalApi public data class RawForwardMessage( /** * 消息列表 @@ -745,16 +744,18 @@ public class ForwardMessageBuilder private constructor( // endregion + /** + * 构造 [RawForwardMessage]. [RawForwardMessage] 可以被多个 [DisplayStrategy] [渲染][RawForwardMessage.render]. + * @since 2.6 + */ + public fun toRawForwardMessage(): RawForwardMessage = RawForwardMessage(container.map { + ForwardMessage.Node(it.senderId, it.time, it.senderName, it.messageChain) + }) - /** 构造 [ForwardMessage] */ - public fun build(): ForwardMessage = RawForwardMessage(container.map { - ForwardMessage.Node( - it.senderId, - it.time, - it.senderName, - it.messageChain - ) - }).render(this.displayStrategy) + /** + * 使用 [displayStrategy] 渲染并构造可以发送的 [ForwardMessage]. + */ + public fun build(): ForwardMessage = toRawForwardMessage().render(this.displayStrategy) internal fun Bot.smartName(): String = when (val c = this@ForwardMessageBuilder.context) { is Group -> c.botAsMember.nameCardOrNick From b227f719a27344caa8e2c669ceb558c76f96baec Mon Sep 17 00:00:00 2001 From: Karlatemp <karlatemp@vip.qq.com> Date: Mon, 5 Apr 2021 22:41:26 +0800 Subject: [PATCH 3/3] Fix kt compiler errors (#1159) * Fix kt compiler errors * Revert "Fix compiler bug, fix #1136" This reverts commit 39f6c168 * Fix log style Co-authored-by: Him188 <Him188@mamoe.net> --- .github/workflows/build.yml | 8 +- .github/workflows/doc.yml | 10 +- .github/workflows/release.yml | 4 +- .../mirai-core-api-compileKotlinAndroid.txt | 5 + .../mirai-core-api-compileKotlinJvm.txt | 5 + build.gradle.kts | 1 + buildSrc/build.gradle.kts | 1 + buildSrc/src/main/kotlin/Versions.kt | 1 + buildSrc/src/main/kotlin/analyzes/AsmUtil.kt | 61 ++++++++-- .../kotlin/analyzes/NoSuchMethodAnalyzer.kt | 21 +--- .../kotlin/postktcompile/PostKotlinCompile.kt | 106 ++++++++++++++++++ .../commonMain/kotlin/utils/RemoteFileImpl.kt | 35 ------ 12 files changed, 188 insertions(+), 70 deletions(-) create mode 100644 binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinAndroid.txt create mode 100644 binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinJvm.txt create mode 100644 buildSrc/src/main/kotlin/postktcompile/PostKotlinCompile.kt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c673bea10..a333ee296 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,10 +22,10 @@ jobs: run: ./gradlew clean --scan - name: Build mirai-core series - run: ./gradlew assemble --scan + run: ./gradlew assemble -Pmirai.pkc.check.enable --scan - name: mirai-core Tests - run: ./gradlew check --scan + run: ./gradlew check -Pmirai.pkc.check.enable --scan build-all: runs-on: ubuntu-latest @@ -48,7 +48,7 @@ jobs: run: ./gradlew clean --scan - name: Build all - run: ./gradlew assemble --scan + run: ./gradlew assemble -Pmirai.pkc.check.enable --scan - name: All Tests - run: ./gradlew check --scan + run: ./gradlew check -Pmirai.pkc.check.enable --scan diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index c1b30efb5..db0a20ed2 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -23,10 +23,10 @@ jobs: - name: chmod -R 777 * run: chmod -R 777 * - - name: Gradle build - run: ./gradlew clean build # if test's failed, don't publish + - name: Gradle build -Pmirai.pkc.check.enable + run: ./gradlew clean build -Pmirai.pkc.check.enable # if test's failed, don't publish - name: Dokka - run: ./gradlew :mirai-core-api:dokkaHtml + run: ./gradlew :mirai-core-api:dokkaHtml -Pmirai.pkc.check.enable - name: GitHub Pages Deploy uses: peaceiris/actions-gh-pages@v3 with: @@ -55,9 +55,9 @@ jobs: - name: chmod -R 777 * run: chmod -R 777 * - name: Gradle build - run: ./gradlew clean build # if test's failed, don't publish + run: ./gradlew clean build -Pmirai.pkc.check.enable # if test's failed, don't publish - name: Dokka - run: ./gradlew :mirai-console:dokkaHtml + run: ./gradlew :mirai-console:dokkaHtml -Pmirai.pkc.check.enable - name: GitHub Pages Deploy uses: peaceiris/actions-gh-pages@v3 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22da153f9..a142d8a40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -62,10 +62,10 @@ jobs: fillBuildConstants --scan - name: Assemble - run: ./gradlew assemble --scan + run: ./gradlew assemble -Pmirai.pkc.check.enable --scan - name: Check - run: ./gradlew check --scan + run: ./gradlew check -Pmirai.pkc.check.enable --scan - name: Gradle :mirai-core-utils:publish run: > diff --git a/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinAndroid.txt b/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinAndroid.txt new file mode 100644 index 000000000..0deec4f7a --- /dev/null +++ b/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinAndroid.txt @@ -0,0 +1,5 @@ +[net/mamoe/mirai/utils/RemoteFile$isDirectory$1] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.isDirectory(Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$listFilesCollection$1] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.listFilesCollection(Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$upload$3] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.upload(Ljava/io/File;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$upload$5] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.upload(Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$uploadAndSend$2] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.uploadAndSend(Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 diff --git a/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinJvm.txt b/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinJvm.txt new file mode 100644 index 000000000..0deec4f7a --- /dev/null +++ b/binary-compatibility-validator/kt-compile-edit/mirai-core-api-compileKotlinJvm.txt @@ -0,0 +1,5 @@ +[net/mamoe/mirai/utils/RemoteFile$isDirectory$1] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.isDirectory(Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$listFilesCollection$1] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.listFilesCollection(Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$upload$3] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.upload(Ljava/io/File;Lnet/mamoe/mirai/utils/RemoteFile$ProgressionCallback;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$upload$5] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.upload(Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 +[net/mamoe/mirai/utils/RemoteFile$uploadAndSend$2] [invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;] - Change opcode of net/mamoe/mirai/utils/RemoteFile.uploadAndSend(Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; from 183 to 185 diff --git a/build.gradle.kts b/build.gradle.kts index 5563c6e67..115982d72 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,6 +70,7 @@ tasks.register("publishMiraiCoreArtifactsToMavenLocal") { } analyzes.CompiledCodeVerify.run { registerAllVerifyTasks() } +postktcompile.PostKotlinCompile.run { registerForAll(rootProject) } allprojects { group = "net.mamoe" diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index ad3797a0f..a61e5a3e5 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -59,4 +59,5 @@ dependencies { api(asm("commons")) api(gradleApi()) + api("com.googlecode.java-diff-utils:diffutils:" + version("difflib")) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index f96d39e8d..6c4cd349e 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -43,6 +43,7 @@ object Versions { const val slf4j = "1.7.30" const val log4j = "2.13.3" const val asm = "9.1" + const val difflib = "1.3.0" // If you the versions below, you need to sync changes to mirai-console/buildSrc/src/main/kotlin/Versions.kt diff --git a/buildSrc/src/main/kotlin/analyzes/AsmUtil.kt b/buildSrc/src/main/kotlin/analyzes/AsmUtil.kt index 80e7883cb..628d1dea6 100644 --- a/buildSrc/src/main/kotlin/analyzes/AsmUtil.kt +++ b/buildSrc/src/main/kotlin/analyzes/AsmUtil.kt @@ -14,13 +14,60 @@ import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.FieldNode import org.objectweb.asm.tree.MethodNode +import java.io.Closeable import java.io.File import java.io.InputStream +import java.util.zip.ZipFile -typealias AsmClasses = Map<String, ClassNode> -typealias AsmClassesM = MutableMap<String, ClassNode> +@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE") +class AsmClassesM( + var res: List<Closeable>?, + val map: MutableMap<String, Lazy<ClassNode>> +) : AbstractMap<String, Lazy<ClassNode>>(), + MutableMap<String, Lazy<ClassNode>> by map, + Closeable { + operator fun plusAssign(map: AsmClassesM) { + this.map.putAll(map.map) + val r = res + if (r is MutableList<Closeable>) { + r.addAll(map.res ?: emptyList()) + return + } + res = (res ?: emptyList()) + (map.res ?: emptyList()) + } + + override fun close() { + res?.forEach { it.close() } + res = null + map.clear() + } +} + +typealias AsmClasses = AsmClassesM object AsmUtil { + var cc = 0 + fun File.readLib(): AsmClassesM { + val rs = mutableListOf<Closeable>() + val result: AsmClassesM = AsmClassesM(rs, HashMap()) + if (this.name.endsWith(".jar")) { + val zip = ZipFile(this) + rs.add(zip) + zip.entries().iterator().forEach l@{ entry -> + if (entry.isDirectory) return@l + if (!entry.name.endsWith(".class")) return@l + result[entry.name.removePrefix("/").removeSuffix(".class")] = lazy { + zip.getInputStream(entry).use { it.readClass() } + } + } + } else if (this.isDirectory) { + this.walk().filter { it.isFile && it.extension == "class" }.forEach { f -> + f.readClass().let { result[it.name] = lazyOf(it) } + } + } + return result + } + fun ClassNode.getMethod(name: String, desc: String, isStatic: Boolean): MethodNode? { return methods?.firstOrNull { it.name == name && it.desc == desc && ((it.access and Opcodes.ACC_STATIC) != 0) == isStatic @@ -33,7 +80,7 @@ object AsmUtil { } } - fun File.readClass(): ClassNode = inputStream().use { it.readClass() } + fun File.readClass(): ClassNode = inputStream().buffered().use { it.readClass() } fun InputStream.readClass(): ClassNode { val cnode = ClassNode() @@ -46,7 +93,7 @@ object AsmUtil { if (!this.containsKey(owner)) { ClassLoader.getSystemClassLoader().getResourceAsStream("$owner.class")?.use { val c = it.readClass() - this[c.name] = c + this[c.name] = lazyOf(c) } } } @@ -59,7 +106,7 @@ object AsmUtil { opcode: Int ): Boolean { patchJvmClass(owner) - val c = this[owner] ?: return false + val c = this[owner]?.value ?: return false val isStatic = opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC if (c.getField(name, desc, isStatic) != null) { return true @@ -77,7 +124,7 @@ object AsmUtil { patchJvmClass(owner) when (opcode) { Opcodes.INVOKESTATIC -> { - val c = this[owner] ?: return false + val c = this[owner]?.value ?: return false return c.getMethod(name, desc, true) != null } Opcodes.INVOKEINTERFACE, @@ -85,7 +132,7 @@ object AsmUtil { Opcodes.INVOKEVIRTUAL -> { fun loopFind(current: String): Boolean { patchJvmClass(current) - val c = this[current] ?: return false + val c = this[current]?.value ?: return false if (c.getMethod(name, desc, false) != null) return true c.superName?.let { if (loopFind(it)) { diff --git a/buildSrc/src/main/kotlin/analyzes/NoSuchMethodAnalyzer.kt b/buildSrc/src/main/kotlin/analyzes/NoSuchMethodAnalyzer.kt index e7f5db256..68c696830 100644 --- a/buildSrc/src/main/kotlin/analyzes/NoSuchMethodAnalyzer.kt +++ b/buildSrc/src/main/kotlin/analyzes/NoSuchMethodAnalyzer.kt @@ -54,35 +54,22 @@ object NoSuchMethodAnalyzer { fun check(classes: Sequence<File>, libs: Sequence<File>) = AsmUtil.run { val analyzer = AndroidApiLevelCheck.Analyzer(emptyMap()) - val asmClasses: AsmClassesM = mutableMapOf() + val asmClasses: AsmClassesM = AsmClassesM(mutableListOf(), HashMap()) libs.forEach { lib -> - if (lib.name.endsWith(".jar")) { - ZipFile(lib).use { zip -> - zip.entries().iterator().forEach l@{ entry -> - if (entry.isDirectory) return@l - if (!entry.name.endsWith(".class")) return@l - zip.getInputStream(entry).use { it.readClass() }.let { - asmClasses[it.name] = it - } - } - } - } else if (lib.isDirectory) { - lib.walk().filter { it.isFile && it.extension == "class" }.forEach { f -> - f.readClass().let { asmClasses[it.name] = it } - } - } + asmClasses += lib.readLib() } classes.map { it.walk() }.flatten().filter { it.isFile } .filter { it.extension == "class" } .map { it.readClass() to it } .onEach { (c, _) -> - asmClasses[c.name] = c + asmClasses[c.name] = lazyOf(c) }.toList().forEach { (classNode, file) -> analyzer.file = file classNode.methods?.forEach { method -> analyzeMethod(analyzer, method, asmClasses) } } + asmClasses.close() if (analyzer.reported) { error("Verify failed") } diff --git a/buildSrc/src/main/kotlin/postktcompile/PostKotlinCompile.kt b/buildSrc/src/main/kotlin/postktcompile/PostKotlinCompile.kt new file mode 100644 index 000000000..ec5e9d71f --- /dev/null +++ b/buildSrc/src/main/kotlin/postktcompile/PostKotlinCompile.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2019-2021 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 postktcompile + +import analyzes.* +import difflib.DiffUtils +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.MethodInsnNode +import java.io.File + +object PostKotlinCompile { + private fun AsmClasses.canAccessInterface(from: ClassNode, to: String): Boolean { + fun scan(current: ClassNode): Boolean { + if (current.name == to) return true + if (current.superName == to) return true + current.interfaces?.forEach { itf -> + if (scan(get(itf)?.value ?: return@forEach)) return true + } + return false + } + return scan(from) + } + + fun fixKtClass(dir: File, libs: Set<File>, logs: MutableList<String>) = AsmUtil.run { + val asmClasses: AsmClassesM = AsmClassesM(mutableListOf(), HashMap()) + dir.walk().filter { + it.isFile && it.extension == "class" + }.forEach { f -> + f.readClass().let { asmClasses[it.name] = lazyOf(it) } + } + val classes = asmClasses.values.toList() + libs.forEach { asmClasses += it.readLib() } + + classes.forEach { klassLazy -> + val klass = klassLazy.value + var edited = false + klass.methods?.forEach { method -> + method.instructions?.forEach { insn -> + if (insn is MethodInsnNode) { + if (insn.itf && insn.opcode != Opcodes.INVOKEINTERFACE && insn.opcode != Opcodes.INVOKESTATIC) { + if (!asmClasses.canAccessInterface(klass, insn.owner)) { + // println("${klass.name} . ${method.name}${method.desc}, ${insn.owner}.${insn.name}${insn.desc} (${insn.opcode})") + edited = true + logs.add( + "[${klass.name}] [${method.name}${method.desc}] - Change opcode of ${insn.owner}.${insn.name}${insn.desc} from ${insn.opcode} to ${Opcodes.INVOKEINTERFACE}" + ) + insn.opcode = Opcodes.INVOKEINTERFACE + } + } + } + } + } + if (edited) { + dir.resolve("${klass.name}.class").writeBytes( + ClassWriter(0).also { klass.accept(it) }.toByteArray() + ) + } + } + asmClasses.close() + } + + fun registerForAll(rootProject: Project) { + val validator = rootProject.file("binary-compatibility-validator/kt-compile-edit") + rootProject.subprojects { + val subp: Project = this@subprojects + subp.tasks.withType(AbstractKotlinCompileTool::class.java) { + val task: AbstractKotlinCompileTool<*> = this@withType + task.doLast { + val logFile = validator.resolve("${subp.name.replace(":", "_")}-${task.name}.txt") + val logs = mutableListOf<String>() + println("$task: Pre classes patching......") + task.outputs.files.toList().forEach { f -> + println("$task: Patching $f") + fixKtClass(f, task.classpath.files, logs) + } + println("$task: Kotlin compiled classes fix completed.") + val oldLog = if (logFile.isFile) logFile.readText().trim().lines() else emptyList() + val newLog = logs.sorted() + if (newLog == oldLog) return@doLast + val patch = DiffUtils.diff(oldLog, newLog) + + val diff = DiffUtils.generateUnifiedDiff(logFile.name, logFile.name + ".rebuild", oldLog, patch, 3) + logFile.parentFile.mkdirs() + logFile.writeText(newLog.joinToString("\n", postfix = "\n")) + val diffMsg = diff.joinToString("\n") + if (rootProject.hasProperty("mirai.pkc.check.enable")) { + error(diffMsg) + } else { + println(diffMsg) + } + } + } + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt index 3cd845320..eb4e7f0d4 100644 --- a/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt +++ b/mirai-core/src/commonMain/kotlin/utils/RemoteFileImpl.kt @@ -33,9 +33,7 @@ import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.FileMessage import net.mamoe.mirai.message.data.sendTo import net.mamoe.mirai.utils.* -import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource import net.mamoe.mirai.utils.RemoteFile.Companion.ROOT_PATH -import java.io.File import java.util.* import kotlin.contracts.contract @@ -194,9 +192,6 @@ internal class RemoteFileImpl( } override suspend fun isFile(): Boolean = this.getFileFolderInfo().checkExists(this.path).isFile - - // compiler bug - override suspend fun isDirectory(): Boolean = !isFile() override suspend fun length(): Long = this.getFileFolderInfo().checkExists(this.path).size override suspend fun exists(): Boolean = this.getFileFolderInfo() != null override suspend fun getInfo(): RemoteFile.FileInfo? { @@ -259,9 +254,6 @@ internal class RemoteFileImpl( } } - // compiler bug - override suspend fun listFilesCollection(): List<RemoteFile> = listFiles().toList() - @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @OptIn(JavaFriendlyAPI::class) override suspend fun listFilesIterator(lazy: Boolean): Iterator<RemoteFile> { @@ -313,9 +305,6 @@ internal class RemoteFileImpl( return getFilesFlow().filter { it.id == id }.firstOrNull()?.resolveToFile() } - // compiler bug - override suspend fun resolveById(id: String): RemoteFile? = resolveById(id, deep = true) - override fun resolveSibling(relative: String): RemoteFileImpl { val parent = this.parent if (parent == null) { @@ -524,34 +513,10 @@ internal class RemoteFileImpl( ) } - // compiler bug - override suspend fun upload(resource: ExternalResource): FileMessage { - return upload(resource, null) - } - - // compiler bug - override suspend fun upload(file: File, callback: RemoteFile.ProgressionCallback?): FileMessage = - file.toExternalResource().use { upload(it, callback) } - - //compiler bug - override suspend fun upload(file: File): FileMessage { - // Dear compiler: - // - // Please generate invokeinterface. - // - // Yours Sincerely - // Him188 - return file.toExternalResource().use { upload(it) } - } - override suspend fun uploadAndSend(resource: ExternalResource): MessageReceipt<Contact> { return upload(resource).sendTo(contact) } - // compiler bug - override suspend fun uploadAndSend(file: File): MessageReceipt<Contact> = - file.toExternalResource().use { uploadAndSend(it) } - // override suspend fun writeSession(resource: ExternalResource): FileUploadSession { // }