001    package railo.runtime.instrumentation;
002    
003    import java.io.IOException;
004    import java.lang.instrument.ClassDefinition;
005    import java.lang.instrument.Instrumentation;
006    import java.lang.instrument.UnmodifiableClassException;
007    
008    import org.objectweb.asm.ClassAdapter;
009    import org.objectweb.asm.ClassReader;
010    import org.objectweb.asm.ClassVisitor;
011    import org.objectweb.asm.ClassWriter;
012    import org.objectweb.asm.MethodVisitor;
013    import org.objectweb.asm.Opcodes;
014    
015    import railo.commons.lang.ClassUtil;
016    import railo.runtime.type.List;
017    import railo.transformer.bytecode.util.ASMUtil;
018    
019    public class InstrumentationUtil {
020            
021            /**
022             * clear all method/constructor bodies of the given class
023             * @param clazz class to clear
024             * @param redefineClass if true change the class instance, if false just create the byte array
025             * @return
026             */
027            public static byte[] clearAllBodies(Class<?> clazz,boolean redefineClass){
028                    byte[] barr = clean(clazz);
029                    if(redefineClass)redefineClassEL(clazz, barr);
030                    return barr;
031            }
032    
033            private static byte[] clean(Class<?> clazz) {  
034            try {
035                            return clean(ClassUtil.toBytes(clazz));
036                    } catch (IOException e) {
037                            e.printStackTrace();
038                    }
039                    return null;
040        }  
041            
042            private static byte[] clean(byte[] org) {  
043                    ClassWriter cw = ASMUtil.getClassWriter();
044            ChangeAdapter ca = new ChangeAdapter(cw);  
045            ClassReader cr = new ClassReader(org); 
046            cr.accept(ca, false);  
047            return cw.toByteArray();
048        }  
049            
050            /**
051             * redefine the class with the given byte array
052             * @param clazz
053             * @param barr
054             * @return
055             */
056            public static boolean redefineClassEL(Class clazz, byte[] barr){
057                    Instrumentation inst = InstrumentationFactory.getInstance();
058                if(inst!=null && inst.isRedefineClassesSupported()) {
059                    try {
060                            inst.redefineClasses(new ClassDefinition(clazz,barr));
061                                    return true;
062                            } 
063                    catch (Throwable t) {t.printStackTrace();}
064                }
065                return false;
066            }
067            public static void redefineClass(Class clazz, byte[] barr) throws ClassNotFoundException, UnmodifiableClassException{
068                    Instrumentation inst = InstrumentationFactory.getInstance();
069                inst.redefineClasses(new ClassDefinition(clazz,barr));
070            }
071    
072            
073            public static class ChangeAdapter extends ClassAdapter {  
074                    public ChangeAdapter(ClassVisitor cv) {  
075                        super(cv);
076                    }  
077    
078                            /**
079                             * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
080                             */
081                            public MethodVisitor visitMethod(int access, String name, String desc,String signature, String[] exceptions) {
082                                    MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);  
083                                    if(name.startsWith("<")) return mv;
084                                    
085                                    mv.visitCode();
086                                    String ret = List.last(desc, ')').toLowerCase();
087                                    // void
088                                    if(ret.equals("v")) {
089                                            mv.visitInsn(Opcodes.RETURN);
090                                    }
091                                    // boolean, short, int, char, 
092                                    else if(ret.equals("z") || ret.equals("s") || ret.equals("i") || ret.equals("c") || ret.equals("b")) {
093                                            mv.visitInsn(Opcodes.ICONST_0);
094                                            mv.visitInsn(Opcodes.IRETURN);
095                                    }
096                                    else if(ret.equals("f")) {
097                                            mv.visitInsn(Opcodes.FCONST_0);
098                                            mv.visitInsn(Opcodes.FRETURN);
099                                    }
100                                    else if(ret.equals("j")) {
101                                            mv.visitInsn(Opcodes.LCONST_0);
102                                            mv.visitInsn(Opcodes.LRETURN);
103                                    }
104                                    else if(ret.equals("d")) {
105                                            mv.visitInsn(Opcodes.DCONST_0);
106                                            mv.visitInsn(Opcodes.DRETURN);
107                                    }
108                                    else {
109                                            mv.visitInsn(Opcodes.ACONST_NULL);
110                                    mv.visitInsn(Opcodes.ARETURN);
111                                    }
112                            mv.visitEnd();
113                            return mv;
114                            }
115                    }
116    
117    
118            public static boolean isSupported() {
119                    Instrumentation inst = InstrumentationFactory.getInstance();
120                return (inst!=null && inst.isRedefineClassesSupported());
121            } 
122    }