为什么字节码生成为
.annotation system Ldalvik/annotation/Throws;
value = {
Ljava/io/FileNotFoundException;
}
.end annotation
Run Code Online (Sandbox Code Playgroud)
而不是.throws Ljava/io/FileNotFoundException
throws FileNotFoundException如果一个方法在java代码的标头中声明?
作为我正在编写的编程语言的编译器的一部分,我在字节码中遇到了通用签名,我试图解析并转换为AST.解析算法大多数都有效,但似乎有一种特殊情况,这些签名的格式有点奇怪.以下是一些这样的情况:
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.lang.Class#getAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.lang.Class#getDeclaredAnnotation: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
java.lang.Class#getDeclaredAnnotationsByType: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
java.util.Arrays#parallelSort: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
java.util.Collections#sort: <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V
Run Code Online (Sandbox Code Playgroud)
在这些类中的所有方法中,这些是唯一具有::签名的方法.我的问题是这个令牌的作用以及它存在的原因.
编辑
我知道Java语言中的::运算符,但这是字节码级别的内容.
假设我写了一种编程语言;对于同名,我将其称为lang。
为了开始编写lang的漫长旅程,我决定从自己编写lang开始。我实际上不能运行它,因为没有什么可以运行自己运行的程序。
因此,我首先使用Java 为lang编写另一个编译器。这次,当我完成后,我决定将其转换为Bytecode,然后保留它。我现在有一个正在运行的编译器,它将把我所有的lang代码转换为Bytecode。
因此,我决定将自己的语言编译器插入我刚用Java编写的编译器中。然后,我将自编译器转换为Bytecode,然后剔除Java编译器。现在,我有了一个纯粹由自己编写的lang编译器,可以将其转换为Bytecode,以备使用。
这样就创建了一个可靠的程序,我理解了所有这些内容,但是我的问题是,相对于JVM的编译器设计,如果决定发布针对该语言的更新该怎么办?我该如何更新字节码?我是否只用旧版本重写了该语言的更新版本?
我问这是因为这是我想要做的。自己编写一种不存在的语言,然后通过首先使用Java创建编译器将其引导到JVM。
这与使用C ++所做的相同。先编写带有类的C,然后再编写C ++,最后,将带有类的C替换为自举的C ++。但是他们到底是怎么更新语言的呢?
正如你们中的一些人可能知道的那样,我们有大量的操作码来比较不同类型的原始值:
LCMP
FCMPL
FCMPG
DCMPL
DCMPG
IFEQ
IFNE
IFLT
IFGE
IFGT
IFLE
IF_ICMPEQ
IF_ICMPNE
IF_ICMPLT
IF_ICMPGE
IF_ICMPGT
IF_ICMPLE
IF_ACMPEQ
IF_ACMPNE
...
Run Code Online (Sandbox Code Playgroud)
由于显而易见的原因,指令集的创建者并没有费心去添加所有IF_LCMPEQ,IF_FCMPLT...指令,但我想知道为什么没有ICMP指令,看到它对于布尔值或者非常有用Integer.compare(int, int).
编写我自己的编译器用于类似Java的语言,我在编译时遇到了问题synchronized blocks.我提出了以下想法,将它们简化为try-finally块:
synchonized (obj) {
statements...
}
Run Code Online (Sandbox Code Playgroud)
可以替换
Object _lock = obj
_monitorEnter(lock)
try {
statements...
}
finally {
_monitorExit(lock)
}
Run Code Online (Sandbox Code Playgroud)
在哪里_monitorEnter和_monitorExit代表MONITORENTER和MONITOREXIT说明.
我对这个如何synchronized编译的假设是正确的,还是我错过了什么?
编辑
我以前的实现对机构内部的一些特殊处理return和throw声明.基本上,它会在每个或指令之前手动加载所有lock变量.这是由块处理,还是我还需要这些检查?MONITOREXIT*RETURNTHROWfinally
我知道字节码检测是什么。它只是在运行时更改 .class 文件字节码,这似乎自 JDK 1.5 以来可用。但是,据说是在类加载期间而不是在运行时。
现在我的问题是,什么是 AOT 或 Ahead of Time 仪器?什么是相反的程序?随着时间的推移仪器?
检测您的代码
Quasar 光纤依赖于字节码检测。这可以在类加载时通过 完成Java Agent,或者在编译时使用 Ant 任务完成。运行检测
Java 代理Quasar的轻量级线程实现依赖于字节码检测。检测可以在编译时(详见下文)或运行时使用 Java 代理执行。要运行 Java 代理,必须将以下内容添加到 java 命令行(或使用您喜欢的构建工具将其添加为 JVM 参数):
-javaagent:path-to-quasar-jar.jar
提前 (AOT)
检测 使用 Quasar 检测程序的简单且可取的方法是使用 Java 代理,它在运行时检测代码。然而,有时运行 Java 代理不是一种选择。Quasar 通过 Ant 任务支持 AOT 检测。该任务位于
co.paralleluniverse.fibers.instrument.InstrumentationTask中quasar-core.jar,它接受要检测的类文件集。并不是所有的类都会被实际检测——只有那些带有suspendable methods(见下文)的类——所以只需将程序中的所有类文件交给任务即可。事实上,Quasar 本身是 提前检测的。
我一直试图通过玩转 ASM 中的跳转来了解堆栈映射框架在 Java 中的工作原理。我创建了一个简单的方法来尝试一些东西:(用 Krakatau 拆卸):
L0: ldc 'hello'
L2: astore_1
L3: getstatic Field java/lang/System out Ljava/io/PrintStream;
L6: new java/lang/StringBuilder
L9: dup
L10: invokespecial Method java/lang/StringBuilder <init> ()V
L13: ldc 'concat1'
L15: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L18: aload_1
L19: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L22: invokevirtual Method java/lang/StringBuilder toString ()Ljava/lang/String;
L25: invokevirtual Method java/io/PrintStream println (Ljava/lang/String;)V
L28: getstatic Field java/lang/System out Ljava/io/PrintStream;
L31: new java/lang/StringBuilder
L34: dup
L35: invokespecial Method java/lang/StringBuilder <init> ()V
L38: ldc 'concat2'
L40: invokevirtual …Run Code Online (Sandbox Code Playgroud) 我编写了一个简单的 Java 字节码解析器来进行一些实验,最近它在一个意想不到的地方失败了。在读取java/lang/reflect/Member.javaJava 1.1.8.16时rt.jar,我的解析器很生气,因为Member它是这样开始的(注意缺少ACC_ABSTRACT标志):
Classfile Member.class\n Last modified Aug 8, 2002; size 350 bytes\n MD5 checksum 9a1aaec8e70e9a2ff9d63331cb0ea34e\n Compiled from "Member.java"\npublic interface java.lang.reflect.Member\n minor version: 3\n major version: 45\n flags: (0x0201) ACC_PUBLIC, ACC_INTERFACE\n...\nRun Code Online (Sandbox Code Playgroud)\n\nJava 1.2.2.17 的版本更正了这一点,并将标志设置为0x0601( ACC_ABSTRACT | ACC_INTERFACE | ACC_PUBLIC)。
我能找到的最早的 JVM 规范(据称是 1.0.2)有这样的说法(\xc2\xa74.1,p.\xc2\xa086,强调):
\n\n\n\n\n接口是隐式抽象的 (\xc2\xa72.13.1);必须设置其
\nACC_ABSTRACT\xef\xac\x82ag 。接口不可能是最终的;如果是的话,它的实现永远无法完成(\xc2\xa72.13.1),因此它无法设置其\xef\xac\x82ag。ACC_FINAL
JVM 规范第 9 版也有类似的话:
\n\n\n如果
ACC_INTERFACE …
问题:第14行是什么意思?
使用 javap -v -c 反汇编以下代码:
public class test {
static int i = 2;
public static void main(String[] args) {
test x = new test();
System.out.println("text + String: " + i);
}
}
Run Code Online (Sandbox Code Playgroud)
在主函数中,我们得到以下内容:
14: invokedynamic #20, 0 // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
19: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
BootstrapMethods:
0: #38 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#44 text + String: \u0001
Run Code Online (Sandbox Code Playgroud)
因此,例如,第 19 行表示来自运行时常量池中 #24 项的 invokevirtual 函数。调用的方法println()来自于类java/io/PrintStream,其输入来自于类Ljava/lang/String,其返回值为 Void。
至于第 14 行,#0 持有对 BootstrapMethod …
我正在编写一个输出 JVM 字节代码的编译器,并使用 ASM 9.4 作为后端。这工作得很好,但我对一个特殊的怪癖感到困惑。
我指定COMPUTE_FRAMES自动计算堆栈帧。据说,这种权衡是(可以理解)这使得代码生成需要更多时间,但它似乎足够快。
我能找到的任何地方都没有记录,指定COMPUTE_FRAMES会导致生成大量冗余指令。具体来说,athrow每个方法末尾都有一个 ,并且在某些情况下,一堆长运行的nops 分散在该方法中。
这似乎并没有造成任何伤害;我最初的测试用例顺利通过,我想 JIT 编译器实际上不会为冗余指令生成任何机器代码。但这有点令人不安。它为什么要这样做?我做错了什么吗?
jvm-bytecode ×10
java ×9
jvm ×5
bytecode ×3
.class-file ×1
android ×1
dalvik ×1
generics ×1
jasmin ×1
javap ×1
quasar ×1
synchronized ×1
try-finally ×1