KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > internal > runtime > methods > DumpingInvocationMethodFactory


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2006 Ola Bini <ola@ologix.com>
15  *
16  * Alternatively, the contents of this file may be used under the terms of
17  * either of the GNU General Public License Version 2 or later (the "GPL"),
18  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19  * in which case the provisions of the GPL or the LGPL are applicable instead
20  * of those above. If you wish to allow use of your version of this file only
21  * under the terms of either the GPL or the LGPL, and not to allow others to
22  * use your version of this file under the terms of the CPL, indicate your
23  * decision by deleting the provisions above and replace them with the notice
24  * and other provisions required by the GPL or the LGPL. If you do not delete
25  * the provisions above, a recipient may use your version of this file under
26  * the terms of any one of the CPL, the GPL or the LGPL.
27  ***** END LICENSE BLOCK *****/

28 package org.jruby.internal.runtime.methods;
29
30 import java.io.File JavaDoc;
31 import java.io.FileOutputStream JavaDoc;
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 /**
47  * @author <a HREF="mailto:ola.bini@ki.se">Ola Bini</a>
48  */

49 public class DumpingInvocationMethodFactory extends MethodFactory implements Opcodes {
50     private final static Class JavaDoc IRUBY_OBJECT_ARR = IRubyObject[].class;
51     private final static String JavaDoc SIMPLE_SUPER_CLASS = SimpleInvocationMethod.class.getName().replace('.','/');
52     private final static String JavaDoc COMPILED_SUPER_CLASS = CompiledMethod.class.getName().replace('.','/');
53     private final static String JavaDoc FULL_SUPER_CLASS = FullInvocationMethod.class.getName().replace('.','/');
54     private final static String JavaDoc IRUB_ID = "Lorg/jruby/runtime/builtin/IRubyObject;";
55     private final static String JavaDoc BLOCK_ID = "Lorg/jruby/runtime/Block;";
56     private final static String JavaDoc CALL_SIG = "(" + IRUB_ID + "[" + IRUB_ID + BLOCK_ID + ")" + IRUB_ID;
57     private final static String JavaDoc CALL_SIG_NB = "(" + IRUB_ID + "[" + IRUB_ID + ")" + IRUB_ID;
58     private final static String JavaDoc COMPILED_CALL_SIG = "(Lorg/jruby/runtime/ThreadContext;" + IRUB_ID + "[" + IRUB_ID + BLOCK_ID + ")" + IRUB_ID;
59     private final static String JavaDoc SUPER_SIG = "(" + ci(RubyModule.class) + ci(Arity.class) + ci(Visibility.class) + ")V";
60     private final static String JavaDoc COMPILED_SUPER_SIG = "(" + ci(RubyModule.class) + ci(Arity.class) + ci(Visibility.class) + ci(SinglyLinkedList.class) + ")V";
61
62     private String JavaDoc dumpPath;
63     
64     public DumpingInvocationMethodFactory(String JavaDoc path) {
65         this.dumpPath = path;
66     }
67
68     /**
69      * Creates a class path name, from a Class.
70      */

71     private static String JavaDoc p(Class JavaDoc n) {
72         return n.getName().replace('.','/');
73     }
74
75     /**
76      * Creates a class identifier of form Labc/abc;, from a Class.
77      */

78     private static String JavaDoc ci(Class JavaDoc n) {
79         return "L" + p(n) + ";";
80     }
81
82     private ClassWriter createCtor(String JavaDoc namePath, String JavaDoc sup) throws Exception JavaDoc {
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 JavaDoc namePath, String JavaDoc sup) throws Exception JavaDoc {
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 JavaDoc tryClass(Ruby runtime, String JavaDoc name) {
116         try {
117             return Class.forName(name,true,runtime.getJRubyClassLoader());
118         } catch(Exception JavaDoc e) {
119             return null;
120         }
121     }
122
123     private Class JavaDoc endCall(Ruby runtime, ClassWriter cw, MethodVisitor mv, String JavaDoc name) {
124         mv.visitMaxs(0,0);
125         mv.visitEnd();
126         cw.visitEnd();
127         byte[] code = cw.toByteArray();
128         String JavaDoc cname = name.replace('.','/');
129         File JavaDoc f = new File JavaDoc(dumpPath,cname+".class");
130         f.getParentFile().mkdirs();
131         try {
132             FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc(f);
133             fos.write(code);
134             fos.close();
135         } catch(Exception JavaDoc e) {
136         }
137         return runtime.getJRubyClassLoader().defineClass(name, code);
138     }
139
140     private String JavaDoc getReturnName(Class JavaDoc type, String JavaDoc method, Class JavaDoc[] args) throws Exception JavaDoc {
141         String JavaDoc t = ci(type.getMethod(method,args).getReturnType());
142         if("void".equalsIgnoreCase(t)) {
143             throw new IllegalArgumentException JavaDoc("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 JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, String JavaDoc sup, boolean block) {
149         String JavaDoc typePath = p(type);
150         String JavaDoc mname = type.getName() + "Invoker" + method + arity;
151         String JavaDoc mnamePath = typePath + "Invoker" + method + arity;
152         Class JavaDoc 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 JavaDoc[] sign = new Class JavaDoc[ 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 JavaDoc sbe = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc ret = getReturnName(type, method, block ? new Class JavaDoc[]{IRUBY_OBJECT_ARR, Block.class} : new Class JavaDoc[]{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 JavaDoc[]{RubyModule.class, Arity.class, Visibility.class}).newInstance(new Object JavaDoc[]{implementationClass,arity,visibility});
207         } catch(Exception JavaDoc e) {
208             e.printStackTrace();
209             throw implementationClass.getRuntime().newLoadError(e.getMessage());
210         }
211     }
212
213     private DynamicMethod getCompleteMethod(RubyModule implementationClass, Class JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, SinglyLinkedList cref, String JavaDoc sup) {
214         String JavaDoc typePath = p(type);
215         String JavaDoc mname = type.getName() + "Invoker" + method + arity;
216         String JavaDoc mnamePath = typePath + "Invoker" + method + arity;
217         Class JavaDoc c = tryClass(implementationClass.getRuntime(), mname);
218         try {
219             if(c == null) {
220                 ClassWriter cw = createCompiledCtor(mnamePath,sup);
221                 MethodVisitor mv = null;
222
223                 // compiled methods will always return IRubyObject
224
//String ret = getReturnName(type, method, new Class[]{ThreadContext.class, RubyKernel.IRUBY_OBJECT, IRUBY_OBJECT_ARR, Block.class});
225
String JavaDoc 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 JavaDoc[]{RubyModule.class, Arity.class, Visibility.class, SinglyLinkedList.class}).newInstance(new Object JavaDoc[]{implementationClass,arity,visibility,cref});
239         } catch(Exception JavaDoc e) {
240             e.printStackTrace();
241             throw implementationClass.getRuntime().newLoadError(e.getMessage());
242         }
243     }
244
245     public DynamicMethod getFullMethod(RubyModule implementationClass, Class JavaDoc type, String JavaDoc 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 JavaDoc type, String JavaDoc 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 JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, SinglyLinkedList cref) {
254         return getCompleteMethod(implementationClass,type,method,arity,visibility,cref,COMPILED_SUPER_CLASS);
255     }
256 }// DumpingInvocationMethodFactory
257
Popular Tags