All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.java.proxies.JavaProxy Maven / Gradle / Ivy

There is a newer version: 9.4.9.0
Show newest version
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.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.jruby.AbstractRubyMethod;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyClass;
import org.jruby.RubyHash;
import org.jruby.RubyHash.Visitor;
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.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.util.ByteList;
import org.jruby.util.CodegenUtils;
import org.jruby.util.JRubyObjectInputStream;

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 Object dataGetStruct() {
        return getJavaObject();
    }

    @Override
    public void dataWrapStruct(Object object) {
        this.javaObject = (JavaObject) object;
        this.object = javaObject.getValue();
    }

    public Object getObject() {
        // FIXME: Added this because marshal_spec seemed to reconstitute objects without calling dataWrapStruct
        // this resulted in object being null after unmarshalling...
        if (object == null) {
            if (javaObject == null) {
                throw getRuntime().newRuntimeError("Java wrapper with no contents: " + this.getMetaClass().getName());
            } else {
                object = javaObject.getValue();
            }
        }
        return object;
    }

    public void setObject(Object object) {
        this.object = object;
    }

    public Object unwrap() { return getObject(); }

    private JavaObject getJavaObject() {
        if (javaObject == null) {
            javaObject = JavaObject.wrap(getRuntime(), object);
        }
        return javaObject;
    }

    @Override
    public Class getJavaClass() {
        return getObject().getClass();
    }

    static JavaClass java_class(final ThreadContext context, final RubyModule module) {
        return (JavaClass) Helpers.invoke(context, module, "java_class");
    }

    @JRubyMethod(meta = true, frame = true) // framed for invokeSuper
    public static IRubyObject inherited(ThreadContext context, IRubyObject recv, IRubyObject subclass) {
        IRubyObject subJavaClass = Helpers.invoke(context, subclass, "java_class");
        if (subJavaClass.isNil()) {
            subJavaClass = Helpers.invoke(context, recv, "java_class");
            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 ) { // construct new array proxy (ArrayJavaProxy)
            return new ArrayJavaProxyCreator(context, javaClass, args); // e.g. Byte[64]
        }
        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 context.runtime.newBoolean(((RubyClass) clazz).getRealClass().getCacheProxy());
    }

    @Override
    public IRubyObject initialize_copy(IRubyObject original) {
        super.initialize_copy(original);
        // because we lazily init JavaObject in the data-wrapped slot, explicitly copy over the object
        setObject( ((JavaProxy) original).getObject() );
        return this;
    }

    /**
     * Create a name/newname map of fields to be exposed as methods.
     */
    private static Map getFieldListFromArgs(final IRubyObject[] args) {
        final Map map = new HashMap();
        // Get map of all fields we want to define.
        for (int i = 0; i < args.length; i++) {
            final IRubyObject arg = args[i];
            if ( arg instanceof RubyHash ) {
                ((RubyHash) arg).visitAll(new Visitor() {
                    @Override
                    public void visit(IRubyObject key, IRubyObject value) {
                        map.put(key.asString().toString(), value.asString().toString());
                    }
                });
            } else {
                String value = arg.asString().toString();
                map.put(value, value);
            }
        }
        return map;
    }

    // Look through all mappings to find a match entry for this field
    private static void installField(final ThreadContext context,
        final Map fieldMap, final Field field,
        final RubyModule module, boolean asReader, boolean asWriter) {

        final String fieldName = field.getName();

        final Iterator> iter = fieldMap.entrySet().iterator();
        while ( iter.hasNext() ) {
            final Map.Entry entry = iter.next();
            if ( entry.getKey().equals( fieldName ) ) {

                if ( Ruby.isSecurityRestricted() && ! Modifier.isPublic(field.getModifiers()) ) {
                    throw context.runtime.newSecurityError("Cannot change accessibility on fields in a restricted mode: field '" + fieldName + "'");
                }

                String asName = entry.getValue();

                if ( Modifier.isStatic(field.getModifiers()) ) {
                    if ( asReader ) {
                        module.getSingletonClass().addMethod(asName, new StaticFieldGetter(fieldName, module, field));
                    }
                    if ( asWriter ) {
                        if ( Modifier.isFinal(field.getModifiers()) ) {
                            throw context.runtime.newSecurityError("Cannot change final field '" + fieldName + "'");
                        }
                        module.getSingletonClass().addMethod(asName + '=', new StaticFieldSetter(fieldName, module, field));
                    }
                } else {
                    if ( asReader ) {
                        module.addMethod(asName, new InstanceFieldGetter(fieldName, module, field));
                    }
                    if ( asWriter ) {
                        if ( Modifier.isFinal(field.getModifiers()) ) {
                            throw context.runtime.newSecurityError("Cannot change final field '" + fieldName + "'");
                        }
                        module.addMethod(asName + '=', new InstanceFieldSetter(fieldName, module, field));
                    }
                }

                iter.remove();
                break;
            }
        }
    }

    private static void findFields(final ThreadContext context,
        final RubyModule topModule, final IRubyObject[] args,
        final boolean asReader, final boolean asWriter) {
        final Map fieldMap = getFieldListFromArgs(args);

        for (RubyModule module = topModule; module != null; module = module.getSuperClass()) {
            final Class javaClass = JavaClass.getJavaClassIfProxy(context, module);
            // Hit a non-java proxy class (included Modules can be a cause of this...skip)
            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);
            }
        }

        // We could not find all of them print out first one (we could print them all?)
        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 context.runtime.newBoolean(equal);
        }
        return context.runtime.getFalse();
    }

    @JRubyMethod
    public IRubyObject java_send(ThreadContext context, IRubyObject rubyName) {
        String name = rubyName.asJavaString();
        Ruby runtime = context.runtime;

        JavaMethod method = new JavaMethod(runtime, getMethod(name));
        return method.invokeDirect(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(name));
        return method.invokeDirect(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(name, argTypeClass));
        return method.invokeDirect(getObject(), arg0.toJava(argTypeClass));
    }

    @JRubyMethod(required = 4, 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(name, argTypesClasses));
        return method.invokeDirect(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(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(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());
        }
    }

    /**
     * We override RubyBasicObject.inspectHashCode to be the identity hash of
     * the contained object, so it remains consistent across wrappers.
     *
     * @return The identity hashcode of the wrapped object
     */
    @Override
    protected int inspectHashCode() {
        return System.identityHashCode(object);
    }

    private Method getMethod(String name, Class... argTypes) {
        try {
            return getObject().getClass().getMethod(name, argTypes);
        } catch (NoSuchMethodException nsme) {
            throw JavaMethod.newMethodNotFoundError(getRuntime(), getObject().getClass(), name + CodegenUtils.prettyParams(argTypes), name);
        }
    }

    private MethodInvoker getMethodInvoker(Method method) {
        if (Modifier.isStatic(method.getModifiers())) {
            return new StaticMethodInvoker(metaClass.getMetaClass(), method);
        } else {
            return new InstanceMethodInvoker(metaClass, method);
        }
    }

    private RubyMethod getRubyMethod(String name, Class... argTypes) {
        Method jmethod = getMethod(name, argTypes);
        if (Modifier.isStatic(jmethod.getModifiers())) {
            return RubyMethod.newMethod(metaClass.getSingletonClass(), CodegenUtils.prettyParams(argTypes).toString(), metaClass.getSingletonClass(), name, getMethodInvoker(jmethod), getMetaClass());
        } else {
            return RubyMethod.newMethod(metaClass, CodegenUtils.prettyParams(argTypes).toString(), metaClass, name, getMethodInvoker(jmethod), this);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Object toJava(final Class type) {
        final Object object = getObject();
        final Class clazz = object.getClass();

        if ( type.isAssignableFrom(clazz) ) return object;

        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);
    }

    /** rb_singleton_class
     *
     * Note: this method is specialized for RubyFixnum, RubySymbol,
     * RubyNil and RubyBoolean
     *
     * Will either return the existing singleton class for this
     * object, or create a new one and return that.
     */
    @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(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();
        }

        @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();
        }

        @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(arg0.toJava(argTypeClass));
        }

        @JRubyMethod(required = 4, 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(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(), method);
                // add alias to meta
                proxyClass.getSingletonClass().addMethod(newNameStr, invoker);
            }
            else {
                invoker = new InstanceMethodInvoker(proxyClass, method);
                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, method);
                return RubyMethod.newMethod(proxyClass, prettyName, proxyClass, name, invoker, clazz);
            }

            MethodInvoker invoker = new InstanceMethodInvoker(proxyClass, method);
            return RubyUnboundMethod.newUnboundMethod(proxyClass, prettyName, proxyClass, name, invoker);
        }

        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);
            }
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy