Java编译器能否在类文件中包含符号常量字段引用?可能吗?

4
众所周知,Java编译器在编译时从其他类中提取常量字段值。由此产生的类文件不包含任何类型的常量池条目。
问:能否告诉编译器仅执行此操作?(Oracle JDK 7会很好)
作为示例,请考虑以下代码段out.println(some.other.class.FOO),它读取FOO(例如public static final int FOO = 1234)并输出它。我能够轻松找到对println的引用,但常量被转换为匿名sipush 1234。
对于类级别的依赖关系分析,这里透明度将非常重要!请注意,我不是要求在相关代码中以某种方式显示更改值(请参见其他SO问题的大量内容)...
我正在考虑一个Java编译器API插件来使用javac,但这听起来有点牵强附会?有什么想法吗?

1
相关链接:https://dev59.com/YHA75IYBdhLWcg3wAEDx?rq=1 - user180100
你能更具体地说明你想让编译器知道什么吗?我认为我可能有一个有趣的答案... - Jean-François Savard
感谢@RC提供的另一个指针。很抱歉在搜索时我错过了这个(可能还有更多...)。@Jean-François,想法是创建一个合成(未使用的)Fieldref(就像GETSTATIC操作码一样)到常量池中。我意识到这只适用于自己的代码(需要编译),但至少可以将dep checker工具保持在类级别上,而不是源代码级别上。 - mgaert
这在C语言中是可行的,其中.obj文件(Java .class)被链接到可执行文件(Java .jar),最终产品不再使用.obj文件。在Java中,.class已经提交给了最终的优化过程。在Java中,使用源代码解析、AST(抽象语法树)更有意义,可以参考ASM和其他工具。 - Joop Eggen
@JoopEggen,ASM 也可以在字节码级别上工作,因此无法访问AST。为了弥合与源代码之间的差距,我正在考虑一个javac的“处理器”插件。这应该可以访问AST,并影响字节码。 - mgaert
1个回答

2
只有初始化为常量表达式的 final 变量 才能被内联。因此,如果您想避免编译时的内联,显然的方法是将字段变成非 final 或者将初始化表达式复杂化,使其不再被视为常量(例如 (null == null) ? 1234 : 0)) ¹

一旦您已经运行了编译器,就太晚了,因为生成的代码与直接插入常量引用字段是完全等价的。

如果您正在对源代码进行静态分析,则可以使用任何标准的依赖项查找工具。


并非完全正确。如果您有一个非静态的最终布尔值,其具有预分配的值,并在同一类中的条件表达式中使用,则该值也会被内联以删除条件。为了完整起见。 - Rafael Winterhalter
@Holger,你是对的,编译时常量首先与字段无关,因为这在规范中是隐含的:(http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28)。我试图给答案添加一些内容。主要是,我忘记了内部类,因为我只是假设来自其他类的非静态字段在这里不会有任何区别,因为它们不能在没有通过该类的实例进行引用的情况下进行引用,而这些实例又永远不是编译时常量。但你当然是对的。 - Rafael Winterhalter
@Rafael Winterhalter:即使它是编译时常量,您也必须通过实例访问实例字段以满足正式规则,但该值将被内联化或更精确地说,生成的字节码将包含一个“null”检查以模拟实例访问(javac使用instance.getClass()),然后使用内联常量值。无论如何,实际上是编译时常量的实例字段很少有用,因为它们浪费了每个实例的存储空间,而这些空间永远不会被访问(除非涉及反射)。 - Holger
@Holger 我不知道这个,谢谢你提供的额外信息。但是你有例子吗?我刚试图重现你描述的内容,但我无法观察到getClass驱动的null检查对于Foo$Barclass Foo { final String foo = "foo"; class Bar { void bar() { System.out.println(foo); } } }该值只是内联而没有检查外部实例是否为null - Rafael Winterhalter
@Holger 我之前真的不知道。感谢您的澄清!(供未来读者参考:class Foo { final boolean foo = true; } class Bar { void bar(Foo foo) { boolean bar = foo.foo } })这尤其有趣,因为当前的C2编译器会抛出一个缓存的、无需使用栈的“NullPointerException”:https://bugs.openjdk.java.net/browse/JDK-8073432 这个javac编译决策为一些愉快的调试会话打开了大门。 - Rafael Winterhalter
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接