使用Javassist 3.20.0.GA,我试图将代码注入空循环。
我尝试的所有内容,在尝试创建修改后的类的新实例时,我一直在运行java.lang.VerifyError错误。
我试图将问题隔离到无法成功运行的小程序。
if object_id('tempdb..#tt') is not null drop table #tt
create table #tt (name varchar(45), [status] varchar(16), [Date] varchar(16), [Time] varchar(16))
insert into #tt (name, [status], [date], [time]) values
('Employee 1','Check In','20/11/2016','03:52:15 PM'),
('Employee 1','Check Out','21/11/2016','12:08:50 AM'),
('Employee 1','Check In','21/11/2016','03:45:37 PM'),
('Employee 1','Check Out','22/11/2016','12:14:25 PM'),
('Employee 1','Check In','22/11/2016','03:41:34 PM'),
('Employee 1','Check Out','23/11/2016','12:04:53 PM')
;with cte as(
select
*,
ROW_NUMBER() over (partition by [Name] order by convert(datetime,[Date] + ' ' + convert(varchar(8),cast([time] as time)),103)) as rownum
from #tt)
select
ci.Name,
ci.[Date] as CheckInDate,
ci.[Time] as CheckInTime,
co.[Date] as CheckOutDate,
co.[Time] as CheckOutTime
from
cte ci
inner join cte co on
co.rownum = ci.rownum + 1
where ci.status = 'Check In'
--Results
Name CheckInDate CheckInTime CheckOutDate CheckOutTime
Employee 1 20/11/2016 03:52:15 PM 21/11/2016 12:08:50 AM
Employee 1 21/11/2016 03:45:37 PM 22/11/2016 12:14:25 PM
Employee 1 22/11/2016 03:41:34 PM 23/11/2016 12:04:53 PM
当我运行此程序时,以下内容将写入控制台..
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.CodeIterator;
import javassist.bytecode.InstructionPrinter;
import javassist.bytecode.MethodInfo;
import javassist.compiler.Javac;
public class EmptyLoopTest {
private void testEmptyLoopModification() throws Exception {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get(EmptyLoopTest.class.getName() + "$EmptyLoopClass");
CtMethod m = cc.getDeclaredMethod("emptyLoopMethod");
printMethod("Before modifications", m);
MethodInfo methodInfo = m.getMethodInfo();
CodeAttribute ca = methodInfo.getCodeAttribute();
CodeIterator ci = ca.iterator();
Javac jv = new Javac(cc);
jv.compileStmnt("System.out.println(\"injected into loop\");");
Bytecode b = jv.getBytecode();
adjustCodeAttributeIfNeeded(b, ca);
ci.insertAt(0, b.get());
printMethod("After modifications", m);
Class c = cc.toClass();
logInfo("Attempting to create instance of modified class");
c.newInstance();
}
private void adjustCodeAttributeIfNeeded(Bytecode b, CodeAttribute ca){
int locals = b.getMaxLocals();
int stack = b.getMaxStack();
if(stack > ca.getMaxStack()) {
ca.setMaxStack(stack);
}
if(locals > ca.getMaxLocals()) {
ca.setMaxLocals(locals);
}
}
private void printMethod(String title, CtMethod m){
logInfo(title);
InstructionPrinter instructionPrinter = new InstructionPrinter(System.out);
instructionPrinter.print(m);
}
private void logInfo(String message){
System.out.println("");
System.out.println("------" + message);
}
public class EmptyLoopClass {
public void emptyLoopMethod() {
for(;;){
}
}
}
public static void main(String[] args) throws Exception {
// have errors written to sysout so all output from this program is in order
System.setErr(System.out);
new EmptyLoopTest().testEmptyLoopModification();
}
}
如果一切都按预期工作,我会在修改到goto 0而不是它当前显示的内容之后期望goto指令,转到8.它就好像StackMapTable不是&#39 ;当我调用javassist.bytecode.CodeIterator#insertAt时适当调整。
有谁能发现我在这里做错了什么?
谢谢,
埃里克
答案 0 :(得分:0)
你创建类似c.newInstance();
的类,所以它应该有0参数构造函数 - 是静态内部类
public static class EmptyLoopClass {
public void emptyLoopMethod() {
for(;;){
}
}
您应该在修改代码后致电methodInfo.rebuildStackMap(cp);
。 JVM使用StackMap验证类文件。
无论如何,你最终会得到字节码
3: ldc #34 = "injected into loop"
5: invokevirtual #40 = Method java.io.PrintStream.println((Ljava/lang/String;)V)
8: goto 8
此代码不在循环内,它在循环之前。实际循环是单指令8: goto 8
,如果要在其中注入指令,则必须生成一些额外的循环代码。