2017-07-20 12 views
0

私は既に存在するクラスのASMを使用して簡単なゲッターメソッドを生成しました。BadローカルStackMapTable

mv = cn.visitMethod(access,     // public method 
        "get_" + f.name,   // name 
        "()Ljava/lang/String;", // descriptor 
        null,      // signature (null means not generic) 
        null);      // exceptions (array of strings 

mv.visitCode(); 
mv.visitVarInsn(Opcodes.ALOAD, 0); 
mv.visitFieldInsn(Opcodes.GETFIELD, cn.name, f.name, f.desc); 
mv.visitInsn(Opcodes.ARETURN); 
mv.visitMaxs(0, 0); 

次に、クラスを生成しました。

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 
cn.accept(cw); 

ここで、私はcw.toByteArray()でクラスにアクセスできます。 問題は、私は、負荷にStackMapTableが適切ではないので、私はエラーを取得するクラスをしようとすると、(それは私がClassWriter.COMPUTE_FRAMESを使用している、あるべき?)である

エラー

Exception in thread "main" java.lang.VerifyError: Bad local variable type 
Exception Details: 
    Location: 
    a.get_c()Ljava/lang/String; @0: aload_0 
    Reason: 
    Type top (current frame, locals[0]) is not assignable to reference type 
    Current Frame: 
    bci: @0 
    flags: { } 
    locals: { } 
    stack: { } 
    Bytecode: 
    0x0000000: 2ab4 0012 b0 

後これはCheckClassAdapterを追加して、何が間違っているかを確認します。

CheckClassAdapter ca = new CheckClassAdapter(cw, false); //Check data flow 
ClassReader cr = new ClassReader(cw.toByteArray()); 
ca.verify(cr, new GenericClassLoader(), true, new PrintWriter(new PrintStream(System.out))); 

出力は次のとおりです。

org.objectweb.asm.tree.analysis.AnalyzerException: Error at instruction 0: Expected an object reference, but found . 
    at org.objectweb.asm.tree.analysis.Analyzer.analyze(Unknown Source) 
    at org.objectweb.asm.util.CheckClassAdapter.verify(Unknown Source) 
    at me.ffy00.ClassGenerator2.generateSkeleton(ClassGenerator2.java:85) 
    at me.ffy00.Test.main(Test.java:25) 
Caused by: org.objectweb.asm.tree.analysis.AnalyzerException: Expected an object reference, but found . 
    at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(Unknown Source) 
    at org.objectweb.asm.tree.analysis.BasicVerifier.copyOperation(Unknown Source) 
    at org.objectweb.asm.tree.analysis.Frame.execute(Unknown Source) 
    ... 4 more 
get_c()Ljava/lang/String; 
00000 . : :  ALOAD 0 
00001 ? :  GETFIELD a.c : Ljava/lang/String; 
00002 ? :  ARETURN 

私のメソッドget_c()を見ればわかりますか?いくつかのオペコード(多分最初のオペコードが理解されていないため?)

00000 . : :  ALOAD 0 
00001 ? :  GETFIELD a.c : Ljava/lang/String; 
00002 ? :  ARETURN 

に私は本当に今、それが何を意味するかんが、それははずのように方法は見えません。私は簡単なゲッタークラスをコンパイルし、このメソッドはこのように見えます。

00000 Getter : :  ALOAD 0 
00001 Getter : Getter :  GETFIELD me/ffy00/Getter.a : Ljava/lang/String; 
00002 Getter : String :  ARETURN 

私はすでにStackMapTableはまだ実装されていなかったので、50にクラスのバージョンを設定しようとしましたが、何も変更する表示されません。私はClassNodeのバージョン変数を50に設定しました。 このようにしてください。

cn.version = 50; 

そして、このクラスはjavapで次のようになります。

public class a 
    minor version: 0 
    major version: 50 
    flags: ACC_PUBLIC, ACC_SUPER 

はまた、スタックサイズとローカル番号を含め、私は私の方法を得ることができてjavap使用が、方法はStackMapTableを持っていない(はい、私は-v使用している、StackMapTableは、他の方法で表示されます)。

static java.lang.String get_c(); 
    descriptor:()Ljava/lang/String; 
    flags: ACC_STATIC 
    Code: 
     stack=1, locals=1, args_size=0 
     0: aload_0 
     1: getfield  #18     // Field c:Ljava/lang/String; 
     4: areturn 

私の変数は次のようになります。

private static final java.lang.String c; 
    descriptor: Ljava/lang/String; 
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL 

いくつかは、方法を修正するために必要なことを指摘できますか。 StackMapTableをASMで修正する方法に関する有益な情報が見つかりませんでした。

+2

あなたのコメントは、 'access'は' public method'ですが、 'javap'はあなたのメソッドがdefault-visibility(別名パッケージ)_and static_だと考えています。非静的メソッドだけがarg0に 'this'を取得します。 –

+0

はい、ありがとうございます。私はそれを完全に忘れてしまった。メソッド本体は、静的ゲッターでは異なる必要があります。 – FFY00

答えて

0

dave_thompson_085で指摘したように、問題は、あなたが誤ってメソッドが静的作られたということですが、あなたはまだthisにアクセスしています。または、バイトコードでは、オブジェクトをスロット0からロードしようとしていますが、メソッドの開始時にはスロット0には何もありません。

ASMは、指定したコードに基づいてスタックフレームを生成します。無効なコードに対して妥当なスタックフレームを魔法のように生成することはできません。

+0

タクス!私はそれを忘れてしまった。非静的ゲッターメソッドのためにロードする必要がある理由を私に説明できますか?私はまだバイトコードを学んでいますが、多くの情報はありません。 – FFY00

+0

@ FFY00非静的フィールドにアクセスしています。つまり、フィールドにアクセスするためのオブジェクトが必要です。 Javaでも同じことが言えますが、「this」は暗黙的です。 – Antimony

+0

この特定の場合、メソッドにはブランチが含まれていないため、ASMはスタックフレームをまったく生成しません。 'VerifyError'が参照する最初のフレームは、メソッドシグネチャによって暗示されます。 @ FFY00 'VerifyError'を慎重に見れば、" StackMapTable "という言葉はメッセージのどこにも現れないことに気付くでしょう。 – Holger