/** * Return a method handle that takes the indicated number of typed arguments and returns an array * of them. The type argument is the array type. */ public static MethodHandle varargsArray(Class<?> arrayType, int nargs) { Class<?> elemType = arrayType.getComponentType(); if (elemType == null) throw new IllegalArgumentException("not an array: " + arrayType); // FIXME: Need more special casing and caching here. if (nargs >= MAX_JVM_ARITY / 2 - 1) { int slots = nargs; final int MAX_ARRAY_SLOTS = MAX_JVM_ARITY - 1; // 1 for receiver MH if (arrayType == double[].class || arrayType == long[].class) slots *= 2; if (slots > MAX_ARRAY_SLOTS) throw new IllegalArgumentException( "too many arguments: " + arrayType.getSimpleName() + ", length " + nargs); } if (elemType == Object.class) return varargsArray(nargs); // other cases: primitive arrays, subtypes of Object[] MethodHandle cache[] = TYPED_COLLECTORS.get(elemType); MethodHandle mh = nargs < cache.length ? cache[nargs] : null; if (mh != null) return mh; if (elemType.isPrimitive()) { MethodHandle builder = FILL_NEW_ARRAY; MethodHandle producer = buildArrayProducer(arrayType); mh = buildVarargsArray(builder, producer, nargs); } else { @SuppressWarnings("unchecked") Class<? extends Object[]> objArrayType = (Class<? extends Object[]>) arrayType; Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType); MethodHandle builder = FILL_NEW_TYPED_ARRAY.bindTo(example); MethodHandle producer = ARRAY_IDENTITY; mh = buildVarargsArray(builder, producer, nargs); } mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType))); assert (assertCorrectArity(mh, nargs)); if (nargs < cache.length) cache[nargs] = mh; return mh; }
private static MethodHandle buildFiller(int nargs) { if (nargs <= LEFT_ARGS) return ARRAY_IDENTITY; // no args to fill; return the array unchanged // we need room for both mh and a in mh.invoke(a, arg*[nargs]) final int CHUNK = LEFT_ARGS; int rightLen = nargs % CHUNK; int midLen = nargs - rightLen; if (rightLen == 0) { midLen = nargs - (rightLen = CHUNK); if (FILL_ARRAY_TO_RIGHT[midLen] == null) { // build some precursors from left to right for (int j = LEFT_ARGS % CHUNK; j < midLen; j += CHUNK) if (j > LEFT_ARGS) fillToRight(j); } } if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS); assert (rightLen > 0); MethodHandle midFill = fillToRight(midLen); // recursive fill MethodHandle rightFill = FILL_ARRAYS[rightLen].bindTo(midLen); // [midLen..nargs-1] assert (midFill.type().parameterCount() == 1 + midLen - LEFT_ARGS); assert (rightFill.type().parameterCount() == 1 + rightLen); // Combine the two fills: // right(mid(a, x10..x19), x20..x23) // The final product will look like this: // right(mid(newArrayLeft(24, x0..x9), x10..x19), x20..x23) if (midLen == LEFT_ARGS) return rightFill; else return MethodHandles.collectArguments(rightFill, 0, midFill); }
public static MethodHandle convertPrimitive(Wrapper wsrc, Wrapper wdst) { EnumMap<Wrapper, MethodHandle> cache = CONVERT_PRIMITIVE_FUNCTIONS[wsrc.ordinal()]; MethodHandle mh = cache.get(wdst); if (mh != null) { return mh; } // slow path Class<?> src = wsrc.primitiveType(); Class<?> dst = wdst.primitiveType(); MethodType type = src == void.class ? MethodType.methodType(dst) : MethodType.methodType(dst, src); if (wsrc == wdst) { mh = identity(src); } else if (wsrc == Wrapper.VOID) { mh = zeroConstantFunction(wdst); } else if (wdst == Wrapper.VOID) { mh = MethodHandles.dropArguments(EMPTY, 0, src); // Defer back to MethodHandles. } else if (wsrc == Wrapper.OBJECT) { mh = unboxCast(dst); } else if (wdst == Wrapper.OBJECT) { mh = box(src); } else { assert (src.isPrimitive() && dst.isPrimitive()); try { mh = IMPL_LOOKUP.findStatic( THIS_CLASS, src.getSimpleName() + "To" + capitalize(dst.getSimpleName()), type); } catch (ReflectiveOperationException ex) { mh = null; } } if (mh != null) { assert (mh.type() == type) : mh; cache.put(wdst, mh); return mh; } throw new IllegalArgumentException( "cannot find primitive conversion function for " + src.getSimpleName() + " -> " + dst.getSimpleName()); }
private static MethodHandle buildVarargsArray( MethodHandle newArray, MethodHandle finisher, int nargs) { // Build up the result mh as a sequence of fills like this: // finisher(fill(fill(newArrayWA(23,x1..x10),10,x11..x20),20,x21..x23)) // The various fill(_,10*I,___*[J]) are reusable. int leftLen = Math.min(nargs, LEFT_ARGS); // absorb some arguments immediately int rightLen = nargs - leftLen; MethodHandle leftCollector = newArray.bindTo(nargs); leftCollector = leftCollector.asCollector(Object[].class, leftLen); MethodHandle mh = finisher; if (rightLen > 0) { MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen); if (mh == ARRAY_IDENTITY) mh = rightFiller; else mh = MethodHandles.collectArguments(mh, 0, rightFiller); } if (mh == ARRAY_IDENTITY) mh = leftCollector; else mh = MethodHandles.collectArguments(mh, 0, leftCollector); return mh; }
private static boolean assertCorrectArity(MethodHandle mh, int arity) { assert (mh.type().parameterCount() == arity) : "arity != " + arity + ": " + mh; return true; }