001/**
002 *
003 * This library is free software; you can redistribute it and/or
004 * modify it under the terms of the GNU Lesser General Public
005 * License as published by the Free Software Foundation; either 
006 * version 2.1 of the License, or (at your option) any later version.
007 * 
008 * This library is distributed in the hope that it will be useful,
009 * but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
011 * Lesser General Public License for more details.
012 * 
013 * You should have received a copy of the GNU Lesser General Public 
014 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
015 * 
016 **/
017package lucee.transformer.bytecode.util;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.Iterator;
022import java.util.List;
023
024import lucee.print;
025import lucee.commons.io.IOUtil;
026import lucee.commons.io.res.Resource;
027import lucee.commons.io.res.ResourceProvider;
028import lucee.commons.io.res.ResourcesImpl;
029import lucee.commons.io.res.util.ResourceUtil;
030import lucee.commons.lang.ClassException;
031import lucee.commons.lang.ClassUtil;
032import lucee.commons.lang.StringUtil;
033import lucee.runtime.PageContext;
034import lucee.runtime.engine.ThreadLocalPageContext;
035import lucee.runtime.exp.ExpressionException;
036import lucee.runtime.exp.PageException;
037import lucee.runtime.exp.PageRuntimeException;
038import lucee.runtime.op.Caster;
039import lucee.runtime.type.util.ListUtil;
040
041import org.objectweb.asm.ClassReader;
042import org.objectweb.asm.ClassVisitor;
043import org.objectweb.asm.ClassWriter;
044import org.objectweb.asm.MethodVisitor;
045import org.objectweb.asm.Opcodes;
046import org.objectweb.asm.Type;
047
048public class MethodCleaner extends ClassVisitor implements Opcodes {
049
050        private final String methodName;
051        //private Class[] arguments;
052        private String strArgs;
053        private Class rtn;
054        private String msg;
055
056        MethodCleaner(ClassVisitor cv, String methodName, Class[] args, Class rtn, String msg) {
057                super(ASM4, cv);
058                this.methodName=methodName;
059                //this.arguments = arguments;
060                
061                StringBuilder sb=new StringBuilder("(");
062                for(int i=0;i<args.length;i++){
063                        sb.append(Type.getDescriptor(args[i]));
064                }
065                sb.append(")");
066                sb.append(Type.getDescriptor(rtn));
067                strArgs=sb.toString();
068                this.rtn=rtn;
069                this.msg=StringUtil.isEmpty(msg)?null:msg;
070        }
071
072
073    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
074        cv.visit(version, access, name, signature, superName, interfaces);
075    }
076    
077    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
078        if(name.equals(methodName) && desc.equals(strArgs)) {
079                MethodVisitor mv = cv.visitMethod(access,  name,  desc,  signature,  exceptions);
080                mv.visitCode();
081                
082                if(msg==null)empty(mv);
083                else exception(mv);
084                
085                mv.visitEnd();
086                return mv;
087        }
088        return super.visitMethod(access,  name,  desc,  signature,  exceptions);
089    
090        }
091    
092        private static List<Object> parse(String str) {
093                List<Object> list=new ArrayList<Object>();
094                int last=0,index1,index2;
095                while((index1=str.indexOf("{arg:",last))!=-1) {
096                        list.add(str.substring(last,index1));
097                        index2=str.indexOf('}',index1+5);
098                        try {
099                                list.add(Caster.toInteger(str.substring(index1+5,index2)));
100                        }
101                        catch (PageException pe) {throw new PageRuntimeException(pe);}
102                        
103                        last=index2+1;
104                        //break;
105                }
106                if(str.length()>last) {
107                        list.add(str.substring(last));
108                }
109                
110                
111                return list;
112        }
113
114
115        private void exception(MethodVisitor mv) {
116                List<Object> list = parse(msg);
117                if(list.size()==1) {
118                        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
119                        mv.visitInsn(DUP);
120                        mv.visitLdcInsn(msg);
121                        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
122                        mv.visitInsn(ATHROW);
123                        //mv.visitMaxs(3, 1);
124                }
125                else {
126                        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
127                        mv.visitInsn(DUP);
128                        mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
129                        mv.visitInsn(DUP);
130                        
131                        Iterator<Object> it = list.iterator();
132                        int count=0;
133                        Object o;
134                        while(it.hasNext()){
135                                o=it.next();
136                                
137                                // add left
138                                mv.visitLdcInsn(o);
139                                if(count++==0) {
140                                        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
141                                }
142                                else {
143                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
144                                }
145                                
146                                // add arg
147                                if(it.hasNext()) {
148                                        Integer i=(Integer) it.next();
149                                        mv.visitVarInsn(ALOAD, i.intValue());
150                                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
151                                }
152                        }
153                        
154                        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuffer", "toString", "()Ljava/lang/String;");
155                        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
156                        mv.visitInsn(ATHROW);
157                        
158                }
159                
160                
161                
162        }
163
164
165        private void empty(MethodVisitor mv) {
166                // void
167                if(rtn==void.class) {
168                        mv.visitInsn(RETURN);
169                }
170                // int,boolean,short,char,byte
171                else if(rtn==int.class) {
172                        mv.visitInsn(ICONST_0);
173                        mv.visitInsn(IRETURN);
174                        mv.visitMaxs(1, 1);
175                }
176                // double
177                else if(rtn==double.class) {
178                        mv.visitInsn(DCONST_0);
179                        mv.visitInsn(DRETURN);
180                        mv.visitMaxs(2, 1);
181                }
182                // float
183                else if(rtn==float.class) {
184                        mv.visitInsn(FCONST_0);
185                        mv.visitInsn(FRETURN);
186                        mv.visitMaxs(1, 1);
187                }
188                // long
189                else if(rtn==long.class) {
190                        mv.visitInsn(LCONST_0);
191                        mv.visitInsn(LRETURN);
192                        mv.visitMaxs(2, 1);
193                }
194                // Object
195                else {
196                        mv.visitInsn(ACONST_NULL);
197                        mv.visitInsn(ARETURN);
198                        mv.visitMaxs(1, 1);
199                }
200        }
201
202        public static byte[] modifie(byte[] src, String methodName,Class[] args, Class rtn, String msg){
203                ClassReader cr = new ClassReader(src);
204                ClassWriter cw = ASMUtil.getClassWriter();
205                ClassVisitor ca = new MethodCleaner(cw,methodName,args,rtn,msg);
206                cr.accept(ca, 0);
207                return cw.toByteArray();
208        }
209        
210        public static void modifie(String path, String methodName,String[] argNames, String rtnName, String msg) throws IOException, ExpressionException{
211                Resource res = ResourceUtil.toResourceExisting(ThreadLocalPageContext.getConfig(), path);
212                Class[] args=new Class[argNames.length];
213                for(int i=0;i<argNames.length;i++){
214                        args[i]=ClassUtil.loadClass(argNames[i]);
215                }
216                Class rtn=ClassUtil.loadClass(rtnName);
217                byte[] result = modifie(IOUtil.toBytes(res), methodName, args, rtn,msg);
218                IOUtil.write(res, result);
219        }
220}