Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  package org.jruby.java.dispatch;
  
  import java.util.Arrays;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import org.jruby.Ruby;
Method selection logic for calling from Ruby to Java.
 
 public class CallableSelector {
     public static ParameterTypes matchingCallableArityN(Ruby runtimeMap cacheParameterTypes[] methodsIRubyObject[] argsint argsLength) {
         int signatureCode = argsHashCode(args);
         ParameterTypes method = (ParameterTypes)cache.get(signatureCode);
         if (method == null) {
             method = findMatchingCallableForArgs(runtimecachesignatureCodemethodsargs);
         }
         return method;
     }
 
     // NOTE: The five match methods are arity-split to avoid the cost of boxing arguments
     // when there's already a cached match. Do not condense them into a single
     // method.
     public static JavaCallable matchingCallableArityN(Ruby runtimeMap cacheJavaCallable[] methodsIRubyObject[] argsint argsLength) {
         int signatureCode = argsHashCode(args);
         JavaCallable method = (JavaCallable)cache.get(signatureCode);
         if (method == null) {
             method = (JavaCallable)findMatchingCallableForArgs(runtimecachesignatureCodemethodsargs);
         }
         return method;
     }
 
     public static JavaCallable matchingCallableArityOne(Ruby runtimeMap cacheJavaCallable[] methodsIRubyObject arg0) {
         int signatureCode = argsHashCode(arg0);
         JavaCallable method = (JavaCallable)cache.get(signatureCode);
         if (method == null) {
             method = (JavaCallable)findMatchingCallableForArgs(runtimecachesignatureCodemethodsarg0);
         }
         return method;
     }
 
     public static JavaCallable matchingCallableArityTwo(Ruby runtimeMap cacheJavaCallable[] methodsIRubyObject arg0IRubyObject arg1) {
         int signatureCode = argsHashCode(arg0arg1);
         JavaCallable method = (JavaCallable)cache.get(signatureCode);
         if (method == null) {
             method = (JavaCallable)findMatchingCallableForArgs(runtimecachesignatureCodemethodsarg0arg1);
         }
         return method;
     }
 
     public static JavaCallable matchingCallableArityThree(Ruby runtimeMap cacheJavaCallable[] methodsIRubyObject arg0IRubyObject arg1IRubyObject arg2) {
         int signatureCode = argsHashCode(arg0arg1arg2);
         JavaCallable method = (JavaCallable)cache.get(signatureCode);
         if (method == null) {
             method = (JavaCallable)findMatchingCallableForArgs(runtimecachesignatureCodemethodsarg0arg1arg2);
         }
         return method;
     }
 
     public static JavaCallable matchingCallableArityFour(Ruby runtimeMap cacheJavaCallable[] methodsIRubyObject arg0IRubyObject arg1IRubyObject arg2IRubyObject arg3) {
         int signatureCode = argsHashCode(arg0arg1arg2arg3);
         JavaCallable method = (JavaCallable)cache.get(signatureCode);
         if (method == null) {
             method = (JavaCallable)findMatchingCallableForArgs(runtimecachesignatureCodemethodsarg0arg1arg2arg3);
         }
         return method;
     }
 
     private static final boolean DEBUG = true;
 
     private static ParameterTypes findMatchingCallableForArgs(Ruby runtimeMap cacheint signatureCodeParameterTypes[] methodsIRubyObject... args) {
         ParameterTypes method = null;
 
         // try the new way first
         List<ParameterTypesnewFinds = findCallable(methodsargs);
         if (newFinds.size() > 0) {
             // new way found one, so let's go with that
             if (newFinds.size() == 1) {
                 method = newFinds.get(0);
             } else {
                 // narrow to most specific version (or first version, if none are more specific
                 ParameterTypes mostSpecific = null;
                Class[] msTypes = null;
                boolean ambiguous = false;
                OUTER: for (ParameterTypes candidate : newFinds) {
                    if (mostSpecific == null) {
                        mostSpecific = candidate;
                        msTypes = mostSpecific.getParameterTypes();
                        continue;
                    }
                    Class[] cTypes = candidate.getParameterTypes();
                    for (int i = 0; i < msTypes.lengthi++) {
                        if (msTypes[i] != cTypes[i] && msTypes[i].isAssignableFrom(cTypes[i])) {
                            mostSpecific = candidate;
                            msTypes = cTypes;
                            ambiguous = false;
                            continue OUTER;
                        }
                    }
                    // none more specific; check for ambiguities
                    for (int i = 0; i < msTypes.lengthi++) {
                        if (msTypes[i] != cTypes[i] && !msTypes[i].isAssignableFrom(cTypes[i]) && !cTypes[i].isAssignableFrom(msTypes[i])) {
                            ambiguous = true;
                        } else {
                            ambiguous = false;
                            continue OUTER;
                        }
                    }
                }
                method = mostSpecific;
                if (ambiguous) {
                    runtime.getWarnings().warn("ambiguous Java methods found, using " + ((Member) ((JavaCallablemethod).accessibleObject()).getName() + CodegenUtils.prettyParams(msTypes));
                }
            }
        }
        // fall back on old ways
        if (method == null) {
            method = findCallable(methodsargs);
        }
        if (method == null) {
            method = findCallable(methodsargs);
        }
        if (method == null) {
            method = findCallable(methodsargs);
        }
        if (method == null) {
            method = findCallable(methodsargs);
        }
        
        // cache found result
        if (method != nullcache.put(signatureCodemethod);
        
        return method;
    }
    private static void warnMultipleMatches(IRubyObject[] argsList<ParameterTypesnewFinds) {
        RubyClass[] argTypes = new RubyClass[args.length];
        for (int i = 0; i < argTypes.lengthi++) {
            argTypes[i] = args[i].getMetaClass();
        }
        StringBuilder builder = new StringBuilder("multiple Java methods for arguments (");
        boolean first = true;
        for (RubyClass argType : argTypes) {
            if (!first) {
                builder.append(",");
            }
            first = false;
            builder.append(argType);
        }
        builder.append("), using first:");
        for (ParameterTypes types : newFinds) {
            builder.append("\n  ").append(types);
        }
        args[0].getRuntime().getWarnings().warn(builder.toString());
    }
    private static ParameterTypes findCallable(ParameterTypes[] callablesCallableAcceptor acceptorIRubyObject... args) {
        ParameterTypes bestCallable = null;
        int bestScore = -1;
        for (int k = 0; k < callables.lengthk++) {
            ParameterTypes callable = callables[k];
            if (acceptor.accept(callableargs)) {
                int currentScore = getExactnessScore(callableargs);
                if (currentScore > bestScore) {
                    bestCallable = callable;
                    bestScore = currentScore;
                }
            }
        }
        return bestCallable;
    }
    private static List<ParameterTypesfindCallable(ParameterTypes[] callablesIRubyObject... args) {
        List<ParameterTypesretainedCallables = new ArrayList<ParameterTypes>(callables.length);
        List<ParameterTypesincomingCallables = new ArrayList<ParameterTypes>(Arrays.asList(callables));
        
        for (int currentArg = 0; currentArg < args.lengthcurrentArg++) {
            retainedCallables.clear();
            for (Matcher matcher : ) {
                for (Iterator<ParameterTypescallableIter = incomingCallables.iterator(); callableIter.hasNext();) {
                    ParameterTypes callable = callableIter.next();
                    Class[] types = callable.getParameterTypes();
                    if (matcher.match(types[currentArg], args[currentArg])) {
                        callableIter.remove();
                        retainedCallables.add(callable);
                    }
                }
            }
            incomingCallables.clear();
            incomingCallables.addAll(retainedCallables);
        }
        return retainedCallables;
    }
    private static int getExactnessScore(ParameterTypes paramTypesIRubyObject[] args) {
        Class[] types = paramTypes.getParameterTypes();
        int count = 0;
        if (paramTypes.isVarArgs()) {
            // varargs exactness gives the last N args as +1 since they'll already
            // have been determined to fit
            // dig out as many trailing args as possible that match varargs type
            int nonVarargs = types.length - 1;
            
            // add one for vararg
            count += 1;
            // check remaining args
            for (int i = 0; i < nonVarargs && i < args.lengthi++) {
                if (types[i].equals(argClass(args[i]))) {
                    count++;
                }
            }
        } else {
            for (int i = 0; i < args.lengthi++) {
                if (types[i].equals(argClass(args[i]))) {
                    count++;
                }
            }
        }
        return count;
    }
    private static interface CallableAcceptor {
        public boolean accept(ParameterTypes typesIRubyObject[] args);
    }
    private static final CallableAcceptor Exact = new CallableAcceptor() {
        public boolean accept(ParameterTypes typesIRubyObject[] args) {
            return exactMatch(typesargs);
        }
    };
    private static final CallableAcceptor AssignableAndPrimitivable = new CallableAcceptor() {
        public boolean accept(ParameterTypes typesIRubyObject[] args) {
            return assignableAndPrimitivable(typesargs);
        }
    };
    private static final CallableAcceptor AssignableOrDuckable = new CallableAcceptor() {
        public boolean accept(ParameterTypes typesIRubyObject[] args) {
            return assignableOrDuckable(typesargs);
        }
    };
    private static final CallableAcceptor AssignableAndPrimitivableWithVarargs = new CallableAcceptor() {
        public boolean accept(ParameterTypes typesIRubyObject[] args) {
            return assignableAndPrimitivableWithVarargs(typesargs);
        }
    };
    private interface Matcher {
        public boolean match(Class typeIRubyObject arg);
    }
    private static boolean exactMatch(ParameterTypes paramTypesIRubyObject... args) {
        Class[] types = paramTypes.getParameterTypes();
        
        if (args.length != types.lengthreturn false;
        
        for (int i = 0; i < types.lengthi++) {
            if (!.match(types[i], args[i])) {
                return false;
            }
        }
        return true;
    }
    private static Matcher EXACT = new Matcher() {
        public boolean match(Class typeIRubyObject arg) {
            return type.equals(argClass(arg))
                    || (type.isPrimitive() && CodegenUtils.getBoxType(type) == argClass(arg));
        }
    };
    private static Matcher ASSIGNABLE = new Matcher() {
        public boolean match(Class typeIRubyObject arg) {
            return assignable(typearg);
        }
    };
    private static Matcher PRIMITIVABLE = new Matcher() {
        public boolean match(Class typeIRubyObject arg) {
            return primitivable(typearg);
        }
    };
    private static Matcher DUCKABLE = new Matcher() {
        public boolean match(Class typeIRubyObject arg) {
            return duckable(typearg);
        }
    };
    private static final Matcher[] MATCH_SEQUENCE = new Matcher[] {};
    private static boolean assignableAndPrimitivable(ParameterTypes paramTypesIRubyObject... args) {
        Class[] types = paramTypes.getParameterTypes();
        
        if (args.length != types.lengthreturn false;
        
        for (int i = 0; i < types.lengthi++) {
            if (!(.match(types[i], args[i]) && .match(types[i], args[i]))) {
                return false;
            }
        }
        return true;
    }
    private static boolean assignableOrDuckable(ParameterTypes paramTypesIRubyObject... args) {
        Class[] types = paramTypes.getParameterTypes();
        
        if (args.length != types.lengthreturn false;
        
        for (int i = 0; i < types.lengthi++) {
            if (!(.match(types[i], args[i]) || .match(types[i], args[i]))) {
                return false;
            }
        }
        return true;
    }
    private static boolean assignableAndPrimitivableWithVarargs(ParameterTypes paramTypesIRubyObject... args) {
        // bail out if this is not a varargs method
        if (!paramTypes.isVarArgs()) return false;
        
        Class[] types = paramTypes.getParameterTypes();
        Class varArgArrayType = types[types.length - 1];
        Class varArgType = varArgArrayType.getComponentType();
        
        // if there's no args, we only match when there's just varargs
        if (args.length == 0) {
            return types.length <= 1;
        }
        // dig out as many trailing args as will fit, ensuring they match varargs type
        int nonVarargs = types.length - 1;
        for (int i = args.length - 1; i >= nonVarargsi--) {
            if (!(.match(varArgTypeargs[i]) || .match(varArgTypeargs[i]))) {
                return false;
            }
        }
        // check remaining args
        for (int i = 0; i < nonVarargsi++) {
            if (!(.match(types[i], args[i]) || .match(types[i], args[i]))) {
                return false;
            }
        }
        return true;
    }
    private static boolean assignable(Class typeIRubyObject arg) {
        return JavaClass.assignable(typeargClass(arg));
    }

    
This method checks whether an argument can be *directly* converted into the target primitive, i.e. without changing from integral to floating-point.

Parameters:
type The target type
arg The argument to convert
Returns:
Whether the argument can be directly converted to the target primitive type
    private static boolean primitivable(Class typeIRubyObject arg) {
        Class argClass = argClass(arg);
        if (type.isPrimitive()) {
            // TODO: This is where we would want to do precision checks to see
            // if it's non-destructive to coerce a given type into the target
            // integral primitive
            if (type == . || type == . || type == . || type == .) {
                return argClass == long.class || // long first because it's what Fixnum claims to be
                        argClass == byte.class ||
                        argClass == short.class ||
                        argClass == char.class ||
                        argClass == int.class ||
                        argClass == Long.class ||
                        argClass == Byte.class ||
                        argClass == Short.class ||
                        argClass == Character.class ||
                        argClass == Integer.class;
            } else if (type == . || type == .) {
                return argClass == double.class || // double first because it's what float claims to be
                        argClass == float.class ||
                        argClass == Float.class ||
                        argClass == Double.class;
            } else if (type == .) {
                return argClass == boolean.class ||
                        argClass == Boolean.class;
            }
        }
        return false;
    }
    private static boolean duckable(Class typeIRubyObject arg) {
        return JavaUtil.isDuckTypeConvertable(argClass(arg), type);
    }
    private static int argsHashCode(IRubyObject a0) {
        return 31 + classHashCode(a0);
    }
    private static int argsHashCode(IRubyObject a0IRubyObject a1) {
        return 31 * argsHashCode(a0) + classHashCode(a1);
    }
    private static int argsHashCode(IRubyObject a0IRubyObject a1IRubyObject a2) {
        return 31 * argsHashCode(a0a1) + classHashCode(a2);
    }
    private static int argsHashCode(IRubyObject a0IRubyObject a1IRubyObject a2IRubyObject a3) {
        return 31 * argsHashCode(a0a1a2) + classHashCode(a3);
    }
    private static int argsHashCode(IRubyObject[] a) {
        if (a == null) {
            return 0;
        }
        int result = 1;
        for (IRubyObject element : a) {
            result = 31 * result + classHashCode(element);
        }
        return result;
    }
    private static int classHashCode(IRubyObject o) {
        return o == null ? 0 : o.getJavaClass().hashCode();
    }
    private static Class argClass(IRubyObject a) {
        if (a == null) {
            return void.class;
        }
        return a.getJavaClass();
    }
    public static RaiseException argTypesDoNotMatch(Ruby runtimeIRubyObject receiverJavaCallable[] methodsObject... args) {
        Class[] argTypes = new Class[args.length];
        for (int i = 0; i < args.lengthi++) {
            argTypes[i] = argClassTypeError(args[i]);
        }
        return argumentError(runtime.getCurrentContext(), methodsreceiverargTypes);
    }
    private static Class argClassTypeError(Object object) {
        if (object == null) {
            return void.class;
        }
        if (object instanceof ConcreteJavaProxy) {
            return ((ConcreteJavaProxy)object).getJavaClass();
        }
        return object.getClass();
    }
    private static RaiseException argumentError(ThreadContext contextParameterTypes[] methodsIRubyObject receiverClass[] argTypes) {
        boolean constructor = methods[0] instanceof JavaConstructor || methods[0] instanceof JavaProxyConstructor;
        
        StringBuffer fullError = new StringBuffer();
        fullError.append("no ");
        if (constructor) {
            fullError.append("constructor");
        } else {
            fullError.append("method '")
                    .append(((JavaMethod)methods[0]).name().toString())
                    .append("' ");
        }
        fullError.append("for arguments ")
                .append(CodegenUtils.prettyParams(argTypes))
                .append(" on ");
        if (receiver instanceof RubyModule) {
            fullError.append(((RubyModule)receiver).getName());
        } else {
            fullError.append(receiver.getMetaClass().getRealClass().getName());
        }
        
        if (methods.length > 1) {
            fullError.append("\n  available overloads:");
            for (ParameterTypes method : methods) {
                fullError.append("\n    " + CodegenUtils.prettyParams(method.getParameterTypes()));
            }
        }
        
        return context.runtime.newNameError(fullError.toString(), null);
    }
New to GrepCode? Check out our FAQ X