私は太陽の上でBをAに変換すると何の問題も見られませんでした。1.6.0 _ 15と1.5.0 _ 17 JRE(私はASMを使用しました)。私は変換コードを外部で実行し、結果のクラスを検査することによって(javapなどで)変換コードを再確認します。私はあなたのエージェントの前にAが何らかの理由でロードされていないことを確認するために、あなたのクラスパス設定をチェックしたいと考えています(おそらく、premainをgetAllLoadedClassesでチェックインしてください)。
EDIT:
あなたはこのようなあなたのエージェントにクラスA
をロードする場合:...
Class.forName("A");
その後、例外がスローされます。
Exception in thread "main" java.lang.NoSuchMethodError: B.print()V
これは理にかなっています - A
はエージェントの依存関係になり、エージェントがインスツルメントすることは意味がありませんそれ自身のコード。スタックオーバーフローの原因となる無限ループが発生します。そのため、A
はClassFileTransformer
で処理されません。
完全性のために、ここに問題なく動作するテストコードがあります。前述のように、それはASMライブラリに依存します。
剤:A
ため
public class ClassModifierAgent implements ClassFileTransformer {
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("transform: " + className);
if ("A".equals(className)) {
return new AModifier().modify(classfileBuffer);
}
if ("B".equals(className)) {
return new BModifier().modify(classfileBuffer);
}
return classfileBuffer;
}
/** Agent "main" equivalent */
public static void premain(String agentArguments,
Instrumentation instrumentation) {
instrumentation.addTransformer(new ClassModifierAgent());
}
}
方法インジェクター:
public class AModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new AVisitor(cv);
}
private static class AVisitor extends ClassAdapter {
public AVisitor(ClassVisitor cv) { super(cv); }
@Override
public void visitEnd() {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "print", "()V",
null, null);
mv.visitCode();
mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
"Ljava/io/PrintStream;");
mv.visitLdcInsn("X");
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
"println", "(Ljava/lang/String;)V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
super.visitEnd();
}
}
}
B
ための方法の代替:
public class BModifier extends Modifier {
@Override
protected ClassVisitor createVisitor(ClassVisitor cv) {
return new BVisitor(cv);
}
class BVisitor extends ClassAdapter {
public BVisitor(ClassVisitor cv) { super(cv); }
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
if ("foo".equals(name)) {
MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC, "foo", "()V",
null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "B", "print", "()V");
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
return new EmptyVisitor();
} else {
return super.visitMethod(access, name, desc, signature, exceptions);
}
}
}
}
共通ベースコード:
public abstract class Modifier {
protected abstract ClassVisitor createVisitor(ClassVisitor cv);
public byte[] modify(byte[] data) {
ClassReader reader = new ClassReader(data);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
ClassVisitor visitor = writer;
visitor = new CheckClassAdapter(visitor);
visitor = createVisitor(visitor);
reader.accept(visitor, 0);
return writer.toByteArray();
}
}
いくつかの目に見える結果として、System.out.println('X');
〜A.print()
を追加しました。このコードで実行し
:あなたの答えのための
transform: MainInstrumented
transform: B
transform: A
X
Thxを:
は...それは、この出力を生成します。 問題を別の観点から見てみましょう。 AとBの両方のクラスが空であるとします。トランスフォームメソッドの最初と最後にログを追加すると、どのクラスがエージェントによってロードされているのか、どの時点にあるのかがわかります。 は、実行:新しいBを() を結果は次のようになります。そのクラスBは、エージェントによってロードされ、その後クラスA され、手動でウルエージェントにClassloader.loadClass()メソッドを使用して負荷クラスAに今すぐ試すことができますBがそれを渡すとき? 結果は次のとおりです。Bはエージェント経由でロードされましたが、Aは実行されませんでした。 ? 乾杯 christoph –
まずは、お返事と努力に感謝します。私はそれを感謝します。 Uが正しいです!私はJavassistをあらゆる変換に使用しました。 Javassistは変更を再コンパイルします。この結果、上記のコンパイルエラーが発生します。 ASMはバイトコードで直接動作し、再コンパイルなどの必要はありません。 エージェント内のクラスの読み込みについて:私はあなたの答えを理解していません。私はEclipseを使用し、エージェントに追加する場合: > //クラス名がBの場合 > Class.forName( "A"); と私はデバッガの実行に従います。例外はスローされず、エージェントは入力されません。 –
もう一度、あなたは正しいです。あなたが言及した例外は正しいです。物事をより簡単にしましょう:両方のクラスにはメソッドがなく、まったく計装されることを意図していません。エージェントが行う唯一の考えは、クラスBがエージェントを渡す場合、Class.forName( "A");呼び出されるものとする。これは、クラスローディングの適切な順序を引き起こすはずです(最初にA、次にB)。この例を試してみてください。あなたはBだけがエージェントを渡すことがわかります!だから、エージェントの一員として呼び出されたときにAがエージェントを渡さない理由が生じる。 –