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