如何解决如何添加/替换 .class 文件中的 SourceFile 属性
我想在编译的 Java SourceFile
文件中添加/替换 .class
属性。我没有注意到 Java 编译器有任何晦涩的命令行选项来覆盖 SourceFile
的默认值。我也没有在 Java 反射 API 中看到任何可以帮助我的东西。略读 JVM 规范的第 4 章表明我可以花几周时间编写 .class
文件解析器/修饰符来完成这项工作。在我投入精力编写这样一个解析器/修饰符之前,我想检查一下我是否遗漏了什么。标准 JDK 中是否有任何内容可以帮助添加/替换 SourceFile
属性?
对于任何想知道我为什么要弄乱 SourceFile
属性的人...我有一个命令行工具,可以将“用一些语法糖增强的 Java”文件预处理为 Java 语法。此类文件的文件扩展名是 .bi
。因此,预处理器将 Foo.bi
转换为 Foo.java
。另外,Foo.bi
和 Foo.java
之间存在行号对应关系,所以如果运行时错误发生在 Foo.java
的第 42 行,那么该错误应该真正在线修复42 of Foo.bi
(然后运行预处理器并编译更新的 Foo.java
文件)。为方便起见,我希望错误的堆栈跟踪指示 Foo.bi
而不是 Foo.java
,并且我的实验表明这可以通过确保 Foo.class
文件具有 {{1 }} 属性,值为 SourceFile
。
解决方法
标准 JDK API 中没有这样的功能,但在尝试自己实现字节码处理器之前,请考虑使用像 ASM 这样的 3rd 方库。使用该库,可以像这样实现任务:
public static void main(String[] args) throws IOException {
Path in = Paths.get(URI.create("jrt:/java.base/java/lang/Object.class"));
Path out = Files.createTempFile("Object",".class");
changeSourceAttr(in,out,"Object.bi");
runJavaP(out);
Files.delete(out);
}
private static void changeSourceAttr(Path in,Path out,String newValue)
throws IOException {
ClassReader cr = new ClassReader(Files.readAllBytes(in));
ClassWriter cw = new ClassWriter(cr,0);
cr.accept(new ClassVisitor(Opcodes.ASM5,cw) {
boolean sourceSeen;
@Override
public void visitSource(String source,String debug) {
sourceSeen = true;
super.visitSource(newValue,debug);
}
@Override
public void visitEnd() {
if(!sourceSeen) {
super.visitSource(newValue,null);
}
super.visitEnd();
}
},0);
byte[] code = cw.toByteArray();
Files.write(out,code);
}
private static void runJavaP(Path out) {
ToolProvider.findFirst("javap")
.ifPresent(tp -> tp.run(System.out,System.err,out.toString()));
}
Compiled from "Object.bi"
public class java.lang.Object {
public java.lang.Object();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString();
public final native void notify();
public final native void notifyAll();
public final void wait() throws java.lang.InterruptedException;
public final native void wait(long) throws java.lang.InterruptedException;
public final void wait(long,int) throws java.lang.InterruptedException;
protected void finalize() throws java.lang.Throwable;
}
相关部分是 changeSourceAttr
方法。它将更改源文件属性(如果存在)或添加新属性。
该示例使用临时文件更改 java.lang.Object
类的属性并运行 javap
以显示结果。示例代码需要JDK9+,实际的转换方法应该也适用于以前的版本。当源文件不是来自 JDK 库时,源文件和目标文件可能是同一个文件。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。