当前位置: 首页 > 知识库问答 >
问题:

Kotlin 替代 javah

巢靖
2023-03-14

javah自JDK 8以来已被弃用,并将在JDK 10中/已被删除,根据JEP 313和弃用文本,应使用带有-h标志的javac代替:

警告:javah工具计划在下一个主要JDK版本中删除。该工具已被JDK 8中添加javac的'-h'选项所取代。建议用户迁移到使用javac'-h'选项;有关更多信息,请参阅javac手册页。

问题是,javah对编译后的进行操作。class文件,而javac操作源文件(即.javafiles)

javah 与 Kotlin 和外部函数配合得很好,因为所有内容最终都被编译为 Java 字节码,但由于使用 Kotlin 时没有任何 Java 源文件,我看不出 javac -h 有任何工作方式。

是否有 Kotlin 的 javah 替代品或解决方法?

共有3个答案

刘胜泫
2023-03-14

我写了一个简单的gradle任务来生成JNI标头,它的方法与@Oo.oO发布的方法类似,但与gradle有更好的集成,因此可以在Windows以及基于Unix的操作系统上运行。

val generateJniHeaders by tasks.creating {
    group = "build"
    dependsOn(tasks.getByName("compileKotlinJvm"))

    // For caching
    inputs.dir("src/jvmMain/kotlin")
    outputs.dir("src/jvmMain/generated/jni")

    doLast {
        val javaHome = Jvm.current().javaHome
        val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found")
        val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found")
        val buildDir = file("build/classes/kotlin/jvm/main")
        val tmpDir = file("build/tmp/jvmJni").apply { mkdirs() }

        val bodyExtractingRegex = """^.+\Rpublic \w* ?class ([^\s]+).*\{\R((?s:.+))\}\R$""".toRegex()
        val nativeMethodExtractingRegex = """.*\bnative\b.*""".toRegex()

        buildDir.walkTopDown()
            .filter { "META" !in it.absolutePath }
            .forEach { file ->
                if (!file.isFile) return@forEach

                val output = ByteArrayOutputStream().use {
                    project.exec {
                        commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
                        standardOutput = it
                    }.assertNormalExitValue()
                    it.toString()
                }

                val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return@forEach

                val lastDot = qualifiedName.lastIndexOf('.')
                val packageName = qualifiedName.substring(0, lastDot)
                val className = qualifiedName.substring(lastDot+1, qualifiedName.length)

                val nativeMethods =
                        nativeMethodExtractingRegex.findAll(methodInfo).mapNotNull { it.groups }.flatMap { it.asSequence().mapNotNull { group -> group?.value } }.toList()
                if (nativeMethods.isEmpty()) return@forEach

                val source = buildString {
                    appendln("package $packageName;")
                    appendln("public class $className {")
                    for (method in nativeMethods) {
                        if ("()" in method) appendln(method)
                        else {
                            val updatedMethod = StringBuilder(method).apply {
                                var count = 0
                                var i = 0
                                while (i < length) {
                                    if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 })
                                    else i++
                                }
                            }
                            appendln(updatedMethod)
                        }
                    }
                    appendln("}")
                }
                val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
                outputFile.writeText(source)

                project.exec {
                    commandLine(javac, "-h", jniHeaderDirectory.absolutePath, outputFile.absolutePath)
                }.assertNormalExitValue()
            }
    }
}
施英哲
2023-03-14

目前没有内置的方法可以做到这一点。2019年11月提出的Kotlin问题追踪器上有一个公开的问题,但截至目前,它还没有被优先排序,也没有目标版本。

JDK 10生成头的唯一方法是使用< code>javac -h,它只适用于Java源代码,不适用于Kotlin。我在How to solve missing javah in Java 10-ugly way linked by oo . oo中测试了这种方法,目前它是一种变通方法。这些步骤是:

    < li >使用< code>javap将字节码反编译回Java < li >编辑反编译的Java,使其适合生成头文件(将文件简化为< code >本机方法定义)。 < li >通过< code>javac -h运行反编译的Java代码以生成标头

我正在考虑写一个gradle脚本来做到这一点(或者希望别人能打败我!)。如果我设法完成了,我会更新这篇文章。

颛孙麻雀
2023-03-14

我推荐gjavap。< br >将来,我还将实现一个更易于使用的命令行工具,提供与javap类似的功能

 类似资料:
  • 我正在搜索方法替换。它用于将值简单地传递给前一个片段,但问题是,我使用BackStackUtil进行导航,所以只有在返回前以某种方式清除堆栈时,才会在返回时调用方法 前一个片段包含项目列表,当下一个片段可以修改拾取的项目,而另一个片段可以做其他事情时,当这些事件中的每一个都传递给前一个片段时,这就是粘性事件链<应用程序结构不允许我在当前阶段应用协调器模式,而且我不想将包附加到保留在堆栈上的片段<我

  • 问题内容: 这是一个从来没有一个正确答案的问题,我已经在网上搜索了很多次,却找不到解决方案。 它适用于Firefox,Chrome。我的responseText将像Réunion这样返回char,它将显示为奇怪的符号。 我尝试了许多方法,例如编码和解码,在响应文件中设置标头都无效。我没主意了。请帮助某人。 在主文件中,确保设置了内容类型和字符集。 在您的AJAX加载页面中,确保您位于顶部。 问题解

  • 问题内容: 问题 在解决这个问题之后,似乎基于文件或磁盘的实现可能是解决我在此处提到的问题的正确解决方案。精简版: 目前,我已将实施为。 条目以相当固定的速率连续添加到其中。稍后对此进行详细说明。 最终,无论如何,这意味着JVM耗尽了堆空间。 在工作中,(强烈)建议我使用SQLite解决此问题,但是在问了上一个问题之后,我认为数据库不是适合此工作的合适工具。所以- 让我知道这听起来是否疯狂 -我认

  • 问题内容: 首先-我的描述;) 我从服务器收到XmlHttpRequests JSON响应。MySQL驱动程序将所有数据输出为字符串,PHP将其按原样返回,因此任何整数都将作为字符串返回,因此: JS中的parseInt()函数是否有任何快速替代方法(hack),可以解析纯数字字符串,例如 问题答案: 要转换为整数,只需使用一元+运算符,它应该是最快的方法: 可以通过类似的方式转换为其他类型: 更

  • 问题内容: 我有以下内容: 和 终于我有了 如果我做 和P元帅,我只是把田野当作事物的一部分,而不是高度。 我知道我可以在A中添加@XmlSeeAlso(B.class),这一切都可以。 但是问题是我不知道B以外的所有扩展类,因为A可能在运行时扩展。 如何在运行时动态定义@XmlSeeAlso? 问题答案: 这取决于您如何创建JAXBContext。该newInstance方法可以与所有的类的显式

  • 问题内容: 在一个Web项目中,用户上传了他们的文件,但是当我在服务器上收到它们时,它们将被存储为.tmp文件而不是原始文件扩展名(这也是我的首选行为)。 但是,这引起了问题。虽然对我来说本地的,但是在我的Linux开发机器上,它可以正常工作并且可以确定正确的mime类型,但是当我将项目上传到生产服务器(amazon beantalk)时,似乎无法正确确定mime类型。 从阅读javadocs来看