我被困在Javassisst中。我想将代码放在其他类中的方法中。我有“无方法”例外。当我自己单独启动Test2类时,它可以正常运行,没有任何错误。我认为是Classloader中的问题,因为我试图从SUTTest类调用方法以进行断言,并且我试图使用相同的反射(Javassist)对Class1进行操作。也许两个类加载器彼此冲突,我不知道。我该如何解决该错误?
Class1-Javassist
ClassPool pool = ClassPool.getDefault();
CtClass ctAgent = pool.get("Test2");
CtMethod method = ctAgent.getDeclaredMethod("runTestSamples");
method.insertAfter("targetClass.getMethod(\"setData\", int.class, int.class).invoke(targetInstance, 9, 2);");
// method.insertAt(58, "memoryClassLoader.addDefinition(targetName, instrumented);
memoryClassLoader = new MemoryClassLoader();
targetClass = memoryClassLoader.loadClass(targetName);
targetClass.getMethod(\"setData\", int.class, int.class).invoke(targetInstance, 9, 2);");
ctAgent.toClass();
new Test2().execute();
Class2-Test2
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.jacoco.agent.AgentJar;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.analysis.IClassCoverage;
import org.jacoco.core.data.ExecutionDataStore;
import org.jacoco.core.data.SessionInfoStore;
import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.LoggerRuntime;
import org.jacoco.core.runtime.RuntimeData;
public class Test2 {
private Runnable targetInstance;
public Class<? extends Runnable> targetClass;
private static HashMap<Integer, String> testSamples;
private static HashMap<String, Integer> coverageData;
public String targetName;
public IRuntime runtime;
public Instrumenter instr;
public InputStream original;
public byte[] instrumented;
public RuntimeData data;
public MemoryClassLoader memoryClassLoader;
static Test2 t2 = new Test2();
int a;
public static void main(String[] args) throws Exception {
testSamples = new HashMap<Integer, String>();
coverageData = new HashMap<String, Integer>();
try {
t2.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
public void execute() throws Exception {
testSamples = new HashMap<Integer, String>();
coverageData = new HashMap<String, Integer>();
targetName = SUTClass.class.getName();
runtime = new LoggerRuntime();
instr = new Instrumenter(runtime);
original = getTargetClass(targetName);
instrumented = instr.instrument(original, targetName);
original.close();
data = new RuntimeData();
runtime.startup(data);
memoryClassLoader = new MemoryClassLoader();
memoryClassLoader.addDefinition(targetName, instrumented);
targetClass = (Class<? extends Runnable>) memoryClassLoader.loadClass(targetName);
targetClass.getMethod("f", int.class, int.class).invoke(targetInstance, 2, 9);
runTestSamples(targetClass);
targetInstance = (Runnable) targetClass.newInstance();
// Test samples
targetInstance.run();
final ExecutionDataStore executionData = new ExecutionDataStore();
final SessionInfoStore sessionInfos = new SessionInfoStore();
data.collect(executionData, sessionInfos, false);
runtime.shutdown();
final CoverageBuilder coverageBuilder = new CoverageBuilder();
final Analyzer analyzer = new Analyzer(executionData, coverageBuilder);
original = getTargetClass(targetName);
analyzer.analyzeClass(original, targetName);
original.close();
for (final IClassCoverage cc : coverageBuilder.getClasses()) {
coverageData.put("coveredInstructions", cc.getInstructionCounter().getCoveredCount());
}
System.out.println(coverageData.get("coveredInstructions"));
System.out.println(a);
}
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name, resolve);
}
}
private InputStream getTargetClass(final String name) {
final String resource = '/' + name.replace('.', '/') + ".class";
return getClass().getResourceAsStream(resource);
}
public void runTestSamples(Class<? extends Runnable> target)
throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
SecurityException, ClassNotFoundException {
targetClass.getMethod("f", int.class, int.class).invoke(targetInstance, 2, 9);
// testSamples.put(1, "targetClass.getMethod(\"f\", int.class,
// int.class).invoke(targetInstance, 2, 9)");
// testSamples.put(2, "targetClass.getMethod(\"d\", int.class,
// int.class).invoke(targetInstance, 2, 9)");
}
}
例外
javassist.CannotCompileException: [source error] getMethod(java.lang.String,java.lang.Class,java.lang.Class) not found in java.lang.Class
at javassist.CtBehavior.insertAfter(CtBehavior.java:909)
at javassist.CtBehavior.insertAfter(CtBehavior.java:824)
at Agent3$IdleBehavior.action(Agent3.java:202)
at jade.core.behaviours.Behaviour.actionWrapper(Behaviour.java:344)
at jade.core.Agent$ActiveLifeCycle.execute(Agent.java:1585)
at jade.core.Agent.run(Agent.java:1524)
at java.lang.Thread.run(Unknown Source)
Caused by: compile error: getMethod(java.lang.String,java.lang.Class,java.lang.Class) not found in java.lang.Class
at javassist.compiler.TypeChecker.atMethodCallCore(TypeChecker.java:777)
at javassist.compiler.TypeChecker.atCallExpr(TypeChecker.java:723)
at javassist.compiler.JvstTypeChecker.atCallExpr(JvstTypeChecker.java:170)
at javassist.compiler.ast.CallExpr.accept(CallExpr.java:49)
at javassist.compiler.TypeChecker.atCallExpr(TypeChecker.java:693)
at javassist.compiler.JvstTypeChecker.atCallExpr(JvstTypeChecker.java:170)
at javassist.compiler.ast.CallExpr.accept(CallExpr.java:49)
at javassist.compiler.CodeGen.doTypeCheck(CodeGen.java:266)
at javassist.compiler.CodeGen.atStmnt(CodeGen.java:360)
at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53)
at javassist.compiler.Javac.compileStmnt(Javac.java:578)
at javassist.CtBehavior.insertAfterAdvice(CtBehavior.java:924)
at javassist.CtBehavior.insertAfter(CtBehavior.java:883)
... 6 more
更新
解决方案method.insertAfter("targetClass.getMethod(\"setData\", new Class[] { int.class, int.class }).invoke(targetInstance, new Object[] { 9, 2 });");
我遇到了新问题:
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
Test2.runTestSamples()V @148: aastore
Reason:
Type integer (current frame, stack[5]) is not assignable to 'java/lang/Object'
Current Frame:
bci: @148
flags: { }
locals: { 'Test2', top, null }
stack: { 'java/lang/reflect/Method', 'java/lang/Runnable', '[Ljava/lang/Object;', '[Ljava/lang/Object;', integer, integer }
Bytecode:
0x0000000: 2ab4 002e 12eb 05bd 005d 5903 b200 ed53
0x0000010: 5904 b200 ed53 b600 f02a b400 2c05 bd00
0x0000020: 0359 0305 b800 cd53 5904 1009 b800 cd53
0x0000030: b600 f457 2ab4 002e 1301 3005 bd00 5d59
0x0000040: 03b2 00ed 5359 04b2 00ed 53b6 00f0 2ab4
0x0000050: 002c 05bd 0003 5903 1009 b800 cd53 5904
0x0000060: 05b8 00cd 53b6 00f4 57a7 0003 014d 2ab4
0x0000070: 002e 1301 3005 bd00 5d59 03b2 00ed 5359
0x0000080: 04b2 00ed 53b6 00f0 2ab4 002c 05bd 0003
0x0000090: 5903 1009 5359 0405 53b6 00f4 57b1
Stackmap Table:
same_frame_extended(@108)
答案 0 :(得分:0)
请注意该异常状态
Caused by: compile error: getMethod(java.lang.String,java.lang.Class,java.lang.Class) not found in java.lang.Class ... at javassist.CtBehavior.insertAfter(CtBehavior.java:883)
根据documentation of java.lang.Class的确没有这种签名的方法,the only method with name getMethod
具有以下签名:
getMethod(String name, Class<?>... parameterTypes)
引用http://www.javassist.org/tutorial/tutorial3.html#varargs:
当前,Javassist不直接支持varargs。 ...
public int length(int... args) { return args.length; }
... 要在由Javassist嵌入的编译器编译的源代码中调用此方法,您必须编写:
length(new int[] { 1, 2, 3 });
代替使用varargs机制的此方法调用:
length(1, 2, 3);
类似signature of method invoke
from documentation of class java.lang.Method:
invoke(Object obj, Object... args)
也引用http://www.javassist.org/tutorial/tutorial3.html#boxing:
Java中的装箱和拆箱是语法糖。没有用于装箱或拆箱的字节码。因此,Javassist的编译器不支持它们。例如,以下语句在Java中有效:
Integer i = 3;
因为装箱是隐式执行的。但是,对于Javassist,您必须将值类型从int明确转换为Integer:
Integer i = new Integer(3);
鉴于上述情况,我认为
method.insertAfter("targetClass.getMethod(\"setData\", int.class, int.class).invoke(targetInstance, 9, 2);");
应该更改
method.insertAfter("targetClass"
+ ".getMethod(\"setData\", new Class[] { int.class, int.class })"
+ ".invoke(targetInstance, new Object[] { new Integer(9), new Integer(2) };");