package org.jruby.java.proxies;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.function.Predicate;
import com.headius.backport9.modules.Module;
import com.headius.backport9.modules.Modules;
import org.jruby.AbstractRubyMethod;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
import org.jruby.RubyMethod;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyUnboundMethod;
import org.jruby.anno.JRubyMethod;
import org.jruby.common.IRubyWarnings;
import org.jruby.java.invokers.InstanceFieldGetter;
import org.jruby.java.invokers.InstanceFieldSetter;
import org.jruby.java.invokers.InstanceMethodInvoker;
import org.jruby.java.invokers.MethodInvoker;
import org.jruby.java.invokers.StaticFieldGetter;
import org.jruby.java.invokers.StaticFieldSetter;
import org.jruby.java.invokers.StaticMethodInvoker;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.JavaMethod;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.javasupport.binding.MethodGatherer;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
import org.jruby.util.ByteList;
import org.jruby.util.CodegenUtils;
import org.jruby.util.JRubyObjectInputStream;
import static org.jruby.runtime.Helpers.arrayOf;
public class JavaProxy extends RubyObject {
private transient JavaObject javaObject;
Object object;
public JavaProxy(Ruby runtime, RubyClass klazz) {
super(runtime, klazz);
}
public JavaProxy(Ruby runtime, RubyClass klazz, Object object) {
super(runtime, klazz);
this.object = object;
}
private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new JavaProxy(runtime, klazz);
}
};
public static RubyClass createJavaProxy(ThreadContext context) {
final Ruby runtime = context.runtime;
RubyClass JavaProxy = runtime.defineClass("JavaProxy", runtime.getObject(), ALLOCATOR);
JavaProxy.getSingletonClass().addReadWriteAttribute(context, "java_class");
JavaProxy.defineAnnotatedMethods(JavaProxy.class);
JavaProxy.includeModule(runtime.getModule("JavaProxyMethods"));
return JavaProxy;
}
@Override
public final Object dataGetStruct() {
if (javaObject == null) {
javaObject = asJavaObject(object);
}
return javaObject;
}
@Override
public final void dataWrapStruct(Object object) {
this.javaObject = (JavaObject) object;
this.object = javaObject.getValue();
}
public final Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
public Object unwrap() { return getObject(); }
protected JavaObject asJavaObject(final Object object) {
return JavaObject.wrap(getRuntime(), object);
}
@Override
public Class<?> getJavaClass() {
return getObject().getClass();
}
static JavaClass java_class(final ThreadContext context, final RubyModule module) {
return (JavaClass) JavaClass.java_class(context, module);
}
@JRubyMethod(meta = true, frame = true)
public static IRubyObject inherited(ThreadContext context, IRubyObject recv, IRubyObject subclass) {
IRubyObject subJavaClass = JavaClass.java_class(context, (RubyClass) subclass);
if (subJavaClass.isNil()) {
subJavaClass = JavaClass.java_class(context, (RubyClass) recv);
Helpers.invoke(context, subclass, "java_class=", subJavaClass);
}
return Helpers.invokeSuper(context, recv, subclass, Block.NULL_BLOCK);
}
@JRubyMethod(meta = true)
public static RubyClass singleton_class(final IRubyObject self) {
return ((RubyClass) self).getSingletonClass();
}
@JRubyMethod(name = "[]", meta = true, rest = true)
public static IRubyObject op_aref(ThreadContext context, IRubyObject self, IRubyObject[] args) {
final JavaClass javaClass = java_class(context, (RubyModule) self);
if ( args.length > 0 ) {
return new ArrayJavaProxyCreator(context, javaClass, args);
}
return Java.get_proxy_class(javaClass, Helpers.invoke(context, javaClass, "array_class"));
}
@JRubyMethod(meta = true)
public static IRubyObject new_array(ThreadContext context, IRubyObject self, IRubyObject len) {
final JavaClass javaClass = java_class(context, (RubyModule) self);
final Class<?> componentType = javaClass.javaClass();
final int length = (int) len.convertToInteger().getLongValue();
return ArrayJavaProxy.newArray(context.runtime, componentType, length);
}
@JRubyMethod(name = "__persistent__=", meta = true)
public static IRubyObject persistent(final IRubyObject clazz, final IRubyObject value) {
((RubyClass) clazz).getRealClass().setCacheProxy( value.isTrue() );
return value;
}
@JRubyMethod(name = "__persistent__", meta = true)
public static IRubyObject persistent(final ThreadContext context, final IRubyObject clazz) {
return RubyBoolean.newBoolean(context, ((RubyClass) clazz).getRealClass().getCacheProxy());
}
@Override
public IRubyObject initialize_copy(IRubyObject original) {
super.initialize_copy(original);
setObject( ((JavaProxy) original).cloneObject() );
return this;
}
protected Object cloneObject() {
final Object object = getObject();
if (object instanceof Cloneable) {
Object clone = JavaUtil.clone(object);
return clone == null ? object : clone;
}
return object;
}
private static Map<String, String> getFieldListFromArgs(ThreadContext context, IRubyObject[] args) {
final HashMap<String, String> map = new HashMap<>(args.length, 1);
for (int i = 0; i < args.length; i++) {
final IRubyObject arg = args[i];
if ( arg instanceof RubyHash ) {
((RubyHash) arg).visitAll(context, MapPopulatorVisitor, map);
} else {
String value = arg.asString().toString();
map.put(value, value);
}
}
return map;
}
private static final RubyHash.VisitorWithState<Map> MapPopulatorVisitor = new RubyHash.VisitorWithState<Map>() {
@Override
public void visit(ThreadContext context, RubyHash self, IRubyObject key, IRubyObject value, int index, Map map) {
map.put(key.asString().toString(), value.asString().toString());
}
};
private static void installField(final ThreadContext context,
final Map<String, String> fieldMap, final Field field,
final RubyModule module, boolean asReader, boolean asWriter) {
final String fieldName = field.getName();
final Iterator<Map.Entry<String,String>> iter = fieldMap.entrySet().iterator();
while ( iter.hasNext() ) {
final Map.Entry<String,String> entry = iter.next();
if ( entry.getKey().equals( fieldName ) ) {
installField(context, entry.getValue(), field, module, asReader, asWriter);
iter.remove(); break;
}
}
}
private static void installField(final ThreadContext context,
final String asName, final Field field, final RubyModule target,
boolean asReader, Boolean asWriter) {
if ( Ruby.isSecurityRestricted() && ! Modifier.isPublic(field.getModifiers()) ) {
throw context.runtime.newSecurityError("Cannot change accessibility on field in restricted mode '" + field + "'");
}
final String fieldName = field.getName();
if ( Modifier.isStatic(field.getModifiers()) ) {
if ( asReader ) {
target.getSingletonClass().addMethod(asName, new StaticFieldGetter(fieldName, target, field, false));
}
if ( asWriter == null || asWriter ) {
if ( Modifier.isFinal(field.getModifiers()) ) {
if ( asWriter == null ) return;
throw context.runtime.newSecurityError("Cannot change final field '" + field + "'");
}
target.getSingletonClass().addMethod(asName + '=', new StaticFieldSetter(fieldName, target, field));
}
} else {
if ( asReader ) {
target.addMethod(asName, new InstanceFieldGetter(fieldName, target, field));
}
if ( asWriter == null || asWriter ) {
if ( Modifier.isFinal(field.getModifiers()) ) {
if ( asWriter == null ) return;
throw context.runtime.newSecurityError("Cannot change final field '" + field + "'");
}
target.addMethod(asName + '=', new InstanceFieldSetter(fieldName, target, field));
}
}
}
public static void installField(final ThreadContext context,
final String asName, final Field field, final RubyModule target) {
installField(context, asName, field, target, true, null);
}
private static void findFields(final ThreadContext context,
final RubyModule topModule, final IRubyObject[] args,
final boolean asReader, final boolean asWriter) {
final Map<String, String> fieldMap = getFieldListFromArgs(context, args);
for (RubyModule module = topModule; module != null; module = module.getSuperClass()) {
final Class<?> javaClass = JavaClass.getJavaClassIfProxy(context, module);
if ( javaClass == null ) continue;
Field[] fields = JavaClass.getDeclaredFields(javaClass);
for (int j = 0; j < fields.length; j++) {
installField(context, fieldMap, fields[j], module, asReader, asWriter);
}
}
if ( ! fieldMap.isEmpty() ) {
throw JavaClass.undefinedFieldError(context.runtime, topModule.getName(), fieldMap.keySet().iterator().next());
}
}
@JRubyMethod(meta = true, rest = true)
public static IRubyObject field_accessor(ThreadContext context, IRubyObject self, IRubyObject[] args) {
findFields(context, (RubyModule) self, args, true, true);
return context.nil;
}
@JRubyMethod(meta = true, rest = true)
public static IRubyObject field_reader(ThreadContext context, IRubyObject self, IRubyObject[] args) {
findFields(context, (RubyModule) self, args, true, false);
return context.nil;
}
@JRubyMethod(meta = true, rest = true)
public static IRubyObject field_writer(ThreadContext context, IRubyObject self, IRubyObject[] args) {
findFields(context, (RubyModule) self, args, false, true);
return context.nil;
}
@Override
@JRubyMethod(name = "equal?")
public IRubyObject equal_p(ThreadContext context, IRubyObject other) {
if ( other instanceof JavaProxy ) {
boolean equal = getObject() == ((JavaProxy) other).getObject();
return RubyBoolean.newBoolean(context, equal);
}
return context.fals;
}
@JRubyMethod
public IRubyObject java_send(ThreadContext context, IRubyObject rubyName) {
String name = rubyName.asJavaString();
Ruby runtime = context.runtime;
JavaMethod method = new JavaMethod(runtime, getMethod(context, name));
return method.invokeDirect(context, getObject());
}
@JRubyMethod
public IRubyObject java_send(ThreadContext context, IRubyObject rubyName, IRubyObject argTypes) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Ruby runtime = context.runtime;
checkArgSizeMismatch(runtime, 0, argTypesAry);
JavaMethod method = new JavaMethod(runtime, getMethod(context, name));
return method.invokeDirect(context, getObject());
}
@JRubyMethod
public IRubyObject java_send(ThreadContext context, IRubyObject rubyName, IRubyObject argTypes, IRubyObject arg0) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Ruby runtime = context.runtime;
checkArgSizeMismatch(runtime, 1, argTypesAry);
Class argTypeClass = (Class) argTypesAry.eltInternal(0).toJava(Class.class);
JavaMethod method = new JavaMethod(runtime, getMethod(context, name, argTypeClass));
return method.invokeDirect(context, getObject(), arg0.toJava(argTypeClass));
}
@JRubyMethod(required = 1, rest = true)
public IRubyObject java_send(ThreadContext context, IRubyObject[] args) {
Ruby runtime = context.runtime;
String name = args[0].asJavaString();
RubyArray argTypesAry = args[1].convertToArray();
final int argsLen = args.length - 2;
checkArgSizeMismatch(runtime, argsLen, argTypesAry);
Class[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argsLen]);
Object[] javaArgs = new Object[argsLen];
for ( int i = 0; i < argsLen; i++ ) {
javaArgs[i] = args[i + 2].toJava( argTypesClasses[i] );
}
JavaMethod method = new JavaMethod(runtime, getMethod(context, name, argTypesClasses));
return method.invokeDirect(context, getObject(), javaArgs);
}
private static void checkArgSizeMismatch(final Ruby runtime, final int expected, final RubyArray argTypes) {
if ( argTypes.size() != expected ) {
Class[] argTypesClasses = (Class[]) argTypes.toArray(new Class[argTypes.size()]);
throw JavaMethod.newArgSizeMismatchError(runtime, argTypesClasses);
}
}
@JRubyMethod
public IRubyObject java_method(ThreadContext context, IRubyObject rubyName) {
String name = rubyName.asJavaString();
return getRubyMethod(context, name);
}
@JRubyMethod
public IRubyObject java_method(ThreadContext context, IRubyObject rubyName, IRubyObject argTypes) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Class[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argTypesAry.size()]);
return getRubyMethod(context, name, argTypesClasses);
}
@JRubyMethod
public IRubyObject marshal_dump() {
if ( ! Serializable.class.isAssignableFrom(object.getClass()) ) {
throw getRuntime().newTypeError("Java type is not serializable, cannot be marshaled " + getJavaClass());
}
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
new ObjectOutputStream(bytes).writeObject(object);
return getRuntime().newString(new ByteList(bytes.toByteArray(), false));
}
catch (IOException ex) {
throw getRuntime().newTypeError("Java type is not serializable: " + ex.getMessage());
}
}
@JRubyMethod
public IRubyObject marshal_load(final ThreadContext context, final IRubyObject str) {
try {
ByteList byteList = str.convertToString().getByteList();
ByteArrayInputStream bytes = new ByteArrayInputStream(byteList.getUnsafeBytes(), byteList.getBegin(), byteList.getRealSize());
this.object = new JRubyObjectInputStream(context.runtime, bytes).readObject();
return this;
}
catch (IOException ex) {
throw context.runtime.newIOErrorFromException(ex);
}
catch (ClassNotFoundException ex) {
throw context.runtime.newTypeError("Class not found unmarshaling Java type: " + ex.getLocalizedMessage());
}
}
@Override
protected int inspectHashCode() {
return System.identityHashCode(object);
}
private Method getMethod(ThreadContext context, String name, Class... argTypes) {
Class<?> originalClass = getObject().getClass();
Method[] holder = {null};
Predicate<Method[]> predicate = (classMethods) -> {
for (Method method : classMethods) {
if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), argTypes)) {
holder[0] = method;
return false;
}
}
return true;
};
MethodGatherer.eachAccessibleMethod(
originalClass,
predicate, predicate);
if (holder[0] != null) return holder[0];
throw JavaMethod.newMethodNotFoundError(context.runtime, originalClass, name + CodegenUtils.prettyParams(argTypes), name);
}
private MethodInvoker getMethodInvoker(Method method) {
if (Modifier.isStatic(method.getModifiers())) {
return new StaticMethodInvoker(metaClass.getMetaClass(), () -> arrayOf(method), method.getName());
} else {
return new InstanceMethodInvoker(metaClass, () -> arrayOf(method), method.getName());
}
}
private RubyMethod getRubyMethod(ThreadContext context, String name, Class... argTypes) {
Method jmethod = getMethod(context, name, argTypes);
RubyClass sourceModule;
if (Modifier.isStatic(jmethod.getModifiers())) {
sourceModule = metaClass.getSingletonClass();
return RubyMethod.newMethod(
sourceModule,
CodegenUtils.prettyParams(argTypes).toString(),
sourceModule,
name,
new CacheEntry(getMethodInvoker(jmethod), sourceModule, metaClass.getGeneration()),
getMetaClass());
} else {
sourceModule = metaClass;
return RubyMethod.newMethod(
sourceModule,
CodegenUtils.prettyParams(argTypes).toString(),
sourceModule,
name,
new CacheEntry(getMethodInvoker(jmethod), sourceModule, metaClass.getGeneration()),
this);
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T toJava(Class<T> type) {
final Object object = getObject();
final Class<?> clazz = object.getClass();
if ( type.isAssignableFrom(clazz) ) return type.cast(object);
if ( type.isAssignableFrom(getClass()) ) return type.cast(this);
throw getRuntime().newTypeError("failed to coerce " + clazz.getName() + " to " + type.getName());
}
@Override
public Object getVariable(int index) {
confirmCachedProxy(NONPERSISTENT_IVAR_MESSAGE);
return super.getVariable(index);
}
@Override
public void setVariable(int index, Object value) {
confirmCachedProxy(NONPERSISTENT_IVAR_MESSAGE);
super.setVariable(index, value);
}
@Override
public RubyClass getSingletonClass() {
confirmCachedProxy(NONPERSISTENT_SINGLETON_MESSAGE);
return super.getSingletonClass();
}
private void confirmCachedProxy(String message) {
final RubyClass realClass = metaClass.getRealClass();
if ( ! realClass.getCacheProxy() ) {
final Ruby runtime = getRuntime();
if (Java.OBJECT_PROXY_CACHE) {
runtime.getWarnings().warnOnce(IRubyWarnings.ID.NON_PERSISTENT_JAVA_PROXY, MessageFormat.format(message, realClass));
} else {
runtime.getWarnings().warn(MessageFormat.format(message, realClass));
realClass.setCacheProxy(true);
runtime.getJavaSupport().getObjectProxyCache().put(getObject(), this);
}
}
}
private static final String NONPERSISTENT_IVAR_MESSAGE = "instance vars on non-persistent Java type {0} (http://wiki.jruby.org/Persistence)";
private static final String NONPERSISTENT_SINGLETON_MESSAGE = "singleton on non-persistent Java type {0} (http://wiki.jruby.org/Persistence)";
public static class ClassMethods {
@JRubyMethod(name = "const_missing", required = 1, meta = true, visibility = Visibility.PRIVATE, frame = true)
public static IRubyObject const_missing(ThreadContext context, IRubyObject self, IRubyObject name) {
return Java.get_inner_class(context, (RubyModule) self, name);
}
@JRubyMethod(meta = true)
public static IRubyObject java_method(ThreadContext context, IRubyObject proxyClass, IRubyObject rubyName) {
String name = rubyName.asJavaString();
return getRubyMethod(context, proxyClass, name);
}
@JRubyMethod(meta = true)
public static IRubyObject java_method(ThreadContext context, IRubyObject proxyClass, IRubyObject rubyName, IRubyObject argTypes) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argTypesAry.size()]);
return getRubyMethod(context, proxyClass, name, argTypesClasses);
}
@JRubyMethod(meta = true)
public static IRubyObject java_send(ThreadContext context, IRubyObject recv, IRubyObject rubyName) {
String name = rubyName.asJavaString();
final Ruby runtime = context.runtime;
JavaMethod method = new JavaMethod(runtime, getMethodFromClass(context, recv, name));
return method.invokeStaticDirect(context);
}
@JRubyMethod(meta = true)
public static IRubyObject java_send(ThreadContext context, IRubyObject recv, IRubyObject rubyName, IRubyObject argTypes) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
final Ruby runtime = context.runtime;
checkArgSizeMismatch(runtime, 0, argTypesAry);
JavaMethod method = new JavaMethod(runtime, getMethodFromClass(context, recv, name));
return method.invokeStaticDirect(context);
}
@JRubyMethod(meta = true)
public static IRubyObject java_send(ThreadContext context, IRubyObject recv, IRubyObject rubyName, IRubyObject argTypes, IRubyObject arg0) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
final Ruby runtime = context.runtime;
checkArgSizeMismatch(runtime, 1, argTypesAry);
Class argTypeClass = (Class) argTypesAry.eltInternal(0).toJava(Class.class);
JavaMethod method = new JavaMethod(runtime, getMethodFromClass(context, recv, name, argTypeClass));
return method.invokeStaticDirect(context, arg0.toJava(argTypeClass));
}
@JRubyMethod(required = 1, rest = true, meta = true)
public static IRubyObject java_send(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
switch (args.length) {
case 1: return java_send(context, recv, args[0]);
case 2: return java_send(context, recv, args[0], args[1]);
case 3: return java_send(context, recv, args[0], args[1], args[2]);
}
final Ruby runtime = context.runtime;
String name = args[0].asJavaString();
RubyArray argTypesAry = args[1].convertToArray();
final int argsLen = args.length - 2;
checkArgSizeMismatch(runtime, argsLen, argTypesAry);
Class[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argsLen]);
Object[] javaArgs = new Object[argsLen];
for ( int i = 0; i < argsLen; i++ ) {
javaArgs[i] = args[i + 2].toJava( argTypesClasses[i] );
}
JavaMethod method = new JavaMethod(runtime, getMethodFromClass(context, recv, name, argTypesClasses));
return method.invokeStaticDirect(context, javaArgs);
}
@JRubyMethod(meta = true, visibility = Visibility.PRIVATE)
public static IRubyObject java_alias(ThreadContext context, IRubyObject clazz, IRubyObject newName, IRubyObject rubyName) {
return java_alias(context, clazz, newName, rubyName, context.runtime.newEmptyArray());
}
@JRubyMethod(meta = true, visibility = Visibility.PRIVATE)
public static IRubyObject java_alias(ThreadContext context, IRubyObject clazz, IRubyObject newName, IRubyObject rubyName, IRubyObject argTypes) {
final Ruby runtime = context.runtime;
if ( ! ( clazz instanceof RubyClass ) ) {
throw runtime.newTypeError(clazz, runtime.getModule());
}
final RubyClass proxyClass = (RubyClass) clazz;
String name = rubyName.asJavaString();
String newNameStr = newName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Class<?>[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argTypesAry.size()]);
final Method method = getMethodFromClass(context, clazz, name, argTypesClasses);
final MethodInvoker invoker;
if ( Modifier.isStatic( method.getModifiers() ) ) {
invoker = new StaticMethodInvoker(proxyClass.getMetaClass(), () -> arrayOf(method), newNameStr);
proxyClass.getSingletonClass().addMethod(newNameStr, invoker);
}
else {
invoker = new InstanceMethodInvoker(proxyClass, () -> arrayOf(method), newNameStr);
proxyClass.addMethod(newNameStr, invoker);
}
return context.nil;
}
private static AbstractRubyMethod getRubyMethod(ThreadContext context, IRubyObject clazz, String name, Class... argTypesClasses) {
final Ruby runtime = context.runtime;
if ( ! ( clazz instanceof RubyModule ) ) {
throw runtime.newTypeError(clazz, runtime.getModule());
}
final RubyModule proxyClass = (RubyModule) clazz;
final Method method = getMethodFromClass(context, clazz, name, argTypesClasses);
final String prettyName = name + CodegenUtils.prettyParams(argTypesClasses);
if ( Modifier.isStatic( method.getModifiers() ) ) {
MethodInvoker invoker = new StaticMethodInvoker(proxyClass, () -> arrayOf(method), name);
return RubyMethod.newMethod(proxyClass, prettyName, proxyClass, name, new CacheEntry(invoker, proxyClass, proxyClass.getGeneration()), clazz);
}
MethodInvoker invoker = new InstanceMethodInvoker(proxyClass, () -> arrayOf(method), name);
return RubyUnboundMethod.newUnboundMethod(proxyClass, prettyName, proxyClass, name, new CacheEntry(invoker, proxyClass, proxyClass.getGeneration()));
}
private static Method getMethodFromClass(final ThreadContext context, final IRubyObject proxyClass,
final String name, final Class... argTypes) {
final Class<?> clazz = JavaClass.getJavaClass(context, (RubyModule) proxyClass);
try {
return clazz.getMethod(name, argTypes);
}
catch (NoSuchMethodException nsme) {
String prettyName = name + CodegenUtils.prettyParams(argTypes);
String errorName = clazz.getName() + '.' + prettyName;
throw context.runtime.newNameError("Java method not found: " + errorName, name);
}
}
}
}