我目前正在编写一个针对Java字节码的玩具编译器.
我想知道在编写.class文件之前是否存在可以在发出的字节码中进行的各种简单窥孔优化的某种目录,可能是摘要.我实际上知道一些具有此功能的库,但我想自己实现它.
我已经在(Java)字节码上工作了一段时间,然而,我从未想过要问为什么会输入一些指令?我知道在ADD操作中,我们需要区分整数加法和FP加法(这就是为什么我们有IADD和FADD).但是,为什么我们需要区分ISTORE和FSTORE?它们都涉及完全相同的操作,即从堆栈移动32位到局部变量位置?
我能想到的唯一答案是类型安全,以防止这种情况:( ILOAD,ILOAD,FADD).但是,我认为类型安全性已经在Java语言级别实施.好的,Class文件格式没有直接与Java结合,所以这是一种为不支持它的语言强制执行类型安全的方法吗?任何想法?谢谢.
编辑:跟进Reedy的回答.我写了这个最小的程序:
public static void main(String args[])
{
int x = 1;
}
Run Code Online (Sandbox Code Playgroud)
编译为:
iconst_1
istore_1
return
Run Code Online (Sandbox Code Playgroud)
使用字节码编辑器,我改变了第二条指令:
iconst_1
fstore_1
return
Run Code Online (Sandbox Code Playgroud)
它返回了一个java.lang.VerifyError:期望在堆栈上找到float.
我想知道,如果在堆栈上没有类型的信息,只有位,FSTORE指令是如何知道它处理的是int而不是浮点数?
注意:我找不到这个问题的更好的标题.随意改进它.
使用Asm可以很容易地定义简单的getter和setter(幸运的是,它甚至可以在FAQ中解释).但是有一件事没有提到,而且我一直无法找到文档,那就是如何使用泛型类型信息来实现它们.
我实际上能够很容易地确定泛型类型信息(因为代码将采用现有的字段和/或方法,并且存在完整的泛型类型处理和解析).我只需要为包含泛型类型的类型生成泛型版本.
我希望这就像修改签名的Asm ClassWriter/MethodVisitor调用一样容易,但文档中的一些注释表明它可能不那么容易(因为泛型信息存储在与常规信息不同的位置).
编辑:看起来入口点是"ClassWriter.visitField/Method(....,String signature) - 请注意,它的"描述"包含正常的非泛型类信息,但术语"签名"(在JLS中)具体指的是到泛型 - 包括类型信息.
因此,如果我有一个static final Object CONSTANT = null,由于某种原因,如果我在另一段代码中引用doSomething(CONSTANT)它,它将不会在编译期间嵌入到代码中.因此,不是doSomething(null)在编译之后,而是在编译之后doSomething(CONSTANT).
说我同意:
#!/usr/bin/env python
# encoding: utf-8
class A(object):
pass
Run Code Online (Sandbox Code Playgroud)
现在我拆开它:
python -m dis test0.py
4 0 LOAD_CONST 0 ('A')
3 LOAD_NAME 0 (object)
6 BUILD_TUPLE 1
9 LOAD_CONST 1 (<code object A at 0x1004ebb30, file "test0.py", line 4>)
12 MAKE_FUNCTION 0
15 CALL_FUNCTION 0
18 BUILD_CLASS
19 STORE_NAME 1 (A)
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
现在我在类定义中添加一些语句:
#!/usr/bin/env python
# encoding: utf-8
class A(object):
print 'hello'
1+1
pass
Run Code Online (Sandbox Code Playgroud)
然后我又拆开了:
4 0 LOAD_CONST 0 ('A')
3 LOAD_NAME 0 (object)
6 BUILD_TUPLE …Run Code Online (Sandbox Code Playgroud) 所以我知道存在2个内存区域:Stack和Heap.
我也知道如果你创建一个局部变量,它将存在于堆栈中,而不是堆中.随着我们将数据推入其中,堆栈将会增长,如:

现在我将尝试通过我对你的困惑:
例如,这个简单的Java代码:
public class TestClass {
public static void main(String[] args) {
Object foo = null;
Object bar = null;
}
}
Run Code Online (Sandbox Code Playgroud)
被翻译成这个字节码:
public static void main(java.lang.String[]);
Code:
Stack=1, Locals=3, Args_size=1
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: return
LineNumberTable:
line 5: 0
line 6: 2
line 7: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 args [Ljava/lang/String;
2 3 1 foo Ljava/lang/Object;
4 1 2 bar Ljava/lang/Object;
Run Code Online (Sandbox Code Playgroud)
根据定义,acons_null …
让我们用Eclipse Mars.2捆绑包中的ECJ编译器编译以下代码:
import java.util.stream.*;
public class Test {
String test(Stream<?> s) {
return s.collect(Collector.of(() -> "", (a, t) -> {}, (a1, a2) -> a1));
}
}
Run Code Online (Sandbox Code Playgroud)
编译命令如下:
$ java -jar org.eclipse.jdt.core_3.11.2.v20160128-0629.jar -8 -g Test.java
成功编译后,让我们检查生成的类文件javap -v -p Test.class.最有趣的是为(a, t) -> {}lambda 生成的合成方法:
private static void lambda$1(java.lang.String, java.lang.Object);
descriptor: (Ljava/lang/String;Ljava/lang/Object;)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 a Ljava/lang/String;
0 1 …Run Code Online (Sandbox Code Playgroud) 鉴于我有一个模块的代码对象,我如何获得相应的模块对象?
它看起来像是moduleNames = {}; exec code in moduleNames非常接近我想要的东西.它将模块中声明的全局变量返回到字典中.但是,如果我想要实际的模块对象,我该如何获得它?
编辑:看起来你可以滚动自己的模块对象.模块类型不方便记录,但您可以执行以下操作:
import sys
module = sys.__class__
del sys
foo = module('foo', 'Doc string')
foo.__file__ = 'foo.pyc'
exec code in foo.__dict__
Run Code Online (Sandbox Code Playgroud) 我看到了http://vmkit.llvm.org/项目,但它并不是我想要的.不希望我的代码在另一个VM上运行,而是在.NET和Java的VM上运行.
是否有任何生成.NET CIL和/或Java字节码的LLVM编译器后端?
我们的项目执行一些Java字节码检测.我们偶然发现了一些奇怪的行为.假设以下代码段:
public void a() {
new Integer(2);
}
Run Code Online (Sandbox Code Playgroud)
Oracle的javac将上面的代码编译成以下字节码:
0: new #2; //class java/lang/Integer
3: dup
4: iconst_2
5: invokespecial #3; //Method java/lang/Integer."<init>":(I)V
8: pop
9: return
Run Code Online (Sandbox Code Playgroud)
和Eclipse的编译器进入:
0: new #15; //class java/lang/Integer
3: iconst_2
4: invokespecial #17; //Method java/lang/Integer."<init>":(I)V
7: return
Run Code Online (Sandbox Code Playgroud)
如您所见,Oracle编译器在"new"之后生成"dup",而Eclipse则不然.在这个用例中完全正确,因为根本不使用新创建的Integer实例,因此不需要"dup".
我的问题是: