# HG changeset patch # User jrose # Date 1297416384 28800 # Node ID 9e2483e6cfabbdd0fd0797d293fd9bf985d5ced8 # Parent c5df55701e9163ecf9a2b2335dc616368d271863 7013417: JSR 292 needs to support variadic method handle calls Summary: Implement MH.asVarargsCollector, etc., and remove withTypeHandler. Reviewed-by: twisti diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/java/dyn/MethodHandle.java --- a/jdk/src/share/classes/java/dyn/MethodHandle.java Thu Feb 10 16:24:40 2011 -0800 +++ b/jdk/src/share/classes/java/dyn/MethodHandle.java Fri Feb 11 01:26:24 2011 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,6 +134,11 @@ * constant pool entry. * For more details, see the package summary.) *

+ * Method handles produced by lookups or constant loads from methods or + * constructors with the variable arity modifier bit ({@code 0x0080}) + * have a corresponding variable arity, as if they were defined with + * the help of {@link #asVarargsCollector asVarargsCollector}. + *

* Java code can also use a reflective API called * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup} * for creating and calling method handles. @@ -175,6 +180,9 @@ // mt is {Object[] => List} mt = MethodType.methodType(java.util.List.class, Object[].class); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); +assert(mh.isVarargsCollector()); +x = mh.invokeGeneric("one", "two"); +assert(x.equals(java.util.Arrays.asList("one","two"))); // mt is {(Object,Object,Object) => Object} mt = MethodType.genericMethodType(3); mh = MethodHandles.collectArguments(mh, mt); @@ -307,26 +315,6 @@ } /** - * Returns a string representation of the method handle, - * starting with the string {@code "MethodHandle"} and - * ending with the string representation of the method handle's type. - * In other words, this method returns a string equal to the value of: - *

-     * "MethodHandle" + type().toString()
-     * 
- *

- * Note: Future releases of this API may add further information - * to the string representation. - * Therefore, the present syntax should not be parsed by applications. - * - * @return a string representation of the method handle - */ - @Override - public String toString() { - return MethodHandleImpl.getNameString(IMPL_TOKEN, this); - } - - /** * Invoke the method handle, allowing any caller signature, but requiring an exact signature match. * The signature at the call site of {@code invokeExact} must * exactly match this method handle's {@link #type type}. @@ -338,7 +326,7 @@ /** * Invoke the method handle, allowing any caller signature, - * and optionally performing conversions for arguments and return types. + * and optionally performing conversions on arguments and return values. *

* If the call site signature exactly matches this method handle's {@link #type type}, * the call proceeds as if by {@link #invokeExact invokeExact}. @@ -353,14 +341,13 @@ * adaptations directly on the caller's arguments, * and call the target method handle according to its own exact type. *

- * If the method handle is equipped with a - * {@linkplain #withTypeHandler type handler}, the handler must produce - * an entry point of the call site's exact type. - * Otherwise, the signature at the call site of {@code invokeGeneric} must - * be a valid argument to the standard {@code asType} method. + * The signature at the call site of {@code invokeGeneric} must + * be a valid argument to the receivers {@code asType} method. * In particular, the caller must specify the same argument arity - * as the callee's type. + * as the callee's type, + * if the callee is not a {@linkplain #asVarargsCollector variable arity collector}. * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's type signature + * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call */ public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable; @@ -400,14 +387,14 @@ *

* This call is equivalent to the following code: *

-     * MethodHandle invoker = MethodHandles.varargsInvoker(this.type(), 0);
+     * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
      * Object result = invoker.invokeExact(this, arguments);
      * 
* @param arguments the arguments to pass to the target * @return the result returned by the target * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments * @throws Throwable anything thrown by the target method invocation - * @see MethodHandles#varargsInvoker + * @see MethodHandles#spreadInvoker */ public final Object invokeWithArguments(Object... arguments) throws Throwable { int argc = arguments == null ? 0 : arguments.length; @@ -456,7 +443,7 @@ } // more than ten arguments get boxed in a varargs list: - MethodHandle invoker = MethodHandles.invokers(type).varargsInvoker(0); + MethodHandle invoker = invokers(type).spreadInvoker(0); return invoker.invokeExact(this, arguments); } /** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */ @@ -488,13 +475,7 @@ * to match up the caller's and callee's types. *

* This method is equivalent to {@link MethodHandles#convertArguments convertArguments}, - * except for method handles produced by {@link #withTypeHandler withTypeHandler}, - * in which case the specified type handler is used for calls to {@code asType}. - *

- * Note that the default behavior of {@code asType} only performs - * pairwise argument conversion and return value conversion. - * Because of this, unless the method handle has a type handler, - * the original type and new type must have the same number of arguments. + * except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}. * * @param newType the expected type of the new method handle * @return a method handle which delegates to {@code this} after performing @@ -508,14 +489,16 @@ } /** - * Produce a method handle which adapts, as its target, + * Make an adapter which accepts a trailing array argument + * and spreads its elements as positional arguments. + * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be * the same as the type of the target, except that the final * {@code arrayLength} parameters of the target's type are replaced * by a single array parameter of type {@code arrayType}. *

* If the array element type differs from any of the corresponding - * argument types on original target, + * argument types on the original target, * the original target is adapted to take the array elements directly, * as if by a call to {@link #asType asType}. *

@@ -539,6 +522,7 @@ * @throws IllegalArgumentException if target does not have at least * {@code arrayLength} parameter types * @throws WrongMethodTypeException if the implied {@code asType} call fails + * @see #asCollector */ public final MethodHandle asSpreader(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); @@ -553,13 +537,15 @@ } /** - * Produce a method handle which adapts, as its target, + * Make an adapter which accepts a given number of trailing + * positional arguments and collects them into an array argument. + * The new method handle adapts, as its target, * the current method handle. The type of the adapter will be * the same as the type of the target, except that a single trailing * parameter (usually of type {@code arrayType}) is replaced by * {@code arrayLength} parameters whose type is element type of {@code arrayType}. *

- * If the array type differs from the final argument type on original target, + * If the array type differs from the final argument type on the original target, * the original target is adapted to take the array type directly, * as if by a call to {@link #asType asType}. *

@@ -570,21 +556,31 @@ * What the target eventually returns is returned unchanged by the adapter. *

* (The array may also be a shared constant when {@code arrayLength} is zero.) - * @param arrayType usually {@code Object[]}, the type of the array argument which will collect the arguments + *

+ * (Note: The {@code arrayType} is often identical to the last + * parameter type of the original target. + * It is an explicit argument for symmetry with {@code asSpreader}, and also + * to allow the target to use a simple {@code Object} as its last parameter type.) + *

+ * In order to create a collecting adapter which is not restricted to a particular + * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead. + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments * @param arrayLength the number of arguments to collect into a new array argument * @return a new method handle which collects some trailing argument * into an array, before calling the original method handle * @throws IllegalArgumentException if {@code arrayType} is not an array type - or {@code arrayType} is not assignable to this method handle's trailing parameter type - * @throws IllegalArgumentException if {@code arrayLength} is not - * a legal array size + * or {@code arrayType} is not assignable to this method handle's trailing parameter type, + * or {@code arrayLength} is not a legal array size * @throws WrongMethodTypeException if the implied {@code asType} call fails + * @see #asSpreader + * @see #asVarargsCollector */ public final MethodHandle asCollector(Class arrayType, int arrayLength) { Class arrayElement = arrayType.getComponentType(); if (arrayElement == null) throw newIllegalArgumentException("not an array type"); MethodType oldType = type(); int nargs = oldType.parameterCount(); + if (nargs == 0) throw newIllegalArgumentException("no trailing argument"); MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); newType = newType.insertParameterTypes(nargs-1, java.util.Collections.>nCopies(arrayLength, arrayElement)); @@ -592,8 +588,183 @@ } /** - * Produce a method handle which binds the given argument - * to the current method handle as target. + * Make a variable arity adapter which is able to accept + * any number of trailing positional arguments and collect them + * into an array argument. + *

+ * The type and behavior of the adapter will be the same as + * the type and behavior of the target, except that certain + * {@code invokeGeneric} and {@code asType} requests can lead to + * trailing positional arguments being collected into target's + * trailing parameter. + * Also, the last parameter type of the adapter will be + * {@code arrayType}, even if the target has a different + * last parameter type. + *

+ * When called with {@link #invokeExact invokeExact}, the adapter invokes + * the target with no argument changes. + * (Note: This behavior is different from a + * {@linkplain #asCollector fixed arity collector}, + * since it accepts a whole array of indeterminate length, + * rather than a fixed number of arguments.) + *

+ * When called with {@link #invokeGeneric invokeGeneric}, if the caller + * type is the same as the adapter, the adapter invokes the target as with + * {@code invokeExact}. + * (This is the normal behavior for {@code invokeGeneric} when types match.) + *

+ * Otherwise, if the caller and adapter arity are the same, and the + * trailing parameter type of the caller is a reference type identical to + * or assignable to the trailing parameter type of the adapter, + * the arguments and return values are converted pairwise, + * as if by {@link MethodHandles#convertArguments convertArguments}. + * (This is also normal behavior for {@code invokeGeneric} in such a case.) + *

+ * Otherwise, the arities differ, or the adapter's trailing parameter + * type is not assignable from the corresponding caller type. + * In this case, the adapter replaces all trailing arguments from + * the original trailing argument position onward, by + * a new array of type {@code arrayType}, whose elements + * comprise (in order) the replaced arguments. + *

+ * The caller type must provides as least enough arguments, + * and of the correct type, to satisfy the target's requirement for + * positional arguments before the trailing array argument. + * Thus, the caller must supply, at a minimum, {@code N-1} arguments, + * where {@code N} is the arity of the target. + * Also, there must exist conversions from the incoming arguments + * to the target's arguments. + * As with other uses of {@code invokeGeneric}, if these basic + * requirements are not fulfilled, a {@code WrongMethodTypeException} + * may be thrown. + *

+ * In all cases, what the target eventually returns is returned unchanged by the adapter. + *

+ * In the final case, it is exactly as if the target method handle were + * temporarily adapted with a {@linkplain #asCollector fixed arity collector} + * to the arity required by the caller type. + * (As with {@code asCollector}, if the array length is zero, + * a shared constant may be used instead of a new array. + * If the implied call to {@code asCollector} would throw + * an {@code IllegalArgumentException} or {@code WrongMethodTypeException}, + * the call to the variable arity adapter must throw + * {@code WrongMethodTypeException}.) + *

+ * The behavior of {@link #asType asType} is also specialized for + * variable arity adapters, to maintain the invariant that + * {@code invokeGeneric} is always equivalent to an {@code asType} + * call to adjust the target type, followed by {@code invokeExact}. + * Therefore, a variable arity adapter responds + * to an {@code asType} request by building a fixed arity collector, + * if and only if the adapter and requested type differ either + * in arity or trailing argument type. + * The resulting fixed arity collector has its type further adjusted + * (if necessary) to the requested type by pairwise conversion, + * as if by another application of {@code asType}. + *

+ * When a method handle is obtained by executing an {@code ldc} instruction + * of a {@code CONSTANT_MethodHandle} constant, and the target method is marked + * as a variable arity method (with the modifier bit {@code 0x0080}), + * the method handle will accept multiple arities, as if the method handle + * constant were created by means of a call to {@code asVarargsCollector}. + *

+ * In order to create a collecting adapter which collects a predetermined + * number of arguments, and whose type reflects this predetermined number, + * use {@link #asCollector asCollector} instead. + *

+ * No method handle transformations produce new method handles with + * variable arity, unless they are documented as doing so. + * Therefore, besides {@code asVarargsCollector}, + * all methods in {@code MethodHandle} and {@code MethodHandles} + * will return a method handle with fixed arity, + * except in the cases where they are specified to return their original + * operand (e.g., {@code asType} of the method handle's own type). + *

+ * Calling {@code asVarargsCollector} on a method handle which is already + * of variable arity will produce a method handle with the same type and behavior. + * It may (or may not) return the original variable arity method handle. + *

+ * Here is an example, of a list-making variable arity method handle: + *

+MethodHandle asList = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+assertEquals("[]", asList.invokeGeneric().toString());
+assertEquals("[1]", asList.invokeGeneric(1).toString());
+assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
+List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+     * 
+ *

+ * These rules are designed as a dynamically-typed variation + * of the Java rules for variable arity methods. + * In both cases, callers to a variable arity method or method handle + * can either pass zero or more positional arguments, or else pass + * pre-collected arrays of any length. Users should be aware of the + * special role of the final argument, and of the effect of a + * type match on that final argument, which determines whether + * or not a single trailing argument is interpreted as a whole + * array or a single element of an array to be collected. + * Note that the dynamic type of the trailing argument has no + * effect on this decision, only a comparison between the static + * type signature of the call site and the type of the method handle.) + *

+ * As a result of the previously stated rules, the variable arity behavior + * of a method handle may be suppressed, by binding it to the exact invoker + * of its own type, as follows: + *

+MethodHandle vamh = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+MethodHandle invokeExact = publicLookup()
+  .findVirtual(MethodHandle.class, "invokeExact", vamh.type());
+MethodHandle mh = invokeExact.bindTo(vamh);
+assert(vamh.type().equals(mh.type()));
+assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+boolean failed = false;
+try { mh.invokeGeneric(1,2,3); }
+catch (WrongMethodTypeException ex) { failed = true; }
+assert(failed);
+     * 
+ * This transformation has no behavioral effect if the method handle is + * not of variable arity. + * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments + * @return a new method handle which can collect any number of trailing arguments + * into an array, before calling the original method handle + * @throws IllegalArgumentException if {@code arrayType} is not an array type + * or {@code arrayType} is not assignable to this method handle's trailing parameter type + * @see #asCollector + */ + public MethodHandle asVarargsCollector(Class arrayType) { + Class arrayElement = arrayType.getComponentType(); + if (arrayElement == null) throw newIllegalArgumentException("not an array type"); + return MethodHandles.asVarargsCollector(this, arrayType); + } + + /** + * Determine if this method handle + * supports {@linkplain #asVarargsCollector variable arity} calls. + * Such method handles arise from the following sources: + * + * @return true if this method handle accepts more than one arity of {@code invokeGeneric} calls + */ + public boolean isVarargsCollector() { + return false; + } + + /** + * Bind a value {@code x} to the first argument of a method handle, without invoking it. + * The new method handle adapts, as its target, + * to the current method handle. * The type of the bound handle will be * the same as the type of the target, except that a single leading * reference parameter will be omitted. @@ -619,74 +790,22 @@ } /** - * PROVISIONAL API, WORK IN PROGRESS: - * Create a new method handle with the same type as this one, - * but whose {@code asType} method invokes the given - * {@code typeHandler} on this method handle, - * instead of the standard {@code MethodHandles.convertArguments}. - *

- * The new method handle will have the same behavior as the - * old one when invoked by {@code invokeExact}. - * For {@code invokeGeneric} calls which exactly match - * the method type, the two method handles will also - * have the same behavior. - * For other {@code invokeGeneric} calls, the {@code typeHandler} - * will control the behavior of the new method handle. - *

- * Thus, a method handle with an {@code asType} handler can - * be configured to accept more than one arity of {@code invokeGeneric} - * call, and potentially every possible arity. - * It can also be configured to supply default values for - * optional arguments, when the caller does not specify them. - *

- * The given method handle must take two arguments and return - * one result. The result it returns must be a method handle - * of exactly the requested type. If the result returned by - * the target is null, a {@link NullPointerException} is thrown, - * else if the type of the target does not exactly match - * the requested type, a {@link WrongMethodTypeException} is thrown. - *

- * A method handle's type handler is not guaranteed to be called every - * time its {@code asType} or {@code invokeGeneric} method is called. - * If the implementation is faced is able to prove that an equivalent - * type handler call has already occurred (on the same two arguments), - * it may substitute the result of that previous invocation, without - * making a new invocation. Thus, type handlers should not (in general) - * perform significant side effects. - *

- * Therefore, the type handler is invoked as if by this code: + * Returns a string representation of the method handle, + * starting with the string {@code "MethodHandle"} and + * ending with the string representation of the method handle's type. + * In other words, this method returns a string equal to the value of: *

-     * MethodHandle target = this;      // original method handle
-     * MethodHandle adapter = ...;      // adapted method handle
-     * MethodType requestedType = ...;  // argument to asType()
-     * if (type().equals(requestedType))
-     *    return adapter;
-     * MethodHandle result = (MethodHandle)
-     *    typeHandler.invokeGeneric(target, requestedType);
-     * if (!result.type().equals(requestedType))
-     *    throw new WrongMethodTypeException();
-     * return result;
+     * "MethodHandle" + type().toString()
      * 
*

- * For example, here is a list-making variable-arity method handle: - *

-MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
-MethodHandle asList = lookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
-  return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-}
-MethodHandle collectingTypeHandler = lookup()
-  .findStatic(lookup().lookupClass(), "collectingTypeHandler",
-     methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
-
-assertEquals("[]", makeAnyList.invokeGeneric().toString());
-assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
-assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
-     * 
+ * Note: Future releases of this API may add further information + * to the string representation. + * Therefore, the present syntax should not be parsed by applications. + * + * @return a string representation of the method handle */ - public MethodHandle withTypeHandler(MethodHandle typeHandler) { - return MethodHandles.withTypeHandler(this, typeHandler); + @Override + public String toString() { + return MethodHandleImpl.getNameString(IMPL_TOKEN, this); } } diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/java/dyn/MethodHandles.java --- a/jdk/src/share/classes/java/dyn/MethodHandles.java Thu Feb 10 16:24:40 2011 -0800 +++ b/jdk/src/share/classes/java/dyn/MethodHandles.java Fri Feb 11 01:26:24 2011 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -135,7 +135,10 @@ *

* In general, the conditions under which a method handle may be * created for a method {@code M} are exactly as restrictive as the conditions - * under which the lookup class could have compiled a call to {@code M}. + * under which the lookup class could have compiled a call to {@code M}, + * or could have compiled an {@code ldc} instruction loading a + * {@code CONSTANT_MethodHandle} of M. + * The same point is true of fields and constructors. *

* In some cases, this access is obtained by the Java compiler by creating * an wrapper method to access a private method of another class @@ -379,6 +382,10 @@ * The method and all its argument types must be accessible to the lookup class. * If the method's class has not yet been initialized, that is done * immediately, before the method handle is returned. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class from which the method is accessed * @param name the name of the method * @param type the type of the method @@ -403,6 +410,10 @@ * implementation to enter. * (The dispatching action is identical with that performed by an * {@code invokevirtual} or {@code invokeinterface} instruction.) + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class or interface from which the method is accessed * @param name the name of the method * @param type the type of the method, with the receiver argument omitted @@ -427,6 +438,10 @@ *

* Note: The requested type must have a return type of {@code void}. * This is consistent with the JVM's treatment of constructor signatures. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the constructor's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class or interface from which the method is accessed * @param type the type of the method, with the receiver argument omitted, and a void return type * @return the desired method handle @@ -438,7 +453,23 @@ assert(ctor.isConstructor()); checkAccess(refc, ctor); MethodHandle rawMH = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); - return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH); + MethodHandle allocMH = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawMH); + return fixVarargs(allocMH, rawMH); + } + + /** Return a version of MH which matches matchMH w.r.t. isVarargsCollector. */ + private static MethodHandle fixVarargs(MethodHandle mh, MethodHandle matchMH) { + boolean va1 = mh.isVarargsCollector(); + boolean va2 = matchMH.isVarargsCollector(); + if (va1 == va2) { + return mh; + } else if (va2) { + MethodType type = mh.type(); + int arity = type.parameterCount(); + return mh.asVarargsCollector(type.parameterType(arity-1)); + } else { + throw new InternalError("already varargs, but template is not: "+mh); + } } /** @@ -458,6 +489,10 @@ * If the explicitly specified caller class is not identical with the * lookup class, or if this lookup object does not have private access * privileges, the access fails. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param refc the class or interface from which the method is accessed * @param name the name of the method (which must not be "<init>") * @param type the type of the method, with the receiver argument omitted @@ -547,13 +582,26 @@ * so that every call to the method handle will invoke the * requested method on the given receiver. *

- * This is equivalent to the following expression: - * - * {@link #insertArguments insertArguments}({@link #findVirtual findVirtual}(defc, name, type), receiver) - * + * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set + * and the trailing array argument is not the only argument. + * (If the trailing array argument is the only argument, + * the given receiver value will be bound to it.) + *

+ * This is equivalent to the following code: + *

+MethodHandle mh0 = {@link #findVirtual findVirtual}(defc, name, type);
+MethodHandle mh1 = mh0.{@link MethodHandle#bindTo bindTo}(receiver);
+MethodType mt1 = mh1.type();
+if (mh0.isVarargsCollector() && mt1.parameterCount() > 0) {
+  mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
+return mh1;
+         * 
* where {@code defc} is either {@code receiver.getClass()} or a super * type of that class, in which the requested method is accessible * to the lookup class. + * (Note that {@code bindTo} does not preserve variable arity.) * @param receiver the object from which the method is accessed * @param name the name of the method * @param type the type of the method, with the receiver argument omitted @@ -568,7 +616,9 @@ MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver); if (bmh == null) throw newNoAccessException(method, lookupClass()); - return bmh; + if (dmh.type().parameterCount() == 0) + return dmh; // bound the trailing parameter; no varargs possible + return fixVarargs(bmh, dmh); } /** @@ -581,6 +631,10 @@ * If the method's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. * If m is not public, do not share the resulting handle with untrusted parties. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param m the reflected method * @return a method handle which can invoke the reflected method * @exception NoAccessException if access checking fails @@ -603,6 +657,10 @@ * If the method's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class, * as if {@code invokespecial} instruction were being linked. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the method's variable arity modifier bit ({@code 0x0080}) is set. * @param m the reflected method * @param specialCaller the class nominally calling the method * @return a method handle which can invoke the reflected method @@ -628,6 +686,10 @@ *

* If the constructor's {@code accessible} flag is not set, * access checking is performed immediately on behalf of the lookup class. + *

+ * The returned method handle will have + * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if + * the constructor's variable arity modifier bit ({@code 0x0080}) is set. * @param c the reflected constructor * @return a method handle which can invoke the reflected constructor * @exception NoAccessException if access checking fails @@ -637,7 +699,8 @@ assert(ctor.isConstructor()); if (!c.isAccessible()) checkAccess(c.getDeclaringClass(), ctor); MethodHandle rawCtor = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); - return MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor); + MethodHandle allocator = MethodHandleImpl.makeAllocator(IMPL_TOKEN, rawCtor); + return fixVarargs(allocator, rawCtor); } /** @@ -785,7 +848,8 @@ MethodType rawType = mh.type(); if (rawType.parameterType(0) == caller) return mh; MethodType narrowType = rawType.changeParameterType(0, caller); - return MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null); + MethodHandle narrowMH = MethodHandleImpl.convertArguments(IMPL_TOKEN, mh, narrowType, rawType, null); + return fixVarargs(narrowMH, mh); } MethodHandle makeAccessor(Class refc, String name, Class type, @@ -909,22 +973,21 @@ *

* This method is equivalent to the following code (though it may be more efficient): *

-     * MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type);
-     * MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
-     * vaType = vaType.insertParameterType(0, MethodHandle.class);
-     * int spreadArgCount = type.parameterCount - objectArgCount;
-     * invoker = invoker.asSpreader(Object.class, spreadArgCount);
-     * return invoker.asType(vaType);
+MethodHandle invoker = publicLookup()
+  .findVirtual(MethodHandle.class, "invokeGeneric", type)
+int spreadArgCount = type.parameterCount - objectArgCount;
+invoker = invoker.asSpreader(Object[].class, spreadArgCount);
+return invoker;
      * 
* @param type the desired target type * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments * @return a method handle suitable for invoking any method handle of the given type */ static public - MethodHandle varargsInvoker(MethodType type, int objectArgCount) { + MethodHandle spreadInvoker(MethodType type, int objectArgCount) { if (objectArgCount < 0 || objectArgCount > type.parameterCount()) throw new IllegalArgumentException("bad argument count "+objectArgCount); - return invokers(type).varargsInvoker(objectArgCount); + return invokers(type).spreadInvoker(objectArgCount); } /** @@ -1826,7 +1889,10 @@ * the given {@code target} on the incoming arguments, * and returning or throwing whatever the {@code target} * returns or throws. The invocation will be as if by - * {@code target.invokeExact}. + * {@code target.invokeGeneric}. + * The target's type will be checked before the SAM + * instance is created, as if by a call to {@code asType}, + * which may result in a {@code WrongMethodTypeException}. *

* The method handle may throw an undeclared exception, * which means any checked exception (or other checked throwable) @@ -1874,15 +1940,17 @@ * {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject} * in the adapters, so that generic code can extract the underlying * method handle without knowing where the SAM adapter came from. + *

+ * Future versions of this API may accept additional types, + * such as abstract classes with single abstract methods. * @param target the method handle to invoke from the wrapper * @param samType the desired type of the wrapper, a SAM type * @return a correctly-typed wrapper for the given {@code target} - * @throws IllegalArgumentException if the {@code target} throws - * an undeclared exception + * @throws IllegalArgumentException if the {@code samType} is not a + * valid argument to this method + * @throws WrongMethodTypeException if the {@code target} cannot + * be converted to the type required by the SAM type */ - // ISSUE: Should we delegate equals/hashCode to the targets? - // Not useful unless there is a stable equals/hashCode behavior - // for MethodHandle, but there isn't. public static T asInstance(final MethodHandle target, final Class samType) { // POC implementation only; violates the above contract several ways @@ -1890,8 +1958,9 @@ if (sam == null) throw new IllegalArgumentException("not a SAM type: "+samType.getName()); MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes()); - if (!samMT.equals(target.type())) - throw new IllegalArgumentException("wrong method type: "+target+" should match "+sam); + MethodHandle checkTarget = target.asType(samMT); // make throw WMT + checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class)); + final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, samMT.parameterCount()); return samType.cast(Proxy.newProxyInstance( samType.getClassLoader(), new Class[]{ samType, AsInstanceObject.class }, @@ -1905,7 +1974,7 @@ if (method.getDeclaringClass() == AsInstanceObject.class) return getArg(method.getName()); if (method.equals(sam)) - return target.invokeVarargs(args); + return vaTarget.invokeExact(args); if (isObjectMethod(method)) return callObjectMethod(this, method, args); throw new InternalError(); @@ -1991,7 +2060,7 @@ } /*non-public*/ - static MethodHandle withTypeHandler(MethodHandle target, MethodHandle typeHandler) { - return MethodHandleImpl.withTypeHandler(IMPL_TOKEN, target, typeHandler); + static MethodHandle asVarargsCollector(MethodHandle target, Class arrayType) { + return MethodHandleImpl.asVarargsCollector(IMPL_TOKEN, target, arrayType); } } diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/java/dyn/package-info.java --- a/jdk/src/share/classes/java/dyn/package-info.java Thu Feb 10 16:24:40 2011 -0800 +++ b/jdk/src/share/classes/java/dyn/package-info.java Fri Feb 11 01:26:24 2011 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -166,13 +166,13 @@ * normal accesses are legal. *

* A constant may refer to a method or constructor with the {@code varargs} - * bit (hexadecimal {@code 80}) set in its modifier bitmask. - * The method handle constant produced for such a method behaves the same + * bit (hexadecimal {@code 0x0080}) set in its modifier bitmask. + * The method handle constant produced for such a method behaves as if + * it were created by {@link java.dyn.MethodHandle#asVarargsCollector asVarargsCollector}. + * In other words, the constant method handle will exhibit variable arity, + * when invoked via {@code invokeGeneric}. + * On the other hand, its behavior with respect to {@code invokeExact} will be the same * as if the {@code varargs} bit were not set. - * The argument-collecting behavior of {@code varargs} can be emulated by - * adapting the method handle constant with - * {@link java.dyn.MethodHandle#asCollector asCollector}. - * There is no provision for doing this automatically. *

* Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types * resolve class names, they do not force class initialization. @@ -369,21 +369,40 @@ * the instruction's bootstrap method will be invoked on three arguments, * conveying the instruction's caller class, name, and method type. * If the {@code invokedynamic} instruction specifies one or more static arguments, - * a fourth argument will be passed to the bootstrap argument, - * either an {@code Object} reference to the sole extra argument (if there is one) - * or an {@code Object} array of references to all the arguments (if there are two or more), - * as if the bootstrap method is a variable-arity method. + * those values will be passed as additional arguments to the method handle. + * (Note that because there is a limit of 255 arguments to any method, + * at most 252 extra arguments can be supplied.) + * The bootstrap method will be invoked as if by either {@code invokeGeneric} + * or {@code invokeWithArguments}. (There is no way to tell the difference.) + * If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set), + * then some or all of the arguments specified here may be collected into a trailing array parameter. + * (This is not a special rule, but rather a useful consequence of the interaction + * between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods, + * and the {@code java.dyn.MethodHandle#asVarargsCollector asVarargsCollector} transformation.) + *

+ * Given these rules, here are examples of legal bootstrap method declarations, + * given various numbers {@code N} of extra arguments. + * The first rows (marked {@code *}) will work for any number of extra arguments. * * * + * + * + * * + * * * + * + * *
Nsample bootstrap method
*CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
*CallSite bootstrap(Object... args)
*CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)
0CallSite bootstrap(Lookup caller, String name, MethodType type)
0CallSite bootstrap(Lookup caller, Object... nameAndType)
1CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
2CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
2CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)
2CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
*
+ * The last example assumes that the extra arguments are of type + * {@code CONSTANT_String} and {@code CONSTANT_Integer}, respectively. + * The second-to-last example assumes that all extra arguments are of type + * {@code CONSTANT_String}. + * The other examples work with all types of extra arguments. *

- * The argument and return types listed here are used by the {@code invokeGeneric} - * call to the bootstrap method. * As noted above, the actual method type of the bootstrap method can vary. * For example, the fourth argument could be {@code MethodHandle}, * if that is the type of the corresponding constant in @@ -391,14 +410,8 @@ * In that case, the {@code invokeGeneric} call will pass the extra method handle * constant as an {@code Object}, but the type matching machinery of {@code invokeGeneric} * will cast the reference back to {@code MethodHandle} before invoking the bootstrap method. - * (If a string constant were passed instead, by badly generated code, that cast would then fail.) - *

- * If the fourth argument is an array, the array element type must be {@code Object}, - * since object arrays (as produced by the JVM at this point) cannot be converted - * to other array types. - *

- * If an array is provided, it will appear to be freshly allocated. - * That is, the same array will not appear to two bootstrap method calls. + * (If a string constant were passed instead, by badly generated code, that cast would then fail, + * resulting in an {@code InvokeDynamicBootstrapError}.) *

* Extra bootstrap method arguments are intended to allow language implementors * to safely and compactly encode metadata. @@ -406,24 +419,6 @@ * since each call site could be given its own unique bootstrap method. * Such a practice is likely to produce large class files and constant pools. * - *

- * PROVISIONAL API, WORK IN PROGRESS: - * (Usage Note: There is no mechanism for specifying five or more positional arguments to the bootstrap method. - * If there are two or more arguments, the Java code of the bootstrap method is required to extract them from - * a varargs-style object array. - * This design uses varargs because it anticipates some use cases where bootstrap arguments - * contribute components of variable-length structures, such as virtual function tables - * or interpreter token streams. - * Such parameters would be awkward or impossible to manage if represented - * as normal positional method arguments, - * since there would need to be one Java method per length. - * On balance, leaving out the varargs feature would cause more trouble to users than keeping it. - * Also, this design allows bootstrap methods to be called in a limited JVM stack depth. - * At both the user and JVM level, the difference between varargs and non-varargs - * calling sequences can easily be bridged via the - * {@link java.dyn.MethodHandle#asSpreader asSpreader} - * and {@link java.dyn.MethodHandle#asSpreader asCollector} methods.) - * *

Structure Summary

*
// summary of constant and attribute structures
 struct CONSTANT_MethodHandle_info {
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java
--- a/jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/AdapterMethodHandle.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -478,37 +478,60 @@
         return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
     }
 
-    static MethodHandle makeTypeHandler(Access token,
-                MethodHandle target, MethodHandle typeHandler) {
+    static MethodHandle makeVarargsCollector(Access token,
+                MethodHandle target, Class arrayType) {
         Access.check(token);
-        return new WithTypeHandler(target, typeHandler);
+        return new AsVarargsCollector(target, arrayType);
     }
 
-    static class WithTypeHandler extends AdapterMethodHandle {
-        final MethodHandle target, typeHandler;
-        WithTypeHandler(MethodHandle target, MethodHandle typeHandler) {
+    static class AsVarargsCollector extends AdapterMethodHandle {
+        final MethodHandle target;
+        final Class arrayType;
+        MethodHandle cache;
+
+        AsVarargsCollector(MethodHandle target, Class arrayType) {
             super(target, target.type(), makeConv(OP_RETYPE_ONLY));
             this.target = target;
-            this.typeHandler = typeHandler.asType(TYPE_HANDLER_TYPE);
+            this.arrayType = arrayType;
+            this.cache = target.asCollector(arrayType, 0);
+        }
+
+        @Override
+        public boolean isVarargsCollector() {
+            return true;
         }
 
+        @Override
         public MethodHandle asType(MethodType newType) {
-            if (this.type() == newType)
-                return this;
+            MethodType type = this.type();
+            int collectArg = type.parameterCount() - 1;
+            int newArity = newType.parameterCount();
+            if (newArity == collectArg+1 &&
+                type.parameterType(collectArg).isAssignableFrom(newType.parameterType(collectArg))) {
+                // if arity and trailing parameter are compatible, do normal thing
+                return super.asType(newType);
+            }
+            // check cache
+            if (cache.type().parameterCount() == newArity)
+                return cache.asType(newType);
+            // build and cache a collector
+            int arrayLength = newArity - collectArg;
+            MethodHandle collector;
             try {
-                MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType);
-                // Contract:  Must return the desired type, or throw WMT
-                if (retyped.type() != newType)
-                    throw new WrongMethodTypeException(retyped.toString());
-                return retyped;
-            } catch (Throwable ex) {
-                if (ex instanceof Error)  throw (Error)ex;
-                if (ex instanceof RuntimeException)  throw (RuntimeException)ex;
-                throw new RuntimeException(ex);
+                collector = target.asCollector(arrayType, arrayLength);
+            } catch (IllegalArgumentException ex) {
+                throw new WrongMethodTypeException("cannot build collector");
             }
+            cache = collector;
+            return collector.asType(newType);
         }
-        private static final MethodType TYPE_HANDLER_TYPE
-            = MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class);
+
+        public MethodHandle asVarargsCollector(Class arrayType) {
+            MethodType type = this.type();
+            if (type.parameterType(type.parameterCount()-1) == arrayType)
+                return this;
+            return super.asVarargsCollector(arrayType);
+        }
     }
 
     /** Can a checkcast adapter validly convert the target to newType?
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/CallSiteImpl.java
--- a/jdk/src/share/classes/sun/dyn/CallSiteImpl.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/CallSiteImpl.java	Fri Feb 11 01:26:24 2011 -0800
@@ -37,17 +37,17 @@
     static CallSite makeSite(MethodHandle bootstrapMethod,
                              // Callee information:
                              String name, MethodType type,
-                             // Call-site attributes, if any:
+                             // Extra arguments for BSM, if any:
                              Object info,
                              // Caller information:
                              MemberName callerMethod, int callerBCI) {
         Class callerClass = callerMethod.getDeclaringClass();
         Object caller;
-        if (bootstrapMethod.type().parameterType(0) == Class.class)
+        if (bootstrapMethod.type().parameterType(0) == Class.class && TRANSITIONAL_BEFORE_PFD)
             caller = callerClass;  // remove for PFD
         else
             caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass);
-        if (bootstrapMethod == null) {
+        if (bootstrapMethod == null && TRANSITIONAL_BEFORE_PFD) {
             // If there is no bootstrap method, throw IncompatibleClassChangeError.
             // This is a valid generic error type for resolution (JLS 12.3.3).
             throw new IncompatibleClassChangeError
@@ -56,30 +56,35 @@
         CallSite site;
         try {
             Object binding;
+            info = maybeReBox(info);
             if (info == null) {
-                if (false)  // switch when invokeGeneric works
-                    binding = bootstrapMethod.invokeGeneric(caller, name, type);
-                else
-                    binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
+                binding = bootstrapMethod.invokeGeneric(caller, name, type);
+            } else if (!info.getClass().isArray()) {
+                binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
             } else {
-                info = maybeReBox(info);
-                if (false)  // switch when invokeGeneric works
-                    binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
+                Object[] argv = (Object[]) info;
+                if (3 + argv.length > 255)
+                    new InvokeDynamicBootstrapError("too many bootstrap method arguments");
+                MethodType bsmType = bootstrapMethod.type();
+                if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
+                    binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
                 else
-                    binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type, info });
+                    binding = MethodHandles.spreadInvoker(bsmType, 3)
+                        .invokeGeneric(bootstrapMethod, caller, name, type, argv);
             }
             //System.out.println("BSM for "+name+type+" => "+binding);
             if (binding instanceof CallSite) {
                 site = (CallSite) binding;
-            } else if (binding instanceof MethodHandle) {
+            } else if (binding instanceof MethodHandle && TRANSITIONAL_BEFORE_PFD) {
                 // Transitional!
                 MethodHandle target = (MethodHandle) binding;
                 site = new ConstantCallSite(target);
             } else {
-                throw new ClassCastException("bootstrap method failed to produce a MethodHandle or CallSite");
+                throw new ClassCastException("bootstrap method failed to produce a CallSite");
             }
-            PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
-                                                     callerMethod, callerBCI);
+            if (TRANSITIONAL_BEFORE_PFD)
+                PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
+                                                         callerMethod, callerBCI);
             assert(site.getTarget() != null);
             assert(site.getTarget().type().equals(type));
         } catch (Throwable ex) {
@@ -93,6 +98,8 @@
         return site;
     }
 
+    private static boolean TRANSITIONAL_BEFORE_PFD = true;  // FIXME: remove for PFD
+
     private static Object maybeReBox(Object x) {
         if (x instanceof Integer) {
             int xi = (int) x;
@@ -117,6 +124,7 @@
     static {
         try {
             PRIVATE_INITIALIZE_CALL_SITE =
+            !TRANSITIONAL_BEFORE_PFD ? null :
             MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
                 MethodType.methodType(void.class,
                                       String.class, MethodType.class,
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/InvokeGeneric.java
--- a/jdk/src/share/classes/sun/dyn/InvokeGeneric.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/InvokeGeneric.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -108,7 +108,7 @@
      */
     private MethodHandle dispatch(MethodType callerType, MethodHandle target) {
         MethodType targetType = target.type();
-        if (USE_AS_TYPE_PATH || target instanceof AdapterMethodHandle.WithTypeHandler) {
+        if (USE_AS_TYPE_PATH || target.isVarargsCollector()) {
             MethodHandle newTarget = target.asType(callerType);
             targetType = callerType;
             Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType);
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/Invokers.java
--- a/jdk/src/share/classes/sun/dyn/Invokers.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/Invokers.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -47,7 +47,7 @@
     private /*lazy*/ MethodHandle genericInvoker;
 
     // generic (untyped) invoker for the outgoing call; accepts a single Object[]
-    private final /*lazy*/ MethodHandle[] varargsInvokers;
+    private final /*lazy*/ MethodHandle[] spreadInvokers;
 
     // invoker for an unbound callsite
     private /*lazy*/ MethodHandle uninitializedCallSite;
@@ -55,10 +55,9 @@
     /** Compute and cache information common to all collecting adapters
      *  that implement members of the erasure-family of the given erased type.
      */
-    public Invokers(Access token, MethodType targetType) {
-        Access.check(token);
+    /*non-public*/ Invokers(MethodType targetType) {
         this.targetType = targetType;
-        this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1];
+        this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
     }
 
     public static MethodType invokerType(MethodType targetType) {
@@ -69,7 +68,7 @@
         MethodHandle invoker = exactInvoker;
         if (invoker != null)  return invoker;
         try {
-            invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType);
+            invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
         } catch (NoAccessException ex) {
             throw new InternalError("JVM cannot find invoker for "+targetType);
         }
@@ -101,13 +100,13 @@
         return invoker;
     }
 
-    public MethodHandle varargsInvoker(int objectArgCount) {
-        MethodHandle vaInvoker = varargsInvokers[objectArgCount];
+    public MethodHandle spreadInvoker(int objectArgCount) {
+        MethodHandle vaInvoker = spreadInvokers[objectArgCount];
         if (vaInvoker != null)  return vaInvoker;
         MethodHandle gInvoker = genericInvoker();
         MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
         vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
-        varargsInvokers[objectArgCount] = vaInvoker;
+        spreadInvokers[objectArgCount] = vaInvoker;
         return vaInvoker;
     }
 
@@ -118,7 +117,7 @@
         if (invoker != null)  return invoker;
         if (targetType.parameterCount() > 0) {
             MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
-            Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0);
+            Invokers invokers0 = MethodTypeImpl.invokers(type0);
             invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
                                                   0, targetType.parameterList());
             assert(invoker.type().equals(targetType));
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/MethodHandleImpl.java
--- a/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodHandleImpl.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -184,7 +184,10 @@
         if (!mh.isValid())
             throw newNoAccessException(method, lookupClass);
         assert(mh.type() == mtype);
-        return mh;
+        if (!method.isVarargs())
+            return mh;
+        else
+            return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
     }
 
     public static
@@ -1263,8 +1266,8 @@
         return MethodHandleNatives.getBootstrap(callerClass);
     }
 
-    public static MethodHandle withTypeHandler(Access token, MethodHandle target, MethodHandle typeHandler) {
+    public static MethodHandle asVarargsCollector(Access token, MethodHandle target, Class arrayType) {
         Access.check(token);
-        return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler);
+        return AdapterMethodHandle.makeVarargsCollector(token, target, arrayType);
     }
 }
diff -r c5df55701e91 -r 9e2483e6cfab jdk/src/share/classes/sun/dyn/MethodTypeImpl.java
--- a/jdk/src/share/classes/sun/dyn/MethodTypeImpl.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/src/share/classes/sun/dyn/MethodTypeImpl.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -498,9 +498,12 @@
 
     public static Invokers invokers(Access token, MethodType type) {
         Access.check(token);
+        return invokers(type);
+    }
+    /*non-public*/ static Invokers invokers(MethodType type) {
         Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
         if (inv != null)  return inv;
-        inv = new Invokers(token, type);
+        inv = new Invokers(type);
         METHOD_TYPE_FRIEND.setInvokers(type, inv);
         return inv;
     }
diff -r c5df55701e91 -r 9e2483e6cfab jdk/test/java/dyn/InvokeDynamicPrintArgs.java
--- a/jdk/test/java/dyn/InvokeDynamicPrintArgs.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/test/java/dyn/InvokeDynamicPrintArgs.java	Fri Feb 11 01:26:24 2011 -0800
@@ -23,15 +23,19 @@
 
 /* @test
  * @summary smoke test for invokedynamic instructions
- * @library indify
+ * @build indify.Indify
  * @compile InvokeDynamicPrintArgs.java
  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
  *      indify.Indify
  *      --verify-specifier-count=3 --transitionalJSR292=false
  *      --expand-properties --classpath ${test.classes}
- *      --java InvokeDynamicPrintArgs --check-output
+ *      --java test.java.dyn.InvokeDynamicPrintArgs --check-output
  */
 
+package test.java.dyn;
+
+import org.junit.Test;
+
 import java.util.*;
 import java.io.*;
 
@@ -53,6 +57,20 @@
         closeBuf();
     }
 
+    @Test
+    public void testInvokeDynamicPrintArgs() throws IOException {
+        System.err.println(System.getProperties());
+        String testClassPath = System.getProperty("build.test.classes.dir");
+        if (testClassPath == null)  throw new RuntimeException();
+        String[] args = new String[]{
+            "--verify-specifier-count=3", "--transitionalJSR292=false",
+            "--expand-properties", "--classpath", testClassPath,
+            "--java", "test.java.dyn.InvokeDynamicPrintArgs", "--check-output"
+        };
+        System.err.println("Indify: "+Arrays.toString(args));
+        indify.Indify.main(args);
+    }
+
     private static PrintStream oldOut;
     private static ByteArrayOutputStream buf;
     private static void openBuf() {
@@ -79,11 +97,11 @@
     }
     private static final String[] EXPECT_OUTPUT = {
         "Printing some argument lists, starting with a empty one:",
-        "[InvokeDynamicPrintArgs, nothing, ()void][]",
-        "[InvokeDynamicPrintArgs, bar, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
-        "[InvokeDynamicPrintArgs, bar2, (java.lang.String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
-        "[InvokeDynamicPrintArgs, baz, (java.lang.String,int,double)void, 1234.5][baz arg, 2, 3.14]",
-        "[InvokeDynamicPrintArgs, foo, (java.lang.String)void][foo arg]",
+        "[test.java.dyn.InvokeDynamicPrintArgs, nothing, ()void][]",
+        "[test.java.dyn.InvokeDynamicPrintArgs, bar, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar arg, 1]",
+        "[test.java.dyn.InvokeDynamicPrintArgs, bar2, (String,int)void, class java.lang.Void, void type!, 1, 234.5, 67.5, 89][bar2 arg, 222]",
+        "[test.java.dyn.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
+        "[test.java.dyn.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
         "Done printing argument lists."
     };
 
@@ -110,18 +128,15 @@
         return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm());
     }
 
-    private static CallSite bsm2(Lookup caller, String name, MethodType type, Object arg) throws ReflectiveOperationException {
+    private static CallSite bsm2(Lookup caller, String name, MethodType type, Object... arg) throws ReflectiveOperationException {
         // ignore caller and name, but match the type:
         List bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
-        if (arg instanceof Object[])
-            bsmInfo.addAll(Arrays.asList((Object[])arg));
-        else
-            bsmInfo.add(arg);
+        bsmInfo.addAll(Arrays.asList((Object[])arg));
         return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
     }
     private static MethodType MT_bsm2() {
         shouldNotCallThis();
-        return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object.class);
+        return methodType(CallSite.class, Lookup.class, String.class, MethodType.class, Object[].class);
     }
     private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
         shouldNotCallThis();
diff -r c5df55701e91 -r 9e2483e6cfab jdk/test/java/dyn/JavaDocExamplesTest.java
--- a/jdk/test/java/dyn/JavaDocExamplesTest.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/test/java/dyn/JavaDocExamplesTest.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -142,37 +142,80 @@
         Assert.assertEquals(exp, act);
     }
 
-static MethodHandle asList;
-    @Test public void testWithTypeHandler() throws Throwable {
+    @Test public void testMethodHandlesSummary() throws Throwable {
         {{
 {} /// JAVADOC
-MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList());
-MethodHandle asList = lookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-
-JavaDocExamplesTest.asList = asList;
-/*
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
-  return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-}
-*/
-
-MethodHandle collectingTypeHandler = lookup()
-  .findStatic(lookup().lookupClass(), "collectingTypeHandler",
-     methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler);
-
-assertEquals("[]", makeAnyList.invokeGeneric().toString());
-assertEquals("[1]", makeAnyList.invokeGeneric(1).toString());
-assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString());
+Object x, y; String s; int i;
+MethodType mt; MethodHandle mh;
+MethodHandles.Lookup lookup = MethodHandles.lookup();
+// mt is (char,char)String
+mt = MethodType.methodType(String.class, char.class, char.class);
+mh = lookup.findVirtual(String.class, "replace", mt);
+// (Ljava/lang/String;CC)Ljava/lang/String;
+s = (String) mh.invokeExact("daddy",'d','n');
+assert(s.equals("nanny"));
+// weakly typed invocation (using MHs.invoke)
+s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
+assert(s.equals("savvy"));
+// mt is (Object[])List
+mt = MethodType.methodType(java.util.List.class, Object[].class);
+mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
+assert(mh.isVarargsCollector());
+x = mh.invokeGeneric("one", "two");
+assert(x.equals(java.util.Arrays.asList("one","two")));
+// mt is (Object,Object,Object)Object
+mt = MethodType.genericMethodType(3);
+mh = MethodHandles.collectArguments(mh, mt);
+// mt is (Object,Object,Object)Object
+// (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+x = mh.invokeExact((Object)1, (Object)2, (Object)3);
+assert(x.equals(java.util.Arrays.asList(1,2,3)));
+// mt is { => int}
+mt = MethodType.methodType(int.class);
+mh = lookup.findVirtual(java.util.List.class, "size", mt);
+// (Ljava/util/List;)I
+i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
+assert(i == 3);
+mt = MethodType.methodType(void.class, String.class);
+mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
+mh.invokeExact(System.out, "Hello, world.");
+{}
             }}
     }
 
-static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) {
-    //System.out.println("Converting "+asList+" to "+newType);
-    MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType);
-    //System.out.println(" =>"+conv);
-    return conv;
+    @Test public void testAsVarargsCollector() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle asList = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+assertEquals("[]", asList.invokeGeneric().toString());
+assertEquals("[1]", asList.invokeGeneric(1).toString());
+assertEquals("[two, too]", asList.invokeGeneric("two", "too").toString());
+Object[] argv = { "three", "thee", "tee" };
+assertEquals("[three, thee, tee]", asList.invokeGeneric(argv).toString());
+List ls = (List) asList.invokeGeneric((Object)argv);
+assertEquals(1, ls.size());
+assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
+            }}
+    }
+
+    @Test public void testVarargsCollectorSuppression() throws Throwable {
+        {{
+{} /// JAVADOC
+MethodHandle vamh = publicLookup()
+  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
+  .asVarargsCollector(Object[].class);
+MethodHandle invokeExact = publicLookup()
+  .findVirtual(MethodHandle.class, "invokeExact", vamh.type());
+MethodHandle mh = invokeExact.bindTo(vamh);
+assert(vamh.type().equals(mh.type()));
+assertEquals("[1, 2, 3]", vamh.invokeGeneric(1,2,3).toString());
+boolean failed = false;
+try { mh.invokeGeneric(1,2,3); }
+catch (WrongMethodTypeException ex) { failed = true; }
+assert(failed);
+{}
+            }}
+    }
 }
-
-}
diff -r c5df55701e91 -r 9e2483e6cfab jdk/test/java/dyn/MethodHandlesTest.java
--- a/jdk/test/java/dyn/MethodHandlesTest.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/test/java/dyn/MethodHandlesTest.java	Fri Feb 11 01:26:24 2011 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1108,18 +1108,6 @@
         }
     }
 
-    static MethodHandle typeHandler2(MethodHandle target, MethodType newType) {
-        MethodType oldType = target.type();
-        int oldArity = oldType.parameterCount();
-        int newArity = newType.parameterCount();
-        if (newArity < oldArity)
-            return MethodHandles.insertArguments(target, oldArity, "OPTIONAL");
-        else if (newArity > oldArity)
-            return MethodHandles.dropArguments(target, oldArity-1, newType.parameterType(oldArity-1));
-        else
-            return target;  // attempt no further conversions
-    }
-
     @Test
     public void testConvertArguments() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
@@ -1137,23 +1125,6 @@
         testConvert(true, true,  id, rtype, name, params);
     }
 
-    @Test
-    public void testTypeHandler() throws Throwable {
-        MethodHandle id = Callee.ofType(1);
-        MethodHandle th2 = PRIVATE.findStatic(MethodHandlesTest.class, "typeHandler2",
-                               MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class));
-        MethodHandle id2 = id.withTypeHandler(th2);
-        testConvert(true,  false, id2, null, "id", Object.class);
-        testConvert(true,  true,  id2, null, "id", Object.class);
-        if (true)  return;  //FIXME
-        testConvert(true,  false, id2, null, "id", String.class);  // FIXME: throws WMT
-        testConvert(false, true,  id2, null, "id", String.class);  // FIXME: should not succeed
-        testConvert(false, false, id2, null, "id", Object.class, String.class); //FIXME: array[1] line 1164
-        testConvert(true,  true,  id2, null, "id", Object.class, String.class);
-        testConvert(false, false, id2, null, "id");
-        testConvert(true,  true,  id2, null, "id");
-    }
-
     void testConvert(boolean positive, boolean useAsType,
                      MethodHandle id, Class rtype, String name, Class... params) throws Throwable {
         countTest(positive);
@@ -1183,9 +1154,9 @@
         RuntimeException error = null;
         try {
             if (useAsType)
-                target = MethodHandles.convertArguments(id, newType);
+                target = id.asType(newType);
             else
-                target = id.asType(newType);
+                target = MethodHandles.convertArguments(id, newType);
         } catch (RuntimeException ex) {
             error = ex;
         }
@@ -1205,6 +1176,20 @@
     }
 
     @Test
+    public void testVarargsCollector() throws Throwable {
+        MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called",
+                               MethodType.methodType(Object.class, String.class, Object[].class));
+        vac0 = vac0.bindTo("vac");
+        MethodHandle vac = vac0.asVarargsCollector(Object[].class);
+        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        testConvert(true,  true,  vac.asType(MethodType.genericMethodType(0)), null, "vac");
+        for (Class at : new Class[] { Object.class, String.class, Integer.class }) {
+            testConvert(true,  true,  vac.asType(MethodType.genericMethodType(1)), null, "vac", at);
+            testConvert(true,  true,  vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at);
+        }
+    }
+
+    @Test
     public void testPermuteArguments() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("permuteArguments");
@@ -1644,14 +1629,14 @@
         assertCalled("invokee", args);
         // varargs invoker #0
         calledLog.clear();
-        inv = MethodHandles.varargsInvoker(type, 0);
+        inv = MethodHandles.spreadInvoker(type, 0);
         result = inv.invokeExact(target, args);
         if (testRetCode)  assertEquals(code, result);
         assertCalled("invokee", args);
         if (nargs >= 1) {
             // varargs invoker #1
             calledLog.clear();
-            inv = MethodHandles.varargsInvoker(type, 1);
+            inv = MethodHandles.spreadInvoker(type, 1);
             result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
@@ -1659,7 +1644,7 @@
         if (nargs >= 2) {
             // varargs invoker #2
             calledLog.clear();
-            inv = MethodHandles.varargsInvoker(type, 2);
+            inv = MethodHandles.spreadInvoker(type, 2);
             result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
@@ -1667,7 +1652,7 @@
         if (nargs >= 3) {
             // varargs invoker #3
             calledLog.clear();
-            inv = MethodHandles.varargsInvoker(type, 3);
+            inv = MethodHandles.spreadInvoker(type, 3);
             result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
             if (testRetCode)  assertEquals(code, result);
             assertCalled("invokee", args);
@@ -1676,7 +1661,7 @@
             // varargs invoker #0..N
             countTest();
             calledLog.clear();
-            inv = MethodHandles.varargsInvoker(type, k);
+            inv = MethodHandles.spreadInvoker(type, k);
             List targetPlusVarArgs = new ArrayList(targetPlusArgs);
             List tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
             Object[] tail = tailList.toArray();
@@ -2089,19 +2074,6 @@
             }
         }
         // Test error checking:
-        MethodHandle genericMH = ValueConversions.varargsArray(0);
-        genericMH = MethodHandles.convertArguments(genericMH, genericMH.type().generic());
-        for (Class sam : new Class[] { Runnable.class,
-                                          Fooable.class,
-                                          Iterable.class }) {
-            try {
-                // Must throw, because none of these guys has generic type.
-                MethodHandles.asInstance(genericMH, sam);
-                System.out.println("Failed to throw");
-                assertTrue(false);
-            } catch (IllegalArgumentException ex) {
-            }
-        }
         for (Class nonSAM : new Class[] { Object.class,
                                              String.class,
                                              CharSequence.class,
diff -r c5df55701e91 -r 9e2483e6cfab jdk/test/java/dyn/indify/Indify.java
--- a/jdk/test/java/dyn/indify/Indify.java	Thu Feb 10 16:24:40 2011 -0800
+++ b/jdk/test/java/dyn/indify/Indify.java	Fri Feb 11 01:26:24 2011 -0800
@@ -98,8 +98,9 @@
 (same output as above)
  * 
  * 

- * Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized, - * the {@code --transitionalJSR292} switch is recommended (and turned on by default). + * Before OpenJDK build b123, the format of {@code CONSTANT_InvokeDynamic} is in transition, + * and the switch {@code --transitionalJSR292=yes} is recommended. + * It is turned off by default, but users of earlier builds may need to turn it on. *

* A version of this transformation built on top of http://asm.ow2.org/ would be welcome. * @author John Rose @@ -116,7 +117,7 @@ public boolean overwrite = false; public boolean quiet = false; public boolean verbose = false; - public boolean transitionalJSR292 = true; // default to false later + public boolean transitionalJSR292 = false; // final version is distributed public boolean all = false; public int verifySpecifierCount = -1; @@ -158,6 +159,7 @@ av = avl.toArray(new String[0]); Class mainClass = Class.forName(mainClassName, true, makeClassLoader()); java.lang.reflect.Method main = mainClass.getMethod("main", String[].class); + try { main.setAccessible(true); } catch (SecurityException ex) { } main.invoke(null, (Object) av); } @@ -223,8 +225,8 @@ private boolean booleanOption(String s) { if (s == null) return true; switch (s) { - case "true": case "yes": case "1": return true; - case "false": case "no": case "0": return false; + case "true": case "yes": case "on": case "1": return true; + case "false": case "no": case "off": case "0": return false; } throw new IllegalArgumentException("unrecognized boolean flag="+s); } @@ -284,7 +286,7 @@ } File classPathFile(File pathDir, String className) { - String qualname = className+".class"; + String qualname = className.replace('.','/')+".class"; qualname = qualname.replace('/', File.separatorChar); return new File(pathDir, qualname); } @@ -339,6 +341,7 @@ private File findClassInPath(String name) { for (String s : classpath) { File f = classPathFile(new File(s), name); + //System.out.println("Checking for "+f); if (f.exists() && f.canRead()) { return f; } @@ -353,11 +356,11 @@ } } private Class transformAndLoadClass(File f) throws ClassNotFoundException, IOException { - if (verbose) System.out.println("Loading class from "+f); + if (verbose) System.err.println("Loading class from "+f); ClassFile cf = new ClassFile(f); Logic logic = new Logic(cf); boolean changed = logic.transform(); - if (verbose && !changed) System.out.println("(no change)"); + if (verbose && !changed) System.err.println("(no change)"); logic.reportPatternMethods(!verbose, keepgoing); byte[] bytes = cf.toByteArray(); return defineClass(null, bytes, 0, bytes.length); @@ -525,6 +528,7 @@ throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount); } } + if (!quiet) System.err.flush(); } // mark constant pool entries according to participation in patterns @@ -696,6 +700,18 @@ args.clear(); break; + case opc_new: + { + String type = pool.getString(CONSTANT_Class, (short)i.u2At(1)); + //System.out.println("new "+type); + switch (type) { + case "java/lang/StringBuilder": + jvm.push("StringBuilder"); + continue decode; // go to next instruction + } + break decode; // bail out + } + case opc_getstatic: { // int.class compiles to getstatic Integer.TYPE @@ -732,8 +748,9 @@ case opc_invokestatic: case opc_invokevirtual: + case opc_invokespecial: { - boolean hasRecv = (bc == opc_invokevirtual); + boolean hasRecv = (bc != opc_invokestatic); int methi = i.u2At(1); char mark = poolMarks[methi]; Short[] ref = pool.getMemberRef((short)methi); @@ -770,6 +787,7 @@ if (mark == 'T' || mark == 'H' || mark == 'I') { ownMethod = findMember(cf.methods, ref[1], ref[2]); } + //if (intrinsic != null) System.out.println("intrinsic = "+intrinsic); switch (intrinsic == null ? "" : intrinsic) { case "fromMethodDescriptorString": con = makeMethodTypeCon(args.get(0)); @@ -860,6 +878,15 @@ } } break decode; + case "StringBuilder.append": + // allow calls like ("value = "+x) + removeEmptyJVMSlots(args); + args.subList(1, args.size()).clear(); + continue; + case "StringBuilder.toString": + args.clear(); + args.add(intrinsic); + continue; } if (!hasRecv && ownMethod != null && patternMark != 0) { con = constants.get(ownMethod); @@ -1506,6 +1533,7 @@ out.write(bytes); } else { trueSize = flatten(out); + //if (!(item instanceof Code)) System.err.println("wrote complex attr name="+(int)(char)name+" size="+trueSize+" data="+Arrays.toString(flatten())); } if (trueSize != size && size >= 0) System.err.println("warning: attribute size changed "+size+" to "+trueSize); @@ -1525,7 +1553,7 @@ } public List attrs() { return null; } // Code overrides this public byte[] flatten() { - ByteArrayOutputStream buf = new ByteArrayOutputStream(size); + ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size)); flatten(buf); return buf.toByteArray(); } @@ -1642,6 +1670,7 @@ opc_invokestatic = 184, opc_invokeinterface = 185, opc_invokedynamic = 186, + opc_new = 187, opc_anewarray = 189, opc_checkcast = 192, opc_ifnull = 198,