Pie*_*ard 3 java jvm bytecode identifier
标识符由Java语言规范,Java SE 7 Edition(§3.8)很好地定义
Run Code Online (Sandbox Code Playgroud)An identifier is an unlimited-length sequence of Java letters and Java digits, the first of which must be a Java letter.
据我所知,由于方法名称是标识符,因此不可能在java中命名以数字开头的方法,并javac遵守此规则.
那么,为什么Java虚拟机似乎不遵守这个规则,允许我们在Bytecode中命名一个以数字开头的函数?
这个简单的代码片段实际上会打印f99()方法名称及其参数的值.
public class Test {
public static void main(String[] args) {
Test t = new Test();
System.out.println(t.f99(100));
}
public int f99(int i){
System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName());
return i;
}
}
Run Code Online (Sandbox Code Playgroud)
编译和执行:
$ javac Test.java
$ java Test
Run Code Online (Sandbox Code Playgroud)
输出:
f99
100
Run Code Online (Sandbox Code Playgroud)
可以在编译后反汇编代码,并f99通过99(在reJ之类的工具的帮助下 )重命名所有出现的代码.
$ java Test
Run Code Online (Sandbox Code Playgroud)
输出:
99
100
Run Code Online (Sandbox Code Playgroud)
那么,方法的名称实际上是"99"吗?
Java语言规范限制有效方法名称中的字符,以帮助使Java语言的解析明确无误.
JVM旨在支持Java以外的语言.因此,限制不应相同; 除非我们想强制所有非Java语言具有相同的限制.为JVM选择的限制是允许对方法签名进行明确解析的最小集合,这种格式出现在JVM规范中而不是JLS中.
取自JVM Spec
a name must not contain any of the ASCII characters . ; [ / < > :
Run Code Online (Sandbox Code Playgroud)
也就是说,以下是有效的JVM签名[Lcom/foo/Bar;,其特殊字符已从方法名称中排除.
<>进一步保留以分离从施用方法,具体地特别JVM方法<init>和<clinit>,它们是在JLS不允许两个方法名.
那么,该方法的名称实际上是“99”吗?
真正的程序员不使用解析器,他们使用sed:
javac Test.java
sed -i 's/\d003f99/\d00299/' Test.class
java Test
Run Code Online (Sandbox Code Playgroud)
输出:
99
100
Run Code Online (Sandbox Code Playgroud)
这是可行的,因为我们知道方法名称以Utf8 条目的明文形式存储在常量池中,并且 JVMS 表示 Utf8 条目的格式为:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
Run Code Online (Sandbox Code Playgroud)
所以我们有类似的东西:
01 | 00 03 | 'f' '9' '9'
Run Code Online (Sandbox Code Playgroud)
(标识符 3 字节长)并将 sed 命令替换03 | 'f' '9' '9'为02 | '9' '9'(现在 2 字节长)。
后来我检查了一下,javap -v Test.class它sed做了我想做的事情。前:
#18 = Utf8 f99
Run Code Online (Sandbox Code Playgroud)
后:
#18 = Utf8 99
Run Code Online (Sandbox Code Playgroud)
经过手动编辑、运行、反编译并.class与 JVMS 进行比较后,我只能得出结论:方法名称必须是99:-)
所以这只是字节码中不存在的 Java 语言限制。
为什么 Java 禁止使用这样的名称?
可能使语法看起来像 C。
不以数字开头可以让人类和解析器更轻松地区分标识符和整数文字。
也可以看看: