Java tutorial
/***** BEGIN LICENSE BLOCK ***** * Version: CPL 1.0/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Common Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.eclipse.org/legal/cpl-v10.html * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Copyright (C) 2006 Ola Bini <ola@ologix.com> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the CPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the CPL, the GPL or the LGPL. ***** END LICENSE BLOCK *****/ package org.jruby.runtime.callback; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyInstanceConfig; import org.jruby.RubyKernel; import org.jruby.RubyModule; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.javasupport.util.RuntimeHelpers; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.CallType; import org.jruby.runtime.CallbackFactory; import org.jruby.runtime.CompiledBlockCallback; import org.jruby.runtime.CompiledBlockCallback19; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; import static org.jruby.util.CodegenUtils.*; import org.jruby.util.JRubyClassLoader; public class InvocationCallbackFactory extends CallbackFactory implements Opcodes { private final Class type; final ProtectionDomain protectionDomain; protected final JRubyClassLoader classLoader; private final String typePath; protected final Ruby runtime; @Deprecated private final static String SUPER_CLASS = p(InvocationCallback.class); @Deprecated private final static String FAST_SUPER_CLASS = p(FastInvocationCallback.class); @Deprecated private final static String CALL_SIG = sig(RubyKernel.IRUBY_OBJECT, params(Object.class, Object[].class, Block.class)); @Deprecated private final static String FAST_CALL_SIG = sig(RubyKernel.IRUBY_OBJECT, params(Object.class, Object[].class)); private final static String BLOCK_CALL_SIG = sig(RubyKernel.IRUBY_OBJECT, params(ThreadContext.class, RubyKernel.IRUBY_OBJECT, IRubyObject.class)); private final static String BLOCK_CALL_SIG19 = sig(RubyKernel.IRUBY_OBJECT, params(ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class)); private final static String IRUB = p(RubyKernel.IRUBY_OBJECT); public static final int DISPATCHER_THREADCONTEXT_INDEX = 1; public static final int DISPATCHER_SELF_INDEX = 2; public static final int DISPATCHER_RUBYMODULE_INDEX = 3; public static final int DISPATCHER_METHOD_INDEX = 4; public static final int DISPATCHER_NAME_INDEX = 5; public static final int DISPATCHER_ARGS_INDEX = 6; public static final int DISPATCHER_CALLTYPE_INDEX = 7; public static final int DISPATCHER_BLOCK_INDEX = 8; public static final int DISPATCHER_RUNTIME_INDEX = 9; private static final int METHOD_ARGS_INDEX = 2; public InvocationCallbackFactory(Ruby runtime, final Class type, ClassLoader classLoader) { this.type = type; if (classLoader instanceof JRubyClassLoader) { this.classLoader = (JRubyClassLoader) classLoader; } else { this.classLoader = new JRubyClassLoader(classLoader); } this.typePath = p(type); this.runtime = runtime; SecurityManager sm = System.getSecurityManager(); if (sm == null) { this.protectionDomain = type.getProtectionDomain(); } else { this.protectionDomain = AccessController.doPrivileged(new PrivilegedAction<ProtectionDomain>() { public ProtectionDomain run() { return type.getProtectionDomain(); } }); } } @Deprecated private Class getReturnClass(String method, Class[] args) throws Exception { return type.getMethod(method, args).getReturnType(); } @Deprecated private ClassWriter createCtor(String namePath) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, namePath, null, SUPER_CLASS, null); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, SUPER_CLASS, "<init>", "()V"); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); return cw; } @Deprecated private ClassWriter createCtorFast(String namePath) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, namePath, null, FAST_SUPER_CLASS, null); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); mv.visitCode(); mv.visitVarInsn(ALOAD, 0); mv.visitMethodInsn(INVOKESPECIAL, FAST_SUPER_CLASS, "<init>", "()V"); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitInsn(RETURN); mv.visitMaxs(1, 1); mv.visitEnd(); return cw; } @Deprecated private ClassWriter createBlockCtor(String namePath, Class fieldClass) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, p(CompiledBlockCallback.class), null); cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject", ci(fieldClass), null, null); SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(Void.TYPE, params(Object.class)), null, null); mv.start(); mv.aload(0); mv.invokespecial(p(CompiledBlockCallback.class), "<init>", sig(void.class)); mv.aload(0); mv.aload(1); mv.checkcast(p(fieldClass)); mv.putfield(namePath, "$scriptObject", ci(fieldClass)); mv.voidreturn(); mv.end(); return cw; } @Deprecated private ClassWriter createBlockCtor19(String namePath, Class fieldClass) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, namePath, null, p(Object.class), new String[] { p(CompiledBlockCallback19.class) }); cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject", ci(fieldClass), null, null); SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "<init>", sig(Void.TYPE, params(Object.class)), null, null); mv.start(); mv.aload(0); mv.invokespecial(p(Object.class), "<init>", sig(void.class)); mv.aload(0); mv.aload(1); mv.checkcast(p(fieldClass)); mv.putfield(namePath, "$scriptObject", ci(fieldClass)); mv.voidreturn(); mv.end(); return cw; } @Deprecated private Class tryClass(String name) { try { return classLoader.loadClass(name); } catch (Exception e) { return null; } } @Deprecated private MethodVisitor startCall(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "call", CALL_SIG, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, typePath); return mv; } @Deprecated private MethodVisitor startCallS(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "call", CALL_SIG, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitVarInsn(ALOAD, 1); checkCast(mv, IRubyObject.class); return mv; } @Deprecated private MethodVisitor startCallFast(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "call", FAST_CALL_SIG, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, typePath); return mv; } @Deprecated private MethodVisitor startDispatcher(ClassWriter cw) { MethodVisitor mv = cw .visitMethod(ACC_PUBLIC, "callMethod", sig(IRubyObject.class, params(ThreadContext.class, IRubyObject.class, RubyClass.class, Integer.TYPE, String.class, IRubyObject[].class, CallType.class, Block.class)), null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitVarInsn(ALOAD, 2); mv.visitTypeInsn(CHECKCAST, typePath); return mv; } @Deprecated private MethodVisitor startCallSFast(ClassWriter cw) { MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "call", FAST_CALL_SIG, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, IRUB); return mv; } @Deprecated private SkinnyMethodAdapter startBlockCall(ClassWriter cw) { SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call", BLOCK_CALL_SIG, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); return mv; } @Deprecated private SkinnyMethodAdapter startBlockCall19(ClassWriter cw) { SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call", BLOCK_CALL_SIG19, null, null); mv.visitCode(); Label line = new Label(); mv.visitLineNumber(0, line); return mv; } @Deprecated protected Class endCall(ClassWriter cw, MethodVisitor mv, String name) { mv.visitEnd(); cw.visitEnd(); byte[] code = cw.toByteArray(); return classLoader.defineClass(name, code, protectionDomain); } @Deprecated public Callback getMethod(String method) { String mname = type.getName() + "Callback$" + method + "_0"; String mnamePath = typePath + "Callback$" + method + "_0"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCall(cw); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.noArguments()); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getMethod(String method, Class arg1) { String mname = type.getName() + "Callback$" + method + "_1"; String mnamePath = typePath + "Callback$" + method + "_1"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1 }; if (c == null) { Class[] signature = new Class[] { arg1, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCall(cw); loadArguments(mv, METHOD_ARGS_INDEX, 1, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.singleArgument()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getMethod(String method, Class arg1, Class arg2) { String mname = type.getName() + "Callback$" + method + "_2"; String mnamePath = typePath + "Callback$" + method + "_2"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2 }; if (c == null) { Class[] signature = new Class[] { arg1, arg2, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCall(cw); loadArguments(mv, METHOD_ARGS_INDEX, 2, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(4, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.twoArguments()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getMethod(String method, Class arg1, Class arg2, Class arg3) { String mname = type.getName() + "Callback$" + method + "_3"; String mnamePath = typePath + "Callback$" + method + "_3"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2, arg3 }; if (c == null) { Class[] signature = new Class[] { arg1, arg2, arg3, Block.class }; Class ret = getReturnClass(method, descriptor); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCall(cw); loadArguments(mv, METHOD_ARGS_INDEX, 3, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(5, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.fixed(3)); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getSingletonMethod(String method) { String mname = type.getName() + "Callback$" + method + "S0"; String mnamePath = typePath + "Callback$" + method + "S0"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCallS(cw); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.noArguments()); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getSingletonMethod(String method, Class arg1) { String mname = type.getName() + "Callback$" + method + "_S1"; String mnamePath = typePath + "Callback$" + method + "_S1"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCallS(cw); loadArguments(mv, METHOD_ARGS_INDEX, 1, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.singleArgument()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getSingletonMethod(String method, Class arg1, Class arg2) { String mname = type.getName() + "Callback$" + method + "_S2"; String mnamePath = typePath + "Callback$" + method + "_S2"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1, arg2, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCallS(cw); loadArguments(mv, METHOD_ARGS_INDEX, 2, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(4, 4); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.twoArguments()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getSingletonMethod(String method, Class arg1, Class arg2, Class arg3) { String mname = type.getName() + "Callback$" + method + "_S3"; String mnamePath = typePath + "Callback$" + method + "_S3"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2, arg3 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1, arg2, arg3, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCallS(cw); loadArguments(mv, METHOD_ARGS_INDEX, 3, descriptor); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(5, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.fixed(3)); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getBlockMethod(String method) { // TODO: This is probably BAD... return new ReflectionCallback(type, method, new Class[] { RubyKernel.IRUBY_OBJECT, RubyKernel.IRUBY_OBJECT }, false, true, Arity.fixed(2), false); } @Deprecated public CompiledBlockCallback getBlockCallback(String method, Object scriptObject) { Class typeClass = scriptObject.getClass(); String typePathString = p(typeClass); String mname = typeClass.getName() + "BlockCallback$" + method + "xx1"; String mnamePath = typePathString + "BlockCallback$" + method + "xx1"; synchronized (classLoader) { Class c = tryClass(mname); try { if (c == null) { ClassWriter cw = createBlockCtor(mnamePath, typeClass); SkinnyMethodAdapter mv = startBlockCall(cw); mv.aload(0); mv.getfield(mnamePath, "$scriptObject", ci(typeClass)); mv.aload(1); mv.aload(2); mv.aload(3); mv.invokestatic(typePathString, method, sig(RubyKernel.IRUBY_OBJECT, "L" + typePathString + ";", ThreadContext.class, RubyKernel.IRUBY_OBJECT, IRubyObject.class)); mv.areturn(); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } CompiledBlockCallback ic = (CompiledBlockCallback) c.getConstructor(Object.class) .newInstance(scriptObject); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public CompiledBlockCallback19 getBlockCallback19(String method, Object scriptObject) { Class typeClass = scriptObject.getClass(); String typePathString = p(typeClass); String mname = typeClass.getName() + "BlockCallback$" + method + "xx1"; String mnamePath = typePathString + "BlockCallback$" + method + "xx1"; synchronized (classLoader) { Class c = tryClass(mname); try { if (c == null) { ClassWriter cw = createBlockCtor19(mnamePath, typeClass); SkinnyMethodAdapter mv = startBlockCall19(cw); mv.aload(0); mv.getfield(mnamePath, "$scriptObject", ci(typeClass)); mv.aload(1); mv.aload(2); mv.aload(3); mv.aload(4); mv.invokestatic(typePathString, method, sig(IRubyObject.class, "L" + typePathString + ";", ThreadContext.class, IRubyObject.class, IRubyObject[].class, Block.class)); mv.areturn(); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } CompiledBlockCallback19 ic = (CompiledBlockCallback19) c.getConstructor(Object.class) .newInstance(scriptObject); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { e.printStackTrace(); throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getOptSingletonMethod(String method) { String mname = type.getName() + "Callback$" + method + "_Sopt"; String mnamePath = typePath + "Callback$" + method + "_Sopt"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, IRubyObject[].class, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCallS(cw); mv.visitVarInsn(ALOAD, METHOD_ARGS_INDEX); checkCast(mv, IRubyObject[].class); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.optional()); ic.setArgumentTypes(InvocationCallback.OPTIONAL_ARGS); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getOptMethod(String method) { String mname = type.getName() + "Callback$" + method + "_opt"; String mnamePath = typePath + "Callback$" + method + "_opt"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { IRubyObject[].class, Block.class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtor(mnamePath); MethodVisitor mv = startCall(cw); mv.visitVarInsn(ALOAD, METHOD_ARGS_INDEX); checkCast(mv, IRubyObject[].class); mv.visitVarInsn(ALOAD, 3); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } InvocationCallback ic = (InvocationCallback) c.newInstance(); ic.setArity(Arity.optional()); ic.setArgumentTypes(InvocationCallback.OPTIONAL_ARGS); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastMethod(String method) { String mname = type.getName() + "Callback$" + method + "_F0"; String mnamePath = typePath + "Callback$" + method + "_F0"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class ret = getReturnClass(method, new Class[0]); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallFast(cw); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.noArguments()); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastMethod(String method, Class arg1) { String mname = type.getName() + "Callback$" + method + "_F1"; String mnamePath = typePath + "Callback$" + method + "_F1"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1 }; if (c == null) { Class[] signature = descriptor; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 1, descriptor); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.singleArgument()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastMethod(String method, Class arg1, Class arg2) { String mname = type.getName() + "Callback$" + method + "_F2"; String mnamePath = typePath + "Callback$" + method + "_F2"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2 }; if (c == null) { Class[] signature = descriptor; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 2, descriptor); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(4, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.twoArguments()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastMethod(String method, Class arg1, Class arg2, Class arg3) { String mname = type.getName() + "Callback$" + method + "_F3"; String mnamePath = typePath + "Callback$" + method + "_F3"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2, arg3 }; if (c == null) { Class[] signature = descriptor; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 3, descriptor); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(5, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.fixed(3)); ic.setArgumentTypes(descriptor); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastSingletonMethod(String method) { String mname = type.getName() + "Callback$" + method + "_FS0"; String mnamePath = typePath + "Callback$" + method + "_FS0"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallSFast(cw); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(1, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.noArguments()); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastSingletonMethod(String method, Class arg1) { String mname = type.getName() + "Callback$" + method + "_FS1"; String mnamePath = typePath + "Callback$" + method + "_FS1"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1 }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallSFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 1, descriptor); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(3, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.singleArgument()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastSingletonMethod(String method, Class arg1, Class arg2) { String mname = type.getName() + "Callback$" + method + "_FS2"; String mnamePath = typePath + "Callback$" + method + "_FS2"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1, arg2 }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallSFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 2, descriptor); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(4, 4); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.twoArguments()); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastSingletonMethod(String method, Class arg1, Class arg2, Class arg3) { String mname = type.getName() + "Callback$" + method + "_FS3"; String mnamePath = typePath + "Callback$" + method + "_FS3"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { Class[] descriptor = new Class[] { arg1, arg2, arg3 }; if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, arg1, arg2, arg3 }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallSFast(cw); loadArguments(mv, METHOD_ARGS_INDEX, 3, descriptor); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(5, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.fixed(3)); ic.setArgumentTypes(descriptor); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastOptMethod(String method) { String mname = type.getName() + "Callback$" + method + "_Fopt"; String mnamePath = typePath + "Callback$" + method + "_Fopt"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { IRubyObject[].class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallFast(cw); mv.visitVarInsn(ALOAD, METHOD_ARGS_INDEX); checkCast(mv, IRubyObject[].class); mv.visitMethodInsn(INVOKEVIRTUAL, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.optional()); ic.setArgumentTypes(InvocationCallback.OPTIONAL_ARGS); ic.setJavaName(method); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated public Callback getFastOptSingletonMethod(String method) { String mname = type.getName() + "Callback$" + method + "_FSopt"; String mnamePath = typePath + "Callback$" + method + "_FSopt"; synchronized (runtime.getJRubyClassLoader()) { Class c = tryClass(mname); try { if (c == null) { Class[] signature = new Class[] { RubyKernel.IRUBY_OBJECT, IRubyObject[].class }; Class ret = getReturnClass(method, signature); ClassWriter cw = createCtorFast(mnamePath); MethodVisitor mv = startCallSFast(cw); mv.visitVarInsn(ALOAD, METHOD_ARGS_INDEX); checkCast(mv, IRubyObject[].class); mv.visitMethodInsn(INVOKESTATIC, typePath, method, sig(ret, signature)); mv.visitInsn(ARETURN); mv.visitMaxs(2, 3); c = endCall(cw, mv, mname); } FastInvocationCallback ic = (FastInvocationCallback) c.newInstance(); ic.setArity(Arity.optional()); ic.setArgumentTypes(InvocationCallback.OPTIONAL_ARGS); ic.setJavaName(method); ic.setSingleton(true); return ic; } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } @Deprecated private void dispatchWithoutSTI(SkinnyMethodAdapter mv, Label afterCall) { // retrieve method mv.aload(DISPATCHER_RUBYMODULE_INDEX); // module mv.aload(DISPATCHER_NAME_INDEX); // name mv.invokevirtual(p(RubyModule.class), "searchMethod", sig(DynamicMethod.class, params(String.class))); Label okCall = new Label(); callMethodMissingIfNecessary(mv, afterCall, okCall); // call is ok, punch it! mv.label(okCall); // method object already present, push various args mv.aload(DISPATCHER_THREADCONTEXT_INDEX); // tc mv.aload(DISPATCHER_SELF_INDEX); // self mv.aload(DISPATCHER_RUBYMODULE_INDEX); // klazz mv.aload(DISPATCHER_NAME_INDEX); // name mv.aload(DISPATCHER_ARGS_INDEX); mv.aload(DISPATCHER_BLOCK_INDEX); mv.invokevirtual(p(DynamicMethod.class), "call", sig(IRubyObject.class, params(ThreadContext.class, IRubyObject.class, RubyModule.class, String.class, IRubyObject[].class, Block.class))); } @Deprecated public void callMethodMissingIfNecessary(SkinnyMethodAdapter mv, Label afterCall, Label okCall) { Label methodMissing = new Label(); // if undefined, branch to method_missing mv.dup(); mv.invokevirtual(p(DynamicMethod.class), "isUndefined", sig(boolean.class)); mv.ifne(methodMissing); // if we're not attempting to invoke method_missing and method is not visible, branch to method_missing mv.aload(DISPATCHER_NAME_INDEX); mv.ldc("method_missing"); // if it's method_missing, just invoke it mv.invokevirtual(p(String.class), "equals", sig(boolean.class, params(Object.class))); mv.ifne(okCall); // check visibility mv.dup(); // dup method mv.aload(DISPATCHER_THREADCONTEXT_INDEX); mv.invokevirtual(p(ThreadContext.class), "getFrameSelf", sig(IRubyObject.class)); mv.aload(DISPATCHER_CALLTYPE_INDEX); mv.invokevirtual(p(DynamicMethod.class), "isCallableFrom", sig(boolean.class, params(IRubyObject.class, CallType.class))); mv.ifne(okCall); // invoke callMethodMissing mv.label(methodMissing); mv.aload(DISPATCHER_THREADCONTEXT_INDEX); // tc mv.swap(); // under method mv.aload(DISPATCHER_SELF_INDEX); // self mv.swap(); // under method mv.aload(DISPATCHER_NAME_INDEX); // name mv.aload(DISPATCHER_ARGS_INDEX); // args // caller mv.aload(DISPATCHER_THREADCONTEXT_INDEX); mv.invokevirtual(p(ThreadContext.class), "getFrameSelf", sig(IRubyObject.class)); mv.aload(DISPATCHER_CALLTYPE_INDEX); // calltype mv.aload(DISPATCHER_BLOCK_INDEX); // block // invoke callMethodMissing method directly // TODO: this could be further optimized, since some DSLs hit method_missing pretty hard... mv.invokestatic(p(RuntimeHelpers.class), "callMethodMissing", sig(IRubyObject.class, params(ThreadContext.class, IRubyObject.class, DynamicMethod.class, String.class, IRubyObject[].class, IRubyObject.class, CallType.class, Block.class))); // if no exception raised, jump to end to leave result on stack for return mv.go_to(afterCall); } @Deprecated private void loadArguments(MethodVisitor mv, int argsIndex, int count, Class[] types) { loadArguments(mv, argsIndex, count, types, false); } @Deprecated private void loadArguments(MethodVisitor mv, int argsIndex, int count, Class[] types, boolean contextProvided) { for (int i = 0; i < count; i++) { loadArgument(mv, argsIndex, i, types[i + (contextProvided ? 1 : 0)]); } } @Deprecated private void loadArgument(MethodVisitor mv, int argsIndex, int argIndex, Class type1) { mv.visitVarInsn(ALOAD, argsIndex); mv.visitLdcInsn(Integer.valueOf(argIndex)); mv.visitInsn(AALOAD); checkCast(mv, type1); } @Deprecated private void checkCast(MethodVisitor mv, Class clazz) { mv.visitTypeInsn(CHECKCAST, p(clazz)); } @Deprecated private void checkArity(SkinnyMethodAdapter mv, Arity arity) { if (arity.getValue() >= 0) { Label arityOk = new Label(); // check arity mv.aload(6); mv.arraylength(); // load arity for check switch (arity.getValue()) { case 3: mv.iconst_3(); break; case 2: mv.iconst_2(); break; case 1: mv.iconst_1(); break; case 0: mv.iconst_0(); break; default: mv.ldc(Integer.valueOf(arity.getValue())); } mv.if_icmpeq(arityOk); // throw mv.aload(9); mv.aload(6); mv.arraylength(); // load arity for error switch (arity.getValue()) { case 3: mv.iconst_3(); break; case 2: mv.iconst_2(); break; case 1: mv.iconst_1(); break; case 0: mv.iconst_0(); break; default: mv.ldc(Integer.valueOf(arity.getValue())); } mv.invokevirtual(p(Ruby.class), "newArgumentError", sig(RaiseException.class, params(int.class, int.class))); mv.athrow(); // arity ok, continue mv.label(arityOk); } } } //InvocationCallbackFactory