【发布时间】:2015-01-28 08:20:51
【问题描述】:
当我查看使用 @groovy.transform.CompileStatic 编译的 Groovy 脚本的字节码时,我看到了一些奇怪的结果
这是复制问题的最简单的类:
@groovy.transform.CompileStatic
class ScriptTestClass{
void test_method(String x,String y,String z){
x = "foo";
}
}
当编译为字节码时,我得到了这个字节码(javap -c -v ScriptTestClass.class 的结果,仅针对主题方法编辑):
javap -c -v ScriptTestClass.class 的结果(虽然只是主题方法):
public void test_method(java.lang.String, java.lang.String, java.lang.String);
flags: ACC_PUBLIC
Code:
stack=1, locals=5, args_size=4
0: ldc #32 // String foo
2: astore 4
4: aload 4
6: astore_1
7: aload 4
9: pop
10: return
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this LScriptTestClass;
0 10 1 x Ljava/lang/String;
0 10 2 y Ljava/lang/String;
0 10 3 z Ljava/lang/String;
LineNumberTable:
line 4: 0
显然,ASTORE / ALOAD 4 在这里不合适。事实上,它们看起来是多余的。如果这些被删除,则字节码是正确的。正确的字节码(当我在 Java 类中编写相同的代码时得到的是:
public void test_method(java.lang.String, java.lang.String, java.lang.String);
flags: ACC_PUBLIC
Code:
stack=1, locals=4, args_size=4
0: ldc #7 // String foo
2: astore_1
3: return
LineNumberTable:
line 26: 0
line 27: 3
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 this Ltesting/ScriptTestClass;
0 4 1 x Ljava/lang/String;
0 4 2 y Ljava/lang/String;
0 4 3 z Ljava/lang/String;
为什么@CompileStatic 会生成访问局部变量槽 4 的字节码?
我正在使用 d Groovy 版本 2.3.7 和 Java 1.7
如果相关,我用来从 Groovy 源获取字节的代码如下。
此代码将脚本解析为一个类并获取字节:
protected ClassNode loadGroovyTestClassAsBytecode(String classSource) throws Exception{
ClassNode classNode = new ClassNode();
String scriptName = "ScriptTestClass.groovy";
Class groovyClass = groovyClassLoader.parseClass(classSource,scriptName);
String className = groovyClass.getName() + ".class";
byte[] classBytes = groovyClassLoader.getClassBytes(className);
}
上面的'groovyClassLoader'是以下类加载器的一个实例,它允许在加载后获取字节:
public class CachingGroovyClassLoader extends GroovyClassLoader {
private Map<String, byte[]> classBytes = new HashMap<String, byte[]>();
public CachingGroovyClassLoader(){
}
public CachingGroovyClassLoader(ClassLoader parent){
super(parent);
}
public byte[] getClassBytes(String name) throws IOException{
return IOUtils.toByteArray(getResourceAsStream(name));
}
@Override
public InputStream getResourceAsStream(String name) {
if (classBytes.containsKey(name)) {
return new ByteArrayInputStream(classBytes.get(name));
}
return super.getResourceAsStream(name);
}
@Override
protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
// These six lines copied from Groovy itself, with the intention to
// return a subclass
InnerLoader loader = AccessController.doPrivileged(new PrivilegedAction<InnerLoader>() {
public InnerLoader run() {
return new InnerLoader(CachingGroovyClassLoader.this);
}
});
return new BytecodeClassCollector(classBytes, loader, unit, su);
}
public static class BytecodeClassCollector extends ClassCollector {
private final Map<String, byte[]> classBytes;
public BytecodeClassCollector(Map<String, byte[]> classBytes, InnerLoader loader, CompilationUnit unit,
SourceUnit su) {
super(loader, unit, su);
this.classBytes = classBytes;
}
@Override
protected Class<?> onClassNode(ClassWriter classWriter, ClassNode classNode) {
classBytes.put(classNode.getName() + ".class", classWriter.toByteArray());
return super.onClassNode(classWriter, classNode);
}
}
}
编辑:
我有一个发布原始类文件的请求,但我不知道该怎么做。这里 还有几件事可能会有所帮助:
javap ScriptTestClass.class 的结果:
Compiled from "ScriptTestClass.groovy"
public class ScriptTestClass implements groovy.lang.GroovyObject {
public static transient boolean __$stMC;
public static long __timeStamp;
public static long __timeStamp__239_neverHappen1417366615662;
public ScriptTestClass();
public void test_method(java.lang.String, java.lang.String, java.lang.String);
public java.lang.Object this$dist$invoke$1(java.lang.String, java.lang.Object);
public void this$dist$set$1(java.lang.String, java.lang.Object);
public java.lang.Object this$dist$get$1(java.lang.String);
protected groovy.lang.MetaClass $getStaticMetaClass();
public groovy.lang.MetaClass getMetaClass();
public void setMetaClass(groovy.lang.MetaClass);
public java.lang.Object invokeMethod(java.lang.String, java.lang.Object);
public java.lang.Object getProperty(java.lang.String);
public void setProperty(java.lang.String, java.lang.Object);
public static void __$swapInit();
static {};
public void super$1$wait();
public java.lang.String super$1$toString();
public void super$1$wait(long);
public void super$1$wait(long, int);
public void super$1$notify();
public void super$1$notifyAll();
public java.lang.Class super$1$getClass();
public java.lang.Object super$1$clone();
public boolean super$1$equals(java.lang.Object);
public int super$1$hashCode();
public void super$1$finalize();
static java.lang.Class class$(java.lang.String);
}
【问题讨论】:
-
你能发布原始类文件吗?
-
您好,感谢您的帮助!我不知道如何在这里上传完整的文件。我确实有类文件。您希望它采用什么格式?还是我应该 PM 给你?
-
这是 groovy-dev 邮件列表中最好讨论的内容。 “这对任何人都有意义吗”并不是一个好的 Stack Overflow 问题。
-
好的。有没有另一种方式来表达这个问题,这确实使它成为一个很好的 SO 问题? IE,问题的表述方式是问题,还是太详细而无法讨论?