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}