2017-08-25 4 views
0

私はjavassistを使い慣れていません。しかし、他にも多くのことが働いていないようです。 javassistで動作しない空のコンストラクタ(java)を作成します。

Iは、クラス -

public static void editMethodAddEvent(CtClass target, MethodInfo method, CtClass eventClass, int start, int[] bytes, int stacksize, String constructorParameters) throws BadBytecode, NotFoundException, CannotCompileException { 
    target.defrost(); 
    CodeAttribute codeAttribute = method.getCodeAttribute(); 
    CodeIterator iterator = codeAttribute.iterator(); 
    int classID = method.getConstPool().addClassInfo(eventClass); 
    int constrnatID = method.getConstPool().addNameAndTypeInfo("<init>",constructorParameters); 
    int constructID = method.getConstPool().addMethodrefInfo(classID,constrnatID); 
    int callnatID = method.getConstPool().addNameAndTypeInfo("call","()V"); 
    int callID = method.getConstPool().addMethodrefInfo(classID,callnatID); 
    iterator.insertGap(start,bytes.length); 

    for (int i = 0; i < bytes.length; i++) { 
     int byteCode = bytes[i]; 
     if (byteCode >= 0) { 
      iterator.writeByte(byteCode, start+i); 
     } else if (byteCode == -1) { 
      iterator.writeByte(classID,start+i); 
     } else if (byteCode == -2) { 
      iterator.writeByte(constructID, start+i); 
     } else if (byteCode == -3) { 
      iterator.writeByte(callID, start+i); 
     } 
    } 

    if(stacksize > codeAttribute.getMaxStack()) 
     codeAttribute.setMaxStack(stacksize); 
    target.toClass(); 
} 

Iは、コードが実行されたときにイベントを発生するために、方法にバイトコードを追加するには、このコードを使用するにコードを注入する方法を作りました。

最後にクラス(target.toClass())をコンパイルすると、エラーは発生せず正常に動作します。

ただし、以下のコードを使用してメソッド(この場合は空のコンストラクタ)を追加すると、エラーが発生します。

public static Class addEmptyConstructor(Class clazz) throws NotFoundException, CannotCompileException { 
    CtClass ctClass = ClassPool.getDefault().getCtClass(clazz.getName()); 
    ctClass.defrost(); 
    ClassFile classFile = ctClass.getClassFile(); 
    MethodInfo newMethod = new MethodInfo(classFile.getConstPool(), "<init>", "()V"); 
    newMethod.setCodeAttribute(new CodeAttribute(classFile.getConstPool(),1,1,new byte[]{0,0,0,0,0},new ExceptionTable(classFile.getConstPool()))); 
    CodeIterator iterator = newMethod.getCodeAttribute().iterator(); 
    iterator.writeByte(42, 0); 
    iterator.writeByte(183,1); 
    iterator.writeByte(0,2); 
    iterator.writeByte(1,3); 
    iterator.writeByte(177,4); 
    classFile.addMethod(newMethod); 
    return ClassPool.getDefault().toClass(ctClass); 
} 

指定されたエラーは次のとおりです。

javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "mod/TestClass" 
     at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:140) 
     at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:95) 
     at javassist.ClassPool.toClass(ClassPool.java:1143) 
     at javassist.ClassPool.toClass(ClassPool.java:1106) 
     at javassist.ClassPool.toClass(ClassPool.java:1064) 
     at mod.edit.MethodEdit.addEmptyConstructor(MethodEdit.java:113) 
     <other nonrelevant stuff...> 
    Caused by: java.lang.LinkageError: loader (instance of 
    sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for 
    name: "haven/mod/TestClass" 
     at java.lang.ClassLoader.defineClass1(Native Method) 
     at java.lang.ClassLoader.defineClass(Unknown Source) 
     at java.lang.ClassLoader.defineClass(Unknown Source) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
     at java.lang.reflect.Method.invoke(Unknown Source) 
     at javassist.util.proxy.DefineClassHelper.toClass3(DefineClassHelper.java:152) 
     at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:134) 
     ... 10 more 

私の目標は、私はパラメータにかけることなく、任意のオブジェクトのインスタンスを作成できるように、空のコンストラクタを作成することです。具体的には、多くの種類のイベントがあり、パラメータがあります。私はコンパイル時にこのコンストラクタにアクセスできるようにしたくありません。したがって、私は実行時にjavassistを使ってコンストラクタを追加しようとしますが、コンパイルを拒否します。単にメソッドを編集するだけでは大騒ぎにはなりませんが、メソッドを追加すると、私が望むことをするのが拒否されるようです。この問題をどうやって解決するのですか?私は16時間以上をかけて試していました。研究していて、別のコードをテストしていました。何もできません。助けてください!!!!

+0

すでにコンストラクタが空のクラスに空のコンストラクタを追加していませんか? – rakwaht

+0

うん、私は今、テストのための特定の場所でこれを呼び出すだけです。この問題は、バイトコードまたは単純化されたCtMethodまたはCtConstructorメソッドのいずれかを介して、任意のメソッドを追加することによって発生します。しかし、助けてくれてありがとう! – PoisonedPorkchop

+0

私は、コンパイラがデフォルトのものを作成しないように、TestClassにパラメータと冗長コードを持つコンストラクタを持っています。 – PoisonedPorkchop

答えて

0

私は問題の根本を見つけたようです。私は自分のメソッドへのクラス参照を渡していました。クラスから、class.getName()を実行してclassPool.get(文字列が必要)メソッドに入れました。クラスのインスタンスを作成すると、クラスのインスタンスが存在するため、クラスローダーはリロードできないようです。単純に(クラスのインスタンスを作成する)TestClass.classを呼び出すことによって、コードは、これが有効であるかどうかわからない(リロードできなかったこと

MethodEdit.addEmptyConstructor(TestClass.class.getName()) 

:私の限られたテストから、私が経由して外部メソッドを呼び出した場合でも、ようでしたより多くのテストが必要)。私はこれが、メソッドにコードを注入する私の他のメソッドが働いていたと思います。私はCtClassとCtMethodを直接名で取得していましたが、class.getName()で取得しませんでした。メソッドを手動で呼び出すことはできますが(TestClass.classの代わりにパラメータとして "mod.TestClass")、クラスファイルを使用してエラーが発生しない場所でこのソリューションを利用できる場合は、教えてください。それまでは、ファイル名の文字列を手動で入力します。

+0

私が上記の前提に間違っている場合は、私を修正してください。 – PoisonedPorkchop

0

あなたの問題は、コンストラクタではなく新しいメソッドを作成している可能性があります。 hereCtNewConstructorにしようとしましたか?

私はコードスニペットのようなものを試してみるべきだと思います: 最初に、コンストラクタを既に持っている人にコンストラクタを追加しようとしていないことを確認してくださいそれ自身で)、このクラスがインタフェースではないことも確認する必要があります。

次に、リンクしたクラスで新しいコンストラクタを作成し、クラスに追加してください。ここに小さなコードスニペットの例があります

if (!hasDefaultConstructor && !ctClass.isInterface()) { 
     CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass); 
     ctClass.addConstructor(defaultConstructor); 
} 
+0

はい、私はこのメソッドとバイトコードの操作を試みました。変更をコンパイルするためにctClass.toClass()を実行するとすぐに、メソッドまたはコンストラクタが追加されているので、エラーが発生します。 – PoisonedPorkchop

+0

私はあなたのエラーを掘り下げていました。問題は、あなたが2度修正しようとしているクラスをロードしているようです。あなたはエージェント/トランスフォーマーを使ってiwth Javassistを紹介していますか?いつどのようにあなたが編集しようとしているこのCtClassを拾い読みすることができますか? – rakwaht

+0

上記のように、 パブリック静的クラスaddEmptyConstructor(クラスclazz)がNotFoundExceptionをスロー、CannotCompileException { CtClass ctClass = ClassPool.getDefault()getCtClass(clazz.getName())。 ctClassが1回作成されます(このメソッドは1回だけ呼び出されるため)。 clazzパラメータによって作成されます。計器については、明確にする?あなたがそこに何を意味するのかは分かりません。 – PoisonedPorkchop

関連する問題