KickJava   Java API By Example, From Geeks To Geeks.

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


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 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 /**
44  * @author <a HREF="mailto:ola.bini@ki.se">Ola Bini</a>
45  */

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

62     private static String JavaDoc p(Class JavaDoc n) {
63         return n.getName().replace('.','/');
64     }
65
66     /**
67      * Creates a class identifier of form Labc/abc;, from a Class.
68      */

69     private static String JavaDoc ci(Class JavaDoc n) {
70         return "L" + p(n) + ";";
71     }
72
73     private ClassWriter createCtor(String JavaDoc namePath, String JavaDoc sup) throws Exception JavaDoc {
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 JavaDoc namePath, String JavaDoc sup) throws Exception JavaDoc {
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 JavaDoc tryClass(Ruby runtime, String JavaDoc name) {
107         try {
108             return Class.forName(name,true,runtime.getJRubyClassLoader());
109         } catch(Exception JavaDoc e) {
110             return null;
111         }
112     }
113
114     private Class JavaDoc endCall(Ruby runtime, ClassWriter cw, MethodVisitor mv, String JavaDoc 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 JavaDoc getReturnName(Class JavaDoc type, String JavaDoc method, Class JavaDoc[] args) throws Exception JavaDoc {
123         String JavaDoc t = ci(type.getMethod(method,args).getReturnType());
124         if("void".equalsIgnoreCase(t)) {
125             throw new IllegalArgumentException JavaDoc("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 JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, String JavaDoc sup, boolean block) {
131         String JavaDoc typePath = p(type);
132         String JavaDoc mname = type.getName() + "Invoker" + method + arity;
133         String JavaDoc mnamePath = typePath + "Invoker" + method + arity;
134         Class JavaDoc 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 JavaDoc[] sign = new Class JavaDoc[ 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 JavaDoc sbe = new StringBuffer JavaDoc();
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 JavaDoc 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 JavaDoc ret = getReturnName(type, method, block ? new Class JavaDoc[]{IRUBY_OBJECT_ARR, Block.class} : new Class JavaDoc[]{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 JavaDoc[]{RubyModule.class, Arity.class, Visibility.class}).newInstance(new Object JavaDoc[]{implementationClass,arity,visibility});
189         } catch(Exception JavaDoc e) {
190             e.printStackTrace();
191             throw implementationClass.getRuntime().newLoadError(e.getMessage());
192         }
193     }
194
195     private DynamicMethod getCompleteMethod(RubyModule implementationClass, Class JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, SinglyLinkedList cref, String JavaDoc sup) {
196         String JavaDoc typePath = p(type);
197         String JavaDoc mname = type.getName() + "Invoker" + method + arity;
198         String JavaDoc mnamePath = typePath + "Invoker" + method + arity;
199         Class JavaDoc c = tryClass(implementationClass.getRuntime(), mname);
200         try {
201             if(c == null) {
202                 ClassWriter cw = createCompiledCtor(mnamePath,sup);
203                 MethodVisitor mv = null;
204
205                 // compiled methods will always return IRubyObject
206
//String ret = getReturnName(type, method, new Class[]{ThreadContext.class, RubyKernel.IRUBY_OBJECT, IRUBY_OBJECT_ARR, Block.class});
207
String JavaDoc 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 JavaDoc[]{RubyModule.class, Arity.class, Visibility.class, SinglyLinkedList.class}).newInstance(new Object JavaDoc[]{implementationClass,arity,visibility,cref});
221         } catch(Exception JavaDoc e) {
222             e.printStackTrace();
223             throw implementationClass.getRuntime().newLoadError(e.getMessage());
224         }
225     }
226
227     public DynamicMethod getFullMethod(RubyModule implementationClass, Class JavaDoc type, String JavaDoc 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 JavaDoc type, String JavaDoc 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 JavaDoc type, String JavaDoc method, Arity arity, Visibility visibility, SinglyLinkedList cref) {
236         return getCompleteMethod(implementationClass,type,method,arity,visibility,cref,COMPILED_SUPER_CLASS);
237     }
238 }// InvocationMethodFactory
239
Popular Tags