1 28 package org.jruby.internal.runtime.methods; 29 30 import java.io.File ; 31 import java.io.FileOutputStream ; 32 33 import org.jruby.Ruby; 34 import org.jruby.RubyKernel; 35 import org.jruby.runtime.Block; 36 import org.objectweb.asm.ClassWriter; 37 import org.objectweb.asm.MethodVisitor; 38 import org.objectweb.asm.Opcodes; 39 import org.jruby.RubyModule; 40 import org.jruby.runtime.Arity; 41 import org.jruby.runtime.MethodFactory; 42 import org.jruby.runtime.Visibility; 43 import org.jruby.runtime.builtin.IRubyObject; 44 import org.jruby.util.collections.SinglyLinkedList; 45 46 49 public class DumpingInvocationMethodFactory extends MethodFactory implements Opcodes { 50 private final static Class IRUBY_OBJECT_ARR = IRubyObject[].class; 51 private final static String SIMPLE_SUPER_CLASS = SimpleInvocationMethod.class.getName().replace('.','/'); 52 private final static String COMPILED_SUPER_CLASS = CompiledMethod.class.getName().replace('.','/'); 53 private final static String FULL_SUPER_CLASS = FullInvocationMethod.class.getName().replace('.','/'); 54 private final static String IRUB_ID = "Lorg/jruby/runtime/builtin/IRubyObject;"; 55 private final static String BLOCK_ID = "Lorg/jruby/runtime/Block;"; 56 private final static String CALL_SIG = "(" + IRUB_ID + "[" + IRUB_ID + BLOCK_ID + ")" + IRUB_ID; 57 private final static String CALL_SIG_NB = "(" + IRUB_ID + "[" + IRUB_ID + ")" + IRUB_ID; 58 private final static String COMPILED_CALL_SIG = "(Lorg/jruby/runtime/ThreadContext;" + IRUB_ID + "[" + IRUB_ID + BLOCK_ID + ")" + IRUB_ID; 59 private final static String SUPER_SIG = "(" + ci(RubyModule.class) + ci(Arity.class) + ci(Visibility.class) + ")V"; 60 private final static String COMPILED_SUPER_SIG = "(" + ci(RubyModule.class) + ci(Arity.class) + ci(Visibility.class) + ci(SinglyLinkedList.class) + ")V"; 61 62 private String dumpPath; 63 64 public DumpingInvocationMethodFactory(String path) { 65 this.dumpPath = path; 66 } 67 68 71 private static String p(Class n) { 72 return n.getName().replace('.','/'); 73 } 74 75 78 private static String ci(Class n) { 79 return "L" + p(n) + ";"; 80 } 81 82 private ClassWriter createCtor(String namePath, String sup) throws Exception { 83 ClassWriter cw = new ClassWriter(true); 84 cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, namePath, null, sup, null); 85 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", SUPER_SIG, null, null); 86 mv.visitCode(); 87 mv.visitVarInsn(ALOAD, 0); 88 mv.visitVarInsn(ALOAD, 1); 89 mv.visitVarInsn(ALOAD, 2); 90 mv.visitVarInsn(ALOAD, 3); 91 mv.visitMethodInsn(INVOKESPECIAL, sup, "<init>", SUPER_SIG); 92 mv.visitInsn(RETURN); 93 mv.visitMaxs(0,0); 94 mv.visitEnd(); 95 return cw; 96 } 97 98 private ClassWriter createCompiledCtor(String namePath, String sup) throws Exception { 99 ClassWriter cw = new ClassWriter(true); 100 cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, namePath, null, sup, null); 101 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", COMPILED_SUPER_SIG, null, null); 102 mv.visitCode(); 103 mv.visitVarInsn(ALOAD, 0); 104 mv.visitVarInsn(ALOAD, 1); 105 mv.visitVarInsn(ALOAD, 2); 106 mv.visitVarInsn(ALOAD, 3); 107 mv.visitVarInsn(ALOAD, 4); 108 mv.visitMethodInsn(INVOKESPECIAL, sup, "<init>", COMPILED_SUPER_SIG); 109 mv.visitInsn(RETURN); 110 mv.visitMaxs(0,0); 111 mv.visitEnd(); 112 return cw; 113 } 114 115 private Class tryClass(Ruby runtime, String name) { 116 try { 117 return Class.forName(name,true,runtime.getJRubyClassLoader()); 118 } catch(Exception e) { 119 return null; 120 } 121 } 122 123 private Class endCall(Ruby runtime, ClassWriter cw, MethodVisitor mv, String name) { 124 mv.visitMaxs(0,0); 125 mv.visitEnd(); 126 cw.visitEnd(); 127 byte[] code = cw.toByteArray(); 128 String cname = name.replace('.','/'); 129 File f = new File (dumpPath,cname+".class"); 130 f.getParentFile().mkdirs(); 131 try { 132 FileOutputStream fos = new FileOutputStream (f); 133 fos.write(code); 134 fos.close(); 135 } catch(Exception e) { 136 } 137 return runtime.getJRubyClassLoader().defineClass(name, code); 138 } 139 140 private String getReturnName(Class type, String method, Class [] args) throws Exception { 141 String t = ci(type.getMethod(method,args).getReturnType()); 142 if("void".equalsIgnoreCase(t)) { 143 throw new IllegalArgumentException ("Method " + method + " has a void return type. This is not allowed in JRuby."); 144 } 145 return t; 146 } 147 148 private DynamicMethod getMethod(RubyModule implementationClass, Class type, String method, Arity arity, Visibility visibility, String sup, boolean block) { 149 String typePath = p(type); 150 String mname = type.getName() + "Invoker" + method + arity; 151 String mnamePath = typePath + "Invoker" + method + arity; 152 Class c = tryClass(implementationClass.getRuntime(), mname); 153 try { 154 if(c == null) { 155 ClassWriter cw = createCtor(mnamePath,sup); 156 MethodVisitor mv = null; 157 if(arity.isFixed()) { 158 int ar_len = arity.getValue(); 159 Class [] sign = new Class [ block ? ar_len+1 : ar_len ]; 160 java.util.Arrays.fill(sign,RubyKernel.IRUBY_OBJECT); 161 if(block) { 162 sign[sign.length-1] = Block.class; 163 } 164 StringBuffer sbe = new StringBuffer (); 165 for(int i=0;i<ar_len;i++) { 166 sbe.append(IRUB_ID); 167 } 168 if(block) { 169 sbe.append(BLOCK_ID); 170 } 171 String ret = getReturnName(type, method, sign); 172 mv = cw.visitMethod(ACC_PUBLIC, "call", block ? CALL_SIG : CALL_SIG_NB, null, null); 173 mv.visitCode(); 174 mv.visitVarInsn(ALOAD, 1); 175 mv.visitTypeInsn(CHECKCAST, typePath); 176 for(int i=0;i<ar_len;i++) { 177 mv.visitVarInsn(ALOAD, 2); 178 if(i < 6) { 179 mv.visitInsn(ICONST_0 + i); 180 } else { 181 mv.visitIntInsn(BIPUSH,i); 182 } 183 mv.visitInsn(AALOAD); 184 } 185 if(block) { 186 mv.visitVarInsn(ALOAD, 3); 187 } 188 mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, "(" + sbe + ")" + ret); 189 mv.visitInsn(ARETURN); 190 } else { 191 String ret = getReturnName(type, method, block ? new Class []{IRUBY_OBJECT_ARR, Block.class} : new Class []{IRUBY_OBJECT_ARR}); 192 mv = cw.visitMethod(ACC_PUBLIC, "call", block ? CALL_SIG : CALL_SIG_NB, null, null); 193 mv.visitCode(); 194 mv.visitVarInsn(ALOAD, 1); 195 mv.visitTypeInsn(CHECKCAST, typePath); 196 mv.visitVarInsn(ALOAD, 2); 197 if(block) { 198 mv.visitVarInsn(ALOAD, 3); 199 } 200 mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, "([" + IRUB_ID + (block ? BLOCK_ID : "") + ")" + ret); 201 mv.visitInsn(ARETURN); 202 } 203 c = endCall(implementationClass.getRuntime(), cw,mv,mname); 204 } 205 206 return (DynamicMethod)c.getConstructor(new Class []{RubyModule.class, Arity.class, Visibility.class}).newInstance(new Object []{implementationClass,arity,visibility}); 207 } catch(Exception e) { 208 e.printStackTrace(); 209 throw implementationClass.getRuntime().newLoadError(e.getMessage()); 210 } 211 } 212 213 private DynamicMethod getCompleteMethod(RubyModule implementationClass, Class type, String method, Arity arity, Visibility visibility, SinglyLinkedList cref, String sup) { 214 String typePath = p(type); 215 String mname = type.getName() + "Invoker" + method + arity; 216 String mnamePath = typePath + "Invoker" + method + arity; 217 Class c = tryClass(implementationClass.getRuntime(), mname); 218 try { 219 if(c == null) { 220 ClassWriter cw = createCompiledCtor(mnamePath,sup); 221 MethodVisitor mv = null; 222 223 String ret = IRUB_ID; 226 mv = cw.visitMethod(ACC_PUBLIC, "call", COMPILED_CALL_SIG, null, null); 227 mv.visitCode(); 228 mv.visitVarInsn(ALOAD, 1); 229 mv.visitVarInsn(ALOAD, 2); 230 mv.visitVarInsn(ALOAD, 3); 231 mv.visitVarInsn(ALOAD, 4); 232 mv.visitMethodInsn(INVOKESTATIC, typePath, method, "(Lorg/jruby/runtime/ThreadContext;" + IRUB_ID + "[" + IRUB_ID + "Lorg/jruby/runtime/Block;)" + ret); 233 mv.visitInsn(ARETURN); 234 235 c = endCall(implementationClass.getRuntime(), cw,mv,mname); 236 } 237 238 return (DynamicMethod)c.getConstructor(new Class []{RubyModule.class, Arity.class, Visibility.class, SinglyLinkedList.class}).newInstance(new Object []{implementationClass,arity,visibility,cref}); 239 } catch(Exception e) { 240 e.printStackTrace(); 241 throw implementationClass.getRuntime().newLoadError(e.getMessage()); 242 } 243 } 244 245 public DynamicMethod getFullMethod(RubyModule implementationClass, Class type, String method, Arity arity, Visibility visibility) { 246 return getMethod(implementationClass,type,method,arity,visibility,FULL_SUPER_CLASS, true); 247 } 248 249 public DynamicMethod getSimpleMethod(RubyModule implementationClass, Class type, String method, Arity arity, Visibility visibility) { 250 return getMethod(implementationClass,type,method,arity,visibility,SIMPLE_SUPER_CLASS, false); 251 } 252 253 public DynamicMethod getCompiledMethod(RubyModule implementationClass, Class type, String method, Arity arity, Visibility visibility, SinglyLinkedList cref) { 254 return getCompleteMethod(implementationClass,type,method,arity,visibility,cref,COMPILED_SUPER_CLASS); 255 } 256 } | Popular Tags |