package scouter.agent.asm.asyncsupport; import scouter.agent.ClassDesc; import scouter.agent.Configure; import scouter.agent.asm.ILASM; import scouter.agent.asm.util.AsmUtil; import scouter.agent.trace.TraceMain; import scouter.org.objectweb.asm.*; import scouter.org.objectweb.asm.commons.LocalVariablesSorter; /** * @author Gun Lee (gunlee01@gmail.com) on 2017. 2. 24. */ public class LambdaFormASM implements ILASM, Opcodes { private Configure conf = Configure.getInstance(); @Override public ClassVisitor transform(ClassVisitor cv, String className, ClassDesc classDesc, String lambdaMethodName, String lambdaMethodDesc, String factoryMethodName, String factoryMethodDesc) { // if (conf.hook_async_servlet_enabled == false) { // return cv; // } return new LambdaFormCV(cv, className, lambdaMethodName, lambdaMethodDesc, factoryMethodName, factoryMethodDesc); } } class LambdaFormCV extends ClassVisitor implements Opcodes { String className; String lambdaMethodName; String lambdaMethodDesc; String factoryMethodName; String factoryMethodDesc; public LambdaFormCV(ClassVisitor cv, String className, String lambdaMethodName, String lambdaMethodDesc, String factoryMethodName, String factoryMethodDesc) { super(ASM4, cv); this.className = className; this.lambdaMethodName = lambdaMethodName; this.lambdaMethodDesc = lambdaMethodDesc; this.factoryMethodName = factoryMethodName; this.factoryMethodDesc = factoryMethodDesc; } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); if (name.equals(lambdaMethodName) && desc.equals(lambdaMethodDesc)) { String fullName = AsmUtil.makeMethodFullName(className, name, desc); if((access & ACC_STATIC) != 0) return mv; return new LambdaMV(access, name, desc, mv, fullName, Type.getArgumentTypes(desc), className); } else if (name.equals(factoryMethodName) && desc.equals(factoryMethodDesc)) { return new FacotoryMV(access, name, desc, mv); } else { return mv; } } } class LambdaMV extends LocalVariablesSorter implements Opcodes { private static final String TARGET = TraceMain.class.getName().replace('.', '/'); private static final String START_METHOD = "startAsyncPossibleService"; private static final String START_METHOD_DESC = "(" + "Ljava/lang/Object;" + "Ljava/lang/String;" + "Ljava/lang/String;" + "Ljava/lang/String;" + "Ljava/lang/String;" + "Ljava/lang/Object;" + "[Ljava/lang/Object;" + ")Ljava/lang/Object;"; private static final String END_METHOD = "endAsyncPossibleService"; private static final String END_METHOD_DESC = "(" + "Ljava/lang/Object;" + "Ljava/lang/Object;" + "Ljava/lang/Throwable;" + ")V"; private Label startFinally = new Label(); private String name; private String desc; private Type[] paramTypes; private Type returnType; private String fullName; private int statIdx; private String className; public LambdaMV(int access, String name, String desc, MethodVisitor mv, String fullName, Type[] paramTypes, String className) { super(ASM4, access, desc, mv); this.name = name; this.desc = desc; this.fullName = fullName; this.paramTypes = paramTypes; this.returnType = Type.getReturnType(desc); this.className = className; } @Override public void visitCode() { int sidx = 1; mv.visitVarInsn(Opcodes.ALOAD, 0); AsmUtil.PUSH(mv, fullName); AsmUtil.PUSH(mv, className); AsmUtil.PUSH(mv, name); AsmUtil.PUSH(mv, desc); mv.visitVarInsn(Opcodes.ALOAD, 0); int arrVarIdx = newLocal(Type.getType("[Ljava/lang/Object;")); AsmUtil.PUSH(mv, paramTypes.length); mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); mv.visitVarInsn(Opcodes.ASTORE, arrVarIdx); for (int i = 0; i < paramTypes.length; i++) { Type tp = paramTypes[i]; mv.visitVarInsn(Opcodes.ALOAD, arrVarIdx); AsmUtil.PUSH(mv, i); AsmUtil.loadForArrayElement(mv, tp, sidx); mv.visitInsn(Opcodes.AASTORE); sidx += tp.getSize(); } mv.visitVarInsn(Opcodes.ALOAD, arrVarIdx); mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, START_METHOD, START_METHOD_DESC,false); statIdx = newLocal(Type.getType(Object.class)); mv.visitVarInsn(Opcodes.ASTORE, statIdx); mv.visitLabel(startFinally); mv.visitCode(); } @Override public void visitInsn(int opcode) { if ((opcode >= IRETURN && opcode <= RETURN)) { capReturn(); } mv.visitInsn(opcode); } private void capReturn() { Type tp = returnType; if (tp == null || tp.equals(Type.VOID_TYPE)) { mv.visitInsn(Opcodes.ACONST_NULL); mv.visitVarInsn(Opcodes.ALOAD, statIdx); mv.visitInsn(Opcodes.ACONST_NULL); mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, END_METHOD, END_METHOD_DESC, false); return; } switch (tp.getSort()) { case Type.DOUBLE: case Type.LONG: mv.visitInsn(Opcodes.DUP2); break; default: mv.visitInsn(Opcodes.DUP); } // TODO method return test dup and store // int rtnIdx = newLocal(tp); // mv.visitVarInsn(Opcodes.ASTORE, rtnIdx); // mv.visitVarInsn(Opcodes.ALOAD, rtnIdx); mv.visitVarInsn(Opcodes.ALOAD, statIdx);// stat mv.visitInsn(Opcodes.ACONST_NULL);// throwable mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, END_METHOD, END_METHOD_DESC, false); } @Override public void visitMaxs(int maxStack, int maxLocals) { Label endFinally = new Label(); mv.visitTryCatchBlock(startFinally, endFinally, endFinally, null); mv.visitLabel(endFinally); mv.visitInsn(DUP); int errIdx = newLocal(Type.getType(Throwable.class)); mv.visitVarInsn(Opcodes.ASTORE, errIdx); mv.visitInsn(Opcodes.ACONST_NULL);// return mv.visitVarInsn(Opcodes.ALOAD, statIdx); mv.visitVarInsn(Opcodes.ALOAD, errIdx); mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, END_METHOD, END_METHOD_DESC,false); mv.visitInsn(ATHROW); mv.visitMaxs(maxStack + 8, maxLocals + 2); } } class FacotoryMV extends LocalVariablesSorter implements Opcodes { private static final String TARGET = TraceMain.class.getName().replace('.', '/'); private static final String END_METHOD = "asyncPossibleInstanceInitInvoked"; private static final String END_METHOD_DESC = "(" + "Ljava/lang/Object;" + ")V"; String name; String desc; private Type returnType; public FacotoryMV(int access, String name, String desc, MethodVisitor mv) { super(ASM4, access, desc, mv); this.name = name; this.desc = desc; this.returnType = Type.getReturnType(desc); } // @Override // public void visitCode() { // mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // mv.visitLdcInsn("[factory method called!]" + name + desc); // mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // // mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); // mv.visitMethodInsn(INVOKESTATIC, "java/lang/Thread", "currentThread", "()Ljava/lang/Thread;", false); // mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Thread", "getName", "()Ljava/lang/String;", false); // mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); // } @Override public void visitInsn(int opcode) { if ((opcode >= IRETURN && opcode <= RETURN)) { mv.visitInsn(DUP); // Type tp = returnType; // int i = newLocal(tp); // mv.visitVarInsn(Opcodes.ASTORE, i); // mv.visitVarInsn(Opcodes.ALOAD, i); // mv.visitVarInsn(Opcodes.ALOAD, i); mv.visitMethodInsn(Opcodes.INVOKESTATIC, TARGET, END_METHOD, END_METHOD_DESC, false); } mv.visitInsn(opcode); } }