修补 java.base 结果 java.lang.LinkageError

Patching java.base results in java.lang.LinkageError

我正在尝试在 Java 11 中做同样的事情,这可以在 java 9 之前用 -Xbootclasspath/p:path 完成。

作为一个简单的示例,我修改了 java.lang.IntegervalueOf 方法之一,并使用以下代码编译了项目:

javac --module-source-path=src/java.base --patch-module java.base=src/java.base -d mods $(find src -name '*.java')

然后我 运行 一个简单的示例使用:

java --patch-module java.base=<pathToMyModifiedJavaBaseClasses> -p lib -m my.moduleA/my.moduleA.Main

这很有效,我看到了显示的修改(我从 valueOf 中打印出来的简单内容)。

然而,当我尝试用 java.lang.ClassLoader 做同样的事情时,我在执行程序(编译工作)时收到以下错误:

Error occurred during initialization of boot layer java.lang.LinkageError: loader 'bootstrap' attempted duplicate class definition for java.lang.invoke.SimpleMethodHandle.

我什至不需要在 java.lang.ClassLoader 中进行更改。我的补丁文件夹中 class 的绝对存在似乎引发了这个错误。 (我只想在 class 的底部添加一个字段)

注意:我只是认为它在 ClassLoader class 是用 Eclipse 编译时有效。我知道的少数差异之一是 Eclipse 编译器似乎还没有遵循 JEP 280。但是 javac 产生的字节码中也有 invokedynamic 指令,所以我怀疑这是问题所在。

你已经指出了正确的方向。当您使用当前版本的 Eclipse 编译 class 时,它会起作用,因为该编译器尚未遵循 JEP 280,因此它不会使用 invokedynamic 进行字符串连接。

这并不意味着在 ClassLoader 中使用 invokedynamic 通常是有问题的。它仅在 java.lang.invoke 程序包引导期间执行的某些关键代码路径中存在问题,显然,此 class 确实在此代码路径上使用了字符串连接。

javac 的情况下,您可以通过选项
强制使用旧的字符串连接代码 -XDstringConcat=inline。查看 JDK 附带的 ClassLoader.class 的字节码,似乎此 class 已使用此选项编译。事实上,查看一些示例,似乎整个 java.base 模块都是使用该选项编译的,与例如相反。 java.desktop,其 classes 使用 invokedynamic 进行字符串连接。

所以结论是,要在java.base模块(在OpenJDK和衍生产品中)中修补classes,使用时使用-XDstringConcat=inline选项编译它们javac.