/***** 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 java.util.Iterator;
import java.util.Map;
import org.jruby.Ruby;
import org.jruby.RubyClass;
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.AbstractCompiledBlockCallback;
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.MethodIndex;
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;
private final static String SUPER_CLASS = p(InvocationCallback.class);
private final static String FAST_SUPER_CLASS = p(FastInvocationCallback.class);
private final static String CALL_SIG = sig(RubyKernel.IRUBY_OBJECT, params(Object.class,
Object[].class, Block.class));
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 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;
}
private ClassWriter createBlockCtor(String namePath) throws Exception {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, namePath, null, p(AbstractCompiledBlockCallback.class), null);
cw.visitField(ACC_PRIVATE | ACC_FINAL, "$scriptObject", ci(Object.class), null, null);
SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw.visitMethod(ACC_PUBLIC, "<init>", sig(Void.TYPE, params(Object.class)), null, null));
mv.start();
mv.aload(0);
mv.aload(1);
mv.invokespecial(p(AbstractCompiledBlockCallback.class), "<init>", sig(void.class, Object.class));
mv.voidreturn();
mv.end();
return cw;
}
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;
}
private SkinnyMethodAdapter startBlockCall(ClassWriter cw) {
SkinnyMethodAdapter mv = new SkinnyMethodAdapter(cw.visitMethod(ACC_PUBLIC | ACC_SYNTHETIC | ACC_FINAL, "call", BLOCK_CALL_SIG, null, null));
mv.visitCode();
Label line = new Label();
mv.visitLineNumber(0, line);
return mv;
}
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);
}
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);
SkinnyMethodAdapter mv = startBlockCall(cw);
mv.aload(0);
mv.getfield(p(AbstractCompiledBlockCallback.class), "$scriptObject", ci(Object.class));
mv.checkcast(p(typeClass));
mv.aload(1);
mv.aload(2);
mv.aload(3);
mv.invokevirtual(typePathString, method, sig(
RubyKernel.IRUBY_OBJECT, params(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 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(new Integer(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(new Integer(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(new Integer(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