2017-08-01 9 views
1

私は単純なクラスを生成しており、適切な変数名を挿入できません。 ASMのバージョンは5.2です。ここでJava ASM GeneratorAdapter変数名

はコードです:

package com.test; 

import org.objectweb.asm.*; 
import org.objectweb.asm.commons.GeneratorAdapter; 
import org.objectweb.asm.commons.Method; 

import java.nio.file.Files; 
import java.nio.file.Paths; 

public class Main { 

    public static void main(String[] args) throws Exception { 
     ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 
     String name = "com.test.Sub"; 
     cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name.replace('.', '/'), null, "java/lang/Object", null); 
     Method ctor = Method.getMethod("void <init>()"); 
     GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, ctor, null, null, cw); 
     mg.visitCode(); 
     mg.loadThis(); 
     mg.invokeConstructor(Type.getType(Object.class), ctor); 
     int var = mg.newLocal(Type.INT_TYPE); 
     mg.push(42.42); 
     mg.storeLocal(var); 
     Label varLabel = mg.mark(); 
     mg.returnValue(); 
     Label endLabel = mg.mark(); 
     mg.visitLocalVariable("x", "D", null, varLabel, endLabel, var); 
     mg.endMethod(); 
     cw.visitEnd(); 
     byte[] bytes = cw.toByteArray(); 
     Files.write(Paths.get(name + ".class"), bytes); 
    } 

} 

私はコード生成を簡素化するためにGeneratorAdapterを使用しています。 GeneratorAdapterLocalVariablesSorterから継承されているので、その中からnewLocal(Type)メソッドを使用することが許可されているものとします。

変数の名前を除いて、発行されるバイトコードに問題はありません。 visitLocalVariable()メソッドが呼び出されると、変数に名前を代入するのではなく、バイトコードに新しい名前を作成します。

放射されたバイトコード:

// class version 52.0 (52) 
// access flags 0x1 
public class com/test/Sub { 
    // access flags 0x1 
    public <init>()V 
    ALOAD 0 
    INVOKESPECIAL java/lang/Object.<init>()V 
    LDC 42.42 
    DSTORE 1 
    L0 
    RETURN 
    L1 
    LOCALVARIABLE x D L0 L1 3 
    MAXSTACK = 2 
    MAXLOCALS = 5 
} 

私はvisitLocalVariable()newLocal()コールにより提供されたものと同じ変数インデックスを使用しています。しかし、バイトコードマップインデックスでは、1の代わりに3です。変数がintのような「より短い」タイプの場合、インデックスは2であり、それでも1ではありません。

私の所見から、これは以下の理由から起こります。 LocalVariablesSorterは、古い変数インデックスから新しいインデックスへのマッピングを維持します。また、メソッドvisitLocalVariableをオーバーライドし、呼び出しを訪問者チェーンに委任する前に、マッピングからnewIndexを計算します。 newIndexは別のプライベートメソッドremap()によって計算されます。このメソッドは、指定された変数に対してマッピングがすでに存在するかどうかをチェックし、存在しない場合は新しいマッピングが作成されます。私が見ているように、newLocal()メソッドはマッピングに何も追加しないという問題があります。

また、私はstoreInsn()GeneratorAdapterにおける代表者visitVarInsn()コールチェーンダウンの代わりに、LocalVariablesSorterの実装を呼び出すことASMソースから見ることができます。 LocalVariablesSorterの実装であるため、可変インデックスに対してremap()メソッドが呼び出され、マッピングが更新されます。

は、したがって、私の質問はGeneratorAdapterを使用する方法であるので、変数が放出されたバイトコードまたはそれらがどのように一緒に正常に動作してチェーンにLocalVariablesSorterGeneratorAdapterを組み合わせることで、適切に命名されていますか?

+0

のオブジェクトをそのまま使用することができます。次の 'visit ...'呼び出しの動作を変更するのは、 'LocalVariablesSorter'の*目的*です。明らかに、変更されるべきでないすべての操作は、 'GeneratorAdapter'によって導入されたメソッドのように' LocalVariablesSorter'をバイパスする必要があります。 – Holger

+0

それで 'GeneratorAdapter'が親の機能を提供できない場合、なぜ' LocalVariablesSorter'から継承されるのですか? – alllex

+0

私に尋ねないで、私はこの決定をした人ではない。しかし、アダプターではなくターゲットビジターにそのメソッドを呼び出す際の問題はどこにありますか? – Holger

答えて

0

GeneratorAdapterは、その目的は、すべての訪問者の呼び出しを適合させることであるLocalVariablesSorterを、拡張しているので、ビジターAPIの一部であるすべてのメソッドはGeneratorAdapterにより導入された専用の方法とは異なり、適応ます。この設計では、既存のメソッドに新しいコードを挿入することができます。既存のメソッドでは、古いコードがvisitor APIを通じて報告されます。

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 
String name = "com.test.Sub"; 
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, 
     name.replace('.', '/'), null, "java/lang/Object", null); 
Method ctor = Method.getMethod("void <init>()"); 
MethodVisitor direct = cw.visitMethod(
     Opcodes.ACC_PUBLIC, ctor.getName(), ctor.getDescriptor(), null, null); 
GeneratorAdapter mg = new GeneratorAdapter(Opcodes.ACC_PUBLIC, ctor, direct); 
mg.visitCode(); 
mg.loadThis(); 
mg.invokeConstructor(Type.getType(Object.class), ctor); 
int var = mg.newLocal(Type.DOUBLE_TYPE); 
mg.push(42.42); 
mg.storeLocal(var); 
Label varLabel = mg.mark(); 
mg.returnValue(); 
Label endLabel = mg.mark(); 
direct.visitLocalVariable("x", "D", null, varLabel, endLabel, var); 
mg.endMethod(); 
cw.visitEnd(); 
byte[] bytes = cw.toByteArray(); 
Files.write(Paths.get(name + ".class"), bytes); 

これはここで、直接ターゲットに取り組んで代替が混乱することができますので:だからビジターAPIの一部である方法visitLocalVariableは、LocalVariablesSorterをバイパスして、ターゲットMethodVisitorに呼び出さなければなりません

MethodVisitorGeneratorAdapterのような便利なラッパーはまったくありません。あなたが()Vを使用して快適に感じていない場合はそれは...それは少しより多くの知識を必要とするものの、しかし、それはJavaバイトコードとクラスファイルを扱うとき、開発者は、とにかく持つべき知識だ、より

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 
String name = "com.test.Sub"; 
String superClName = "java/lang/Object", ctorName = "<init>", ctorDesc = "()V"; 
cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name.replace('.','/'), null, superClName, null); 
MethodVisitor direct = cw.visitMethod(Opcodes.ACC_PUBLIC, ctorName, ctorDesc, null, null); 
direct.visitCode(); 
// "this" is alway 0 (zero) and for parameterless methods the next var location is 1 (one) 
int thisVar = 0, var = 1; 
direct.visitVarInsn(Opcodes.ALOAD, thisVar); 
direct.visitMethodInsn(Opcodes.INVOKESPECIAL, superClName, ctorName, ctorDesc, false); 
direct.visitLdcInsn(42.42); 
Label varLabel = new Label(), endLabel = new Label(); 
direct.visitVarInsn(Opcodes.DSTORE, var); 
direct.visitLabel(varLabel); 
direct.visitInsn(Opcodes.RETURN); 
direct.visitLabel(endLabel); 
direct.visitLocalVariable("x", "D", null, varLabel, endLabel, var); 
direct.visitMaxs(-1, -1);// no actual values, using COMPUTE_FRAMES 
direct.visitEnd(); 
cw.visitEnd(); 
byte[] bytes = cw.toByteArray(); 
Files.write(Paths.get(name + ".class"), bytes); 

複雑ではありませんパラメータなしのvoidメソッドの場合は、前と同じようにType.getMethodDescriptor(Type.VOID_TYPE)