| 1651 | } |
| 1652 | |
| 1653 | static class FISupport { |
| 1654 | private static final IPersistentSet AFN_FIS = RT.set(Callable.class, Runnable.class, Comparator.class); |
| 1655 | private static final IPersistentSet OBJECT_METHODS = RT.set("equals", "toString", "hashCode"); |
| 1656 | |
| 1657 | // Return FI method if: |
| 1658 | // 1) Target is a functional interface and not already implemented by AFn |
| 1659 | // 2) Target method matches one of our fn invoker methods (0 <= arity <= 10) |
| 1660 | static java.lang.reflect.Method maybeFIMethod(Class target) { |
| 1661 | if (target != null && target.isAnnotationPresent(FunctionalInterface.class) |
| 1662 | && !AFN_FIS.contains(target)) { |
| 1663 | |
| 1664 | java.lang.reflect.Method[] methods = target.getMethods(); |
| 1665 | for (java.lang.reflect.Method method : methods) { |
| 1666 | if (method.getParameterCount() >= 0 && method.getParameterCount() <= 10 |
| 1667 | && Modifier.isAbstract(method.getModifiers()) |
| 1668 | && !OBJECT_METHODS.contains(method.getName())) |
| 1669 | return method; |
| 1670 | } |
| 1671 | } |
| 1672 | return null; |
| 1673 | } |
| 1674 | |
| 1675 | // Invokers support only long, double, Object params; widen numerics |
| 1676 | private static Class toInvokerParamType(Class c) { |
| 1677 | if (c.equals(Byte.TYPE) || c.equals(Short.TYPE) || c.equals(Integer.TYPE) || c.equals(Long.TYPE)) { |
| 1678 | return Long.TYPE; |
| 1679 | } else if (c.equals(Float.TYPE) || c.equals(Double.TYPE)) { |
| 1680 | return Double.TYPE; |
| 1681 | } |
| 1682 | return Object.class; |
| 1683 | } |
| 1684 | |
| 1685 | /** |
| 1686 | * If targetClass is FI and has an adaptable functional method |
| 1687 | * Find fn invoker method matching adaptable method of FI |
| 1688 | * Emit bytecode for (expr is emitted): |
| 1689 | * if(expr instanceof IFn && !(expr instanceof FI)) |
| 1690 | * invokeDynamic(targetMethod, fnInvokerImplMethod) |
| 1691 | * Else emit nothing |
| 1692 | */ |
| 1693 | static boolean maybeEmitFIAdapter(ObjExpr objx, GeneratorAdapter gen, Expr expr, Class targetClass) { |
| 1694 | // Optimization: |
| 1695 | // if(expr instanceof QualifiedMethodExpr) |
| 1696 | // emitInvokeDynamic(targetMethod, QME method) // DON'T emit expr |
| 1697 | |
| 1698 | java.lang.reflect.Method targetMethod = maybeFIMethod(targetClass); |
| 1699 | if (targetMethod == null) |
| 1700 | return false; |
| 1701 | |
| 1702 | // compute fn invoker method |
| 1703 | int paramCount = targetMethod.getParameterCount(); |
| 1704 | Class[] invokerParams = new Class[paramCount + 1]; |
| 1705 | invokerParams[0] = IFn.class; // close over Ifn as first arg |
| 1706 | StringBuilder invokeMethodBuilder = new StringBuilder("invoke"); |
| 1707 | for (int i = 0; i < paramCount; i++) { |
| 1708 | // FnInvokers only has prims for first 2 args |
| 1709 | invokerParams[i + 1] = paramCount <= 2 ? toInvokerParamType(targetMethod.getParameterTypes()[i]) : Object.class; |
| 1710 | invokeMethodBuilder.append(FnInvokers.encodeInvokerType(invokerParams[i + 1])); |