提交 9d99b46d 编写于 作者: J jrose

7013417: JSR 292 needs to support variadic method handle calls

Summary: Implement MH.asVarargsCollector, etc., and remove withTypeHandler.
Reviewed-by: twisti
上级 7440a203
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -134,6 +134,11 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility ...@@ -134,6 +134,11 @@ import static sun.dyn.MemberName.newIllegalArgumentException; // utility
* constant pool entry. * constant pool entry.
* For more details, see the <a href="package-summary.html#mhcon">package summary</a>.) * For more details, see the <a href="package-summary.html#mhcon">package summary</a>.)
* <p> * <p>
* 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}.
* <p>
* Java code can also use a reflective API called * Java code can also use a reflective API called
* {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup} * {@link java.dyn.MethodHandles.Lookup MethodHandles.Lookup}
* for creating and calling method handles. * for creating and calling method handles.
...@@ -175,6 +180,9 @@ assert(s.equals("savvy")); ...@@ -175,6 +180,9 @@ assert(s.equals("savvy"));
// mt is {Object[] =&gt; List} // mt is {Object[] =&gt; List}
mt = MethodType.methodType(java.util.List.class, Object[].class); mt = MethodType.methodType(java.util.List.class, Object[].class);
mh = lookup.findStatic(java.util.Arrays.class, "asList", mt); 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) =&gt; Object} // mt is {(Object,Object,Object) =&gt; Object}
mt = MethodType.genericMethodType(3); mt = MethodType.genericMethodType(3);
mh = MethodHandles.collectArguments(mh, mt); mh = MethodHandles.collectArguments(mh, mt);
...@@ -306,26 +314,6 @@ public abstract class MethodHandle ...@@ -306,26 +314,6 @@ public abstract class MethodHandle
}); });
} }
/**
* 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:
* <blockquote><pre>
* "MethodHandle" + type().toString()
* </pre></blockquote>
* <p>
* 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. * Invoke the method handle, allowing any caller signature, but requiring an exact signature match.
* The signature at the call site of {@code invokeExact} must * The signature at the call site of {@code invokeExact} must
...@@ -338,7 +326,7 @@ public abstract class MethodHandle ...@@ -338,7 +326,7 @@ public abstract class MethodHandle
/** /**
* Invoke the method handle, allowing any caller signature, * 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.
* <p> * <p>
* If the call site signature exactly matches this method handle's {@link #type type}, * If the call site signature exactly matches this method handle's {@link #type type},
* the call proceeds as if by {@link #invokeExact invokeExact}. * the call proceeds as if by {@link #invokeExact invokeExact}.
...@@ -353,14 +341,13 @@ public abstract class MethodHandle ...@@ -353,14 +341,13 @@ public abstract class MethodHandle
* adaptations directly on the caller's arguments, * adaptations directly on the caller's arguments,
* and call the target method handle according to its own exact type. * and call the target method handle according to its own exact type.
* <p> * <p>
* If the method handle is equipped with a * The signature at the call site of {@code invokeGeneric} must
* {@linkplain #withTypeHandler type handler}, the handler must produce * be a valid argument to the receivers {@code asType} method.
* 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.
* In particular, the caller must specify the same argument arity * 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 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 * @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; public final native @PolymorphicSignature Object invokeGeneric(Object... args) throws Throwable;
...@@ -400,14 +387,14 @@ public abstract class MethodHandle ...@@ -400,14 +387,14 @@ public abstract class MethodHandle
* <p> * <p>
* This call is equivalent to the following code: * This call is equivalent to the following code:
* <p><blockquote><pre> * <p><blockquote><pre>
* MethodHandle invoker = MethodHandles.varargsInvoker(this.type(), 0); * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
* Object result = invoker.invokeExact(this, arguments); * Object result = invoker.invokeExact(this, arguments);
* </pre></blockquote> * </pre></blockquote>
* @param arguments the arguments to pass to the target * @param arguments the arguments to pass to the target
* @return the result returned by the target * @return the result returned by the target
* @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the arguments
* @throws Throwable anything thrown by the target method invocation * @throws Throwable anything thrown by the target method invocation
* @see MethodHandles#varargsInvoker * @see MethodHandles#spreadInvoker
*/ */
public final Object invokeWithArguments(Object... arguments) throws Throwable { public final Object invokeWithArguments(Object... arguments) throws Throwable {
int argc = arguments == null ? 0 : arguments.length; int argc = arguments == null ? 0 : arguments.length;
...@@ -456,7 +443,7 @@ public abstract class MethodHandle ...@@ -456,7 +443,7 @@ public abstract class MethodHandle
} }
// more than ten arguments get boxed in a varargs list: // 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); return invoker.invokeExact(this, arguments);
} }
/** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */ /** Equivalent to {@code invokeWithArguments(arguments.toArray())}. */
...@@ -488,13 +475,7 @@ public abstract class MethodHandle ...@@ -488,13 +475,7 @@ public abstract class MethodHandle
* to match up the caller's and callee's types. * to match up the caller's and callee's types.
* <p> * <p>
* This method is equivalent to {@link MethodHandles#convertArguments convertArguments}, * This method is equivalent to {@link MethodHandles#convertArguments convertArguments},
* except for method handles produced by {@link #withTypeHandler withTypeHandler}, * except for variable arity method handles produced by {@link #asVarargsCollector asVarargsCollector}.
* in which case the specified type handler is used for calls to {@code asType}.
* <p>
* 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.
* *
* @param newType the expected type of the new method handle * @param newType the expected type of the new method handle
* @return a method handle which delegates to {@code this} after performing * @return a method handle which delegates to {@code this} after performing
...@@ -508,14 +489,16 @@ public abstract class MethodHandle ...@@ -508,14 +489,16 @@ public abstract class MethodHandle
} }
/** /**
* Produce a method handle which adapts, as its <i>target</i>, * Make an adapter which accepts a trailing array argument
* and spreads its elements as positional arguments.
* The new method handle adapts, as its <i>target</i>,
* the current method handle. The type of the adapter will be * the current method handle. The type of the adapter will be
* the same as the type of the target, except that the final * the same as the type of the target, except that the final
* {@code arrayLength} parameters of the target's type are replaced * {@code arrayLength} parameters of the target's type are replaced
* by a single array parameter of type {@code arrayType}. * by a single array parameter of type {@code arrayType}.
* <p> * <p>
* If the array element type differs from any of the corresponding * 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, * the original target is adapted to take the array elements directly,
* as if by a call to {@link #asType asType}. * as if by a call to {@link #asType asType}.
* <p> * <p>
...@@ -539,6 +522,7 @@ public abstract class MethodHandle ...@@ -539,6 +522,7 @@ public abstract class MethodHandle
* @throws IllegalArgumentException if target does not have at least * @throws IllegalArgumentException if target does not have at least
* {@code arrayLength} parameter types * {@code arrayLength} parameter types
* @throws WrongMethodTypeException if the implied {@code asType} call fails * @throws WrongMethodTypeException if the implied {@code asType} call fails
* @see #asCollector
*/ */
public final MethodHandle asSpreader(Class<?> arrayType, int arrayLength) { public final MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType(); Class<?> arrayElement = arrayType.getComponentType();
...@@ -553,13 +537,15 @@ public abstract class MethodHandle ...@@ -553,13 +537,15 @@ public abstract class MethodHandle
} }
/** /**
* Produce a method handle which adapts, as its <i>target</i>, * 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 <i>target</i>,
* the current method handle. The type of the adapter will be * the current method handle. The type of the adapter will be
* the same as the type of the target, except that a single trailing * the same as the type of the target, except that a single trailing
* parameter (usually of type {@code arrayType}) is replaced by * parameter (usually of type {@code arrayType}) is replaced by
* {@code arrayLength} parameters whose type is element type of {@code arrayType}. * {@code arrayLength} parameters whose type is element type of {@code arrayType}.
* <p> * <p>
* 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, * the original target is adapted to take the array type directly,
* as if by a call to {@link #asType asType}. * as if by a call to {@link #asType asType}.
* <p> * <p>
...@@ -570,21 +556,31 @@ public abstract class MethodHandle ...@@ -570,21 +556,31 @@ public abstract class MethodHandle
* What the target eventually returns is returned unchanged by the adapter. * What the target eventually returns is returned unchanged by the adapter.
* <p> * <p>
* (The array may also be a shared constant when {@code arrayLength} is zero.) * (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 * <p>
* (<em>Note:</em> 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.)
* <p>
* 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 * @param arrayLength the number of arguments to collect into a new array argument
* @return a new method handle which collects some trailing argument * @return a new method handle which collects some trailing argument
* into an array, before calling the original method handle * into an array, before calling the original method handle
* @throws IllegalArgumentException if {@code arrayType} is not an array type * @throws IllegalArgumentException if {@code arrayType} is not an array type
or {@code arrayType} is not assignable to this method handle's trailing parameter type * or {@code arrayType} is not assignable to this method handle's trailing parameter type,
* @throws IllegalArgumentException if {@code arrayLength} is not * or {@code arrayLength} is not a legal array size
* a legal array size
* @throws WrongMethodTypeException if the implied {@code asType} call fails * @throws WrongMethodTypeException if the implied {@code asType} call fails
* @see #asSpreader
* @see #asVarargsCollector
*/ */
public final MethodHandle asCollector(Class<?> arrayType, int arrayLength) { public final MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
Class<?> arrayElement = arrayType.getComponentType(); Class<?> arrayElement = arrayType.getComponentType();
if (arrayElement == null) throw newIllegalArgumentException("not an array type"); if (arrayElement == null) throw newIllegalArgumentException("not an array type");
MethodType oldType = type(); MethodType oldType = type();
int nargs = oldType.parameterCount(); int nargs = oldType.parameterCount();
if (nargs == 0) throw newIllegalArgumentException("no trailing argument");
MethodType newType = oldType.dropParameterTypes(nargs-1, nargs); MethodType newType = oldType.dropParameterTypes(nargs-1, nargs);
newType = newType.insertParameterTypes(nargs-1, newType = newType.insertParameterTypes(nargs-1,
java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement)); java.util.Collections.<Class<?>>nCopies(arrayLength, arrayElement));
...@@ -592,8 +588,183 @@ public abstract class MethodHandle ...@@ -592,8 +588,183 @@ public abstract class MethodHandle
} }
/** /**
* Produce a method handle which binds the given argument * Make a <em>variable arity</em> adapter which is able to accept
* to the current method handle as <i>target</i>. * any number of trailing positional arguments and collect them
* into an array argument.
* <p>
* 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.
* <p>
* When called with {@link #invokeExact invokeExact}, the adapter invokes
* the target with no argument changes.
* (<em>Note:</em> 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.)
* <p>
* 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.)
* <p>
* 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.)
* <p>
* 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.
* <p>
* 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.
* <p>
* In all cases, what the target eventually returns is returned unchanged by the adapter.
* <p>
* 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}.)
* <p>
* 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}.
* <p>
* 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}.
* <p>
* 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.
* <p>
* 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).
* <p>
* 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.
* <p>
* Here is an example, of a list-making variable arity method handle:
* <blockquote><pre>
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)));
* </pre></blockquote>
* <p style="font-size:smaller;">
* 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.)
* <p style="font-size:smaller;">
* 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:
* <blockquote><pre>
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);
* </pre></blockquote>
* 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:
* <ul>
* <li>a call to {@linkplain #asVarargsCollector asVarargsCollector}
* <li>a call to a {@linkplain java.dyn.MethodHandles.Lookup lookup method}
* which resolves to a variable arity Java method or constructor
* <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
* which resolves to a variable arity Java method or constructor
* </ul>
* @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 <i>target</i>,
* to the current method handle.
* The type of the bound handle will be * The type of the bound handle will be
* the same as the type of the target, except that a single leading * the same as the type of the target, except that a single leading
* reference parameter will be omitted. * reference parameter will be omitted.
...@@ -619,74 +790,22 @@ public abstract class MethodHandle ...@@ -619,74 +790,22 @@ public abstract class MethodHandle
} }
/** /**
* <em>PROVISIONAL API, WORK IN PROGRESS:</em> * Returns a string representation of the method handle,
* Create a new method handle with the same type as this one, * starting with the string {@code "MethodHandle"} and
* but whose {@code asType} method invokes the given * ending with the string representation of the method handle's type.
* {@code typeHandler} on this method handle, * In other words, this method returns a string equal to the value of:
* instead of the standard {@code MethodHandles.convertArguments}.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* Therefore, the type handler is invoked as if by this code:
* <blockquote><pre> * <blockquote><pre>
* MethodHandle target = this; // original method handle * "MethodHandle" + type().toString()
* 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;
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* For example, here is a list-making variable-arity method handle: * Note: Future releases of this API may add further information
* <blockquote><pre> * to the string representation.
MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList()); * Therefore, the present syntax should not be parsed by applications.
MethodHandle asList = lookup() *
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); * @return a string representation of the method handle
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());
* <pre><blockquote>
*/ */
public MethodHandle withTypeHandler(MethodHandle typeHandler) { @Override
return MethodHandles.withTypeHandler(this, typeHandler); public String toString() {
return MethodHandleImpl.getNameString(IMPL_TOKEN, this);
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -135,7 +135,10 @@ public class MethodHandles { ...@@ -135,7 +135,10 @@ public class MethodHandles {
* <p> * <p>
* In general, the conditions under which a method handle may be * In general, the conditions under which a method handle may be
* created for a method {@code M} are exactly as restrictive as the conditions * 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.
* <p> * <p>
* In some cases, this access is obtained by the Java compiler by creating * In some cases, this access is obtained by the Java compiler by creating
* an wrapper method to access a private method of another class * an wrapper method to access a private method of another class
...@@ -379,6 +382,10 @@ public class MethodHandles { ...@@ -379,6 +382,10 @@ public class MethodHandles {
* The method and all its argument types must be accessible to the lookup class. * 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 * If the method's class has not yet been initialized, that is done
* immediately, before the method handle is returned. * immediately, before the method handle is returned.
* <p>
* 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 refc the class from which the method is accessed
* @param name the name of the method * @param name the name of the method
* @param type the type of the method * @param type the type of the method
...@@ -403,6 +410,10 @@ public class MethodHandles { ...@@ -403,6 +410,10 @@ public class MethodHandles {
* implementation to enter. * implementation to enter.
* (The dispatching action is identical with that performed by an * (The dispatching action is identical with that performed by an
* {@code invokevirtual} or {@code invokeinterface} instruction.) * {@code invokevirtual} or {@code invokeinterface} instruction.)
* <p>
* 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 refc the class or interface from which the method is accessed
* @param name the name of the method * @param name the name of the method
* @param type the type of the method, with the receiver argument omitted * @param type the type of the method, with the receiver argument omitted
...@@ -427,6 +438,10 @@ public class MethodHandles { ...@@ -427,6 +438,10 @@ public class MethodHandles {
* <p> * <p>
* Note: The requested type must have a return type of {@code void}. * Note: The requested type must have a return type of {@code void}.
* This is consistent with the JVM's treatment of constructor signatures. * This is consistent with the JVM's treatment of constructor signatures.
* <p>
* 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 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 * @param type the type of the method, with the receiver argument omitted, and a void return type
* @return the desired method handle * @return the desired method handle
...@@ -438,7 +453,23 @@ public class MethodHandles { ...@@ -438,7 +453,23 @@ public class MethodHandles {
assert(ctor.isConstructor()); assert(ctor.isConstructor());
checkAccess(refc, ctor); checkAccess(refc, ctor);
MethodHandle rawMH = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); 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 @@ public class MethodHandles { ...@@ -458,6 +489,10 @@ public class MethodHandles {
* If the explicitly specified caller class is not identical with the * If the explicitly specified caller class is not identical with the
* lookup class, or if this lookup object does not have private access * lookup class, or if this lookup object does not have private access
* privileges, the access fails. * privileges, the access fails.
* <p>
* 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 refc the class or interface from which the method is accessed
* @param name the name of the method (which must not be "&lt;init&gt;") * @param name the name of the method (which must not be "&lt;init&gt;")
* @param type the type of the method, with the receiver argument omitted * @param type the type of the method, with the receiver argument omitted
...@@ -547,13 +582,26 @@ public class MethodHandles { ...@@ -547,13 +582,26 @@ public class MethodHandles {
* so that every call to the method handle will invoke the * so that every call to the method handle will invoke the
* requested method on the given receiver. * requested method on the given receiver.
* <p> * <p>
* This is equivalent to the following expression: * The returned method handle will have
* <code> * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
* {@link #insertArguments insertArguments}({@link #findVirtual findVirtual}(defc, name, type), receiver) * the method's variable arity modifier bit ({@code 0x0080}) is set
* </code> * <em>and</em> 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.)
* <p>
* This is equivalent to the following code:
* <blockquote><pre>
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;
* </pre></blockquote>
* where {@code defc} is either {@code receiver.getClass()} or a super * where {@code defc} is either {@code receiver.getClass()} or a super
* type of that class, in which the requested method is accessible * type of that class, in which the requested method is accessible
* to the lookup class. * to the lookup class.
* (Note that {@code bindTo} does not preserve variable arity.)
* @param receiver the object from which the method is accessed * @param receiver the object from which the method is accessed
* @param name the name of the method * @param name the name of the method
* @param type the type of the method, with the receiver argument omitted * @param type the type of the method, with the receiver argument omitted
...@@ -568,7 +616,9 @@ public class MethodHandles { ...@@ -568,7 +616,9 @@ public class MethodHandles {
MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver); MethodHandle bmh = MethodHandleImpl.bindReceiver(IMPL_TOKEN, dmh, receiver);
if (bmh == null) if (bmh == null)
throw newNoAccessException(method, lookupClass()); 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 @@ public class MethodHandles { ...@@ -581,6 +631,10 @@ public class MethodHandles {
* If the method's {@code accessible} flag is not set, * If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class. * access checking is performed immediately on behalf of the lookup class.
* If <i>m</i> is not public, do not share the resulting handle with untrusted parties. * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
* <p>
* 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 m the reflected method
* @return a method handle which can invoke the reflected method * @return a method handle which can invoke the reflected method
* @exception NoAccessException if access checking fails * @exception NoAccessException if access checking fails
...@@ -603,6 +657,10 @@ public class MethodHandles { ...@@ -603,6 +657,10 @@ public class MethodHandles {
* If the method's {@code accessible} flag is not set, * If the method's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class, * access checking is performed immediately on behalf of the lookup class,
* as if {@code invokespecial} instruction were being linked. * as if {@code invokespecial} instruction were being linked.
* <p>
* 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 m the reflected method
* @param specialCaller the class nominally calling the method * @param specialCaller the class nominally calling the method
* @return a method handle which can invoke the reflected method * @return a method handle which can invoke the reflected method
...@@ -628,6 +686,10 @@ public class MethodHandles { ...@@ -628,6 +686,10 @@ public class MethodHandles {
* <p> * <p>
* If the constructor's {@code accessible} flag is not set, * If the constructor's {@code accessible} flag is not set,
* access checking is performed immediately on behalf of the lookup class. * access checking is performed immediately on behalf of the lookup class.
* <p>
* 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 * @param c the reflected constructor
* @return a method handle which can invoke the reflected constructor * @return a method handle which can invoke the reflected constructor
* @exception NoAccessException if access checking fails * @exception NoAccessException if access checking fails
...@@ -637,7 +699,8 @@ public class MethodHandles { ...@@ -637,7 +699,8 @@ public class MethodHandles {
assert(ctor.isConstructor()); assert(ctor.isConstructor());
if (!c.isAccessible()) checkAccess(c.getDeclaringClass(), ctor); if (!c.isAccessible()) checkAccess(c.getDeclaringClass(), ctor);
MethodHandle rawCtor = MethodHandleImpl.findMethod(IMPL_TOKEN, ctor, false, lookupClassOrNull()); 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 @@ public class MethodHandles { ...@@ -785,7 +848,8 @@ public class MethodHandles {
MethodType rawType = mh.type(); MethodType rawType = mh.type();
if (rawType.parameterType(0) == caller) return mh; if (rawType.parameterType(0) == caller) return mh;
MethodType narrowType = rawType.changeParameterType(0, caller); 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, MethodHandle makeAccessor(Class<?> refc, String name, Class<?> type,
...@@ -909,22 +973,21 @@ public class MethodHandles { ...@@ -909,22 +973,21 @@ public class MethodHandles {
* <p> * <p>
* This method is equivalent to the following code (though it may be more efficient): * This method is equivalent to the following code (though it may be more efficient):
* <p><blockquote><pre> * <p><blockquote><pre>
* MethodHandle invoker = lookup().findVirtual(MethodHandle.class, "invokeGeneric", type); MethodHandle invoker = publicLookup()
* MethodType vaType = MethodType.genericMethodType(objectArgCount, true); .findVirtual(MethodHandle.class, "invokeGeneric", type)
* vaType = vaType.insertParameterType(0, MethodHandle.class); int spreadArgCount = type.parameterCount - objectArgCount;
* int spreadArgCount = type.parameterCount - objectArgCount; invoker = invoker.asSpreader(Object[].class, spreadArgCount);
* invoker = invoker.asSpreader(Object.class, spreadArgCount); return invoker;
* return invoker.asType(vaType);
* </pre></blockquote> * </pre></blockquote>
* @param type the desired target type * @param type the desired target type
* @param objectArgCount number of fixed (non-varargs) {@code Object} arguments * @param objectArgCount number of fixed (non-varargs) {@code Object} arguments
* @return a method handle suitable for invoking any method handle of the given type * @return a method handle suitable for invoking any method handle of the given type
*/ */
static public static public
MethodHandle varargsInvoker(MethodType type, int objectArgCount) { MethodHandle spreadInvoker(MethodType type, int objectArgCount) {
if (objectArgCount < 0 || objectArgCount > type.parameterCount()) if (objectArgCount < 0 || objectArgCount > type.parameterCount())
throw new IllegalArgumentException("bad argument count "+objectArgCount); throw new IllegalArgumentException("bad argument count "+objectArgCount);
return invokers(type).varargsInvoker(objectArgCount); return invokers(type).spreadInvoker(objectArgCount);
} }
/** /**
...@@ -1826,7 +1889,10 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1826,7 +1889,10 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
* the given {@code target} on the incoming arguments, * the given {@code target} on the incoming arguments,
* and returning or throwing whatever the {@code target} * and returning or throwing whatever the {@code target}
* returns or throws. The invocation will be as if by * 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}.
* <p> * <p>
* The method handle may throw an <em>undeclared exception</em>, * The method handle may throw an <em>undeclared exception</em>,
* which means any checked exception (or other checked throwable) * which means any checked exception (or other checked throwable)
...@@ -1874,15 +1940,17 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1874,15 +1940,17 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
* {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject} * {@link java.dyn.MethodHandles.AsInstanceObject AsInstanceObject}
* in the adapters, so that generic code can extract the underlying * in the adapters, so that generic code can extract the underlying
* method handle without knowing where the SAM adapter came from. * method handle without knowing where the SAM adapter came from.
* <p>
* 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 target the method handle to invoke from the wrapper
* @param samType the desired type of the wrapper, a SAM type * @param samType the desired type of the wrapper, a SAM type
* @return a correctly-typed wrapper for the given {@code target} * @return a correctly-typed wrapper for the given {@code target}
* @throws IllegalArgumentException if the {@code target} throws * @throws IllegalArgumentException if the {@code samType} is not a
* an undeclared exception * 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 public static
<T> T asInstance(final MethodHandle target, final Class<T> samType) { <T> T asInstance(final MethodHandle target, final Class<T> samType) {
// POC implementation only; violates the above contract several ways // POC implementation only; violates the above contract several ways
...@@ -1890,8 +1958,9 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1890,8 +1958,9 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
if (sam == null) if (sam == null)
throw new IllegalArgumentException("not a SAM type: "+samType.getName()); throw new IllegalArgumentException("not a SAM type: "+samType.getName());
MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes()); MethodType samMT = MethodType.methodType(sam.getReturnType(), sam.getParameterTypes());
if (!samMT.equals(target.type())) MethodHandle checkTarget = target.asType(samMT); // make throw WMT
throw new IllegalArgumentException("wrong method type: "+target+" should match "+sam); checkTarget = checkTarget.asType(checkTarget.type().changeReturnType(Object.class));
final MethodHandle vaTarget = checkTarget.asSpreader(Object[].class, samMT.parameterCount());
return samType.cast(Proxy.newProxyInstance( return samType.cast(Proxy.newProxyInstance(
samType.getClassLoader(), samType.getClassLoader(),
new Class[]{ samType, AsInstanceObject.class }, new Class[]{ samType, AsInstanceObject.class },
...@@ -1905,7 +1974,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1905,7 +1974,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
if (method.getDeclaringClass() == AsInstanceObject.class) if (method.getDeclaringClass() == AsInstanceObject.class)
return getArg(method.getName()); return getArg(method.getName());
if (method.equals(sam)) if (method.equals(sam))
return target.invokeVarargs(args); return vaTarget.invokeExact(args);
if (isObjectMethod(method)) if (isObjectMethod(method))
return callObjectMethod(this, method, args); return callObjectMethod(this, method, args);
throw new InternalError(); throw new InternalError();
...@@ -1991,7 +2060,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2 ...@@ -1991,7 +2060,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
} }
/*non-public*/ /*non-public*/
static MethodHandle withTypeHandler(MethodHandle target, MethodHandle typeHandler) { static MethodHandle asVarargsCollector(MethodHandle target, Class<?> arrayType) {
return MethodHandleImpl.withTypeHandler(IMPL_TOKEN, target, typeHandler); return MethodHandleImpl.asVarargsCollector(IMPL_TOKEN, target, arrayType);
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -166,13 +166,13 @@ ...@@ -166,13 +166,13 @@
* normal accesses are legal. * normal accesses are legal.
* <p> * <p>
* A constant may refer to a method or constructor with the {@code varargs} * A constant may refer to a method or constructor with the {@code varargs}
* bit (hexadecimal {@code 80}) set in its modifier bitmask. * bit (hexadecimal {@code 0x0080}) set in its modifier bitmask.
* The method handle constant produced for such a method behaves the same * 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. * 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.
* <p> * <p>
* Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types * Although the {@code CONSTANT_MethodHandle} and {@code CONSTANT_MethodType} constant types
* resolve class names, they do not force class initialization. * resolve class names, they do not force class initialization.
...@@ -369,21 +369,40 @@ ...@@ -369,21 +369,40 @@
* the instruction's bootstrap method will be invoked on three arguments, * the instruction's bootstrap method will be invoked on three arguments,
* conveying the instruction's caller class, name, and method type. * conveying the instruction's caller class, name, and method type.
* If the {@code invokedynamic} instruction specifies one or more static arguments, * If the {@code invokedynamic} instruction specifies one or more static arguments,
* a fourth argument will be passed to the bootstrap argument, * those values will be passed as additional arguments to the method handle.
* either an {@code Object} reference to the sole extra argument (if there is one) * (Note that because there is a limit of 255 arguments to any method,
* or an {@code Object} array of references to all the arguments (if there are two or more), * at most 252 extra arguments can be supplied.)
* as if the bootstrap method is a variable-arity method. * 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.)
* <p>
* 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.
* <code> * <code>
* <table border=1 cellpadding=5 summary="Static argument types"> * <table border=1 cellpadding=5 summary="Static argument types">
* <tr><th>N</th><th>sample bootstrap method</th></tr> * <tr><th>N</th><th>sample bootstrap method</th></tr>
* <tr><td>*</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
* <tr><td>*</td><td><code>CallSite bootstrap(Object... args)</code></td></tr>
* <tr><td>*</td><td><code>CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)</code></td></tr>
* <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type)</code></td></tr> * <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type)</code></td></tr>
* <tr><td>0</td><td><code>CallSite bootstrap(Lookup caller, Object... nameAndType)</code></td></tr>
* <tr><td>1</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)</code></td></tr> * <tr><td>1</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)</code></td></tr>
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr> * <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)</code></td></tr>
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)</code></td></tr>
* <tr><td>2</td><td><code>CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)</code></td></tr>
* </table> * </table>
* </code> * </code>
* 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.
* <p> * <p>
* 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. * As noted above, the actual method type of the bootstrap method can vary.
* For example, the fourth argument could be {@code MethodHandle}, * For example, the fourth argument could be {@code MethodHandle},
* if that is the type of the corresponding constant in * if that is the type of the corresponding constant in
...@@ -391,14 +410,8 @@ ...@@ -391,14 +410,8 @@
* In that case, the {@code invokeGeneric} call will pass the extra method handle * 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} * 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. * 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 a string constant were passed instead, by badly generated code, that cast would then fail,
* <p> * resulting in an {@code InvokeDynamicBootstrapError}.)
* 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.
* <p>
* 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.
* <p> * <p>
* Extra bootstrap method arguments are intended to allow language implementors * Extra bootstrap method arguments are intended to allow language implementors
* to safely and compactly encode metadata. * to safely and compactly encode metadata.
...@@ -406,24 +419,6 @@ ...@@ -406,24 +419,6 @@
* since each call site could be given its own unique bootstrap method. * 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. * Such a practice is likely to produce large class files and constant pools.
* *
* <p style="font-size:smaller;">
* <em>PROVISIONAL API, WORK IN PROGRESS:</em>
* (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.)
*
* <h2><a name="structs"></a>Structure Summary</h2> * <h2><a name="structs"></a>Structure Summary</h2>
* <blockquote><pre>// summary of constant and attribute structures * <blockquote><pre>// summary of constant and attribute structures
struct CONSTANT_MethodHandle_info { struct CONSTANT_MethodHandle_info {
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -478,37 +478,60 @@ public class AdapterMethodHandle extends BoundMethodHandle { ...@@ -478,37 +478,60 @@ public class AdapterMethodHandle extends BoundMethodHandle {
return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY)); return new AdapterMethodHandle(target, newType, makeConv(raw ? OP_RETYPE_RAW : OP_RETYPE_ONLY));
} }
static MethodHandle makeTypeHandler(Access token, static MethodHandle makeVarargsCollector(Access token,
MethodHandle target, MethodHandle typeHandler) { MethodHandle target, Class<?> arrayType) {
Access.check(token); Access.check(token);
return new WithTypeHandler(target, typeHandler); return new AsVarargsCollector(target, arrayType);
} }
static class WithTypeHandler extends AdapterMethodHandle { static class AsVarargsCollector extends AdapterMethodHandle {
final MethodHandle target, typeHandler; final MethodHandle target;
WithTypeHandler(MethodHandle target, MethodHandle typeHandler) { final Class<?> arrayType;
MethodHandle cache;
AsVarargsCollector(MethodHandle target, Class<?> arrayType) {
super(target, target.type(), makeConv(OP_RETYPE_ONLY)); super(target, target.type(), makeConv(OP_RETYPE_ONLY));
this.target = target; 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) { public MethodHandle asType(MethodType newType) {
if (this.type() == newType) MethodType type = this.type();
return this; 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 { try {
MethodHandle retyped = (MethodHandle) typeHandler.invokeExact(target, newType); collector = target.asCollector(arrayType, arrayLength);
// Contract: Must return the desired type, or throw WMT } catch (IllegalArgumentException ex) {
if (retyped.type() != newType) throw new WrongMethodTypeException("cannot build collector");
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);
} }
cache = collector;
return collector.asType(newType);
}
public MethodHandle asVarargsCollector(Class<?> arrayType) {
MethodType type = this.type();
if (type.parameterType(type.parameterCount()-1) == arrayType)
return this;
return super.asVarargsCollector(arrayType);
} }
private static final MethodType TYPE_HANDLER_TYPE
= MethodType.methodType(MethodHandle.class, MethodHandle.class, MethodType.class);
} }
/** Can a checkcast adapter validly convert the target to newType? /** Can a checkcast adapter validly convert the target to newType?
......
...@@ -37,17 +37,17 @@ public class CallSiteImpl { ...@@ -37,17 +37,17 @@ public class CallSiteImpl {
static CallSite makeSite(MethodHandle bootstrapMethod, static CallSite makeSite(MethodHandle bootstrapMethod,
// Callee information: // Callee information:
String name, MethodType type, String name, MethodType type,
// Call-site attributes, if any: // Extra arguments for BSM, if any:
Object info, Object info,
// Caller information: // Caller information:
MemberName callerMethod, int callerBCI) { MemberName callerMethod, int callerBCI) {
Class<?> callerClass = callerMethod.getDeclaringClass(); Class<?> callerClass = callerMethod.getDeclaringClass();
Object caller; Object caller;
if (bootstrapMethod.type().parameterType(0) == Class.class) if (bootstrapMethod.type().parameterType(0) == Class.class && TRANSITIONAL_BEFORE_PFD)
caller = callerClass; // remove for PFD caller = callerClass; // remove for PFD
else else
caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass); caller = MethodHandleImpl.IMPL_LOOKUP.in(callerClass);
if (bootstrapMethod == null) { if (bootstrapMethod == null && TRANSITIONAL_BEFORE_PFD) {
// If there is no bootstrap method, throw IncompatibleClassChangeError. // If there is no bootstrap method, throw IncompatibleClassChangeError.
// This is a valid generic error type for resolution (JLS 12.3.3). // This is a valid generic error type for resolution (JLS 12.3.3).
throw new IncompatibleClassChangeError throw new IncompatibleClassChangeError
...@@ -56,30 +56,35 @@ public class CallSiteImpl { ...@@ -56,30 +56,35 @@ public class CallSiteImpl {
CallSite site; CallSite site;
try { try {
Object binding; Object binding;
info = maybeReBox(info);
if (info == null) { if (info == null) {
if (false) // switch when invokeGeneric works binding = bootstrapMethod.invokeGeneric(caller, name, type);
binding = bootstrapMethod.invokeGeneric(caller, name, type); } else if (!info.getClass().isArray()) {
else binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
binding = bootstrapMethod.invokeVarargs(new Object[]{ caller, name, type });
} else { } else {
info = maybeReBox(info); Object[] argv = (Object[]) info;
if (false) // switch when invokeGeneric works if (3 + argv.length > 255)
binding = bootstrapMethod.invokeGeneric(caller, name, type, info); 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 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); //System.out.println("BSM for "+name+type+" => "+binding);
if (binding instanceof CallSite) { if (binding instanceof CallSite) {
site = (CallSite) binding; site = (CallSite) binding;
} else if (binding instanceof MethodHandle) { } else if (binding instanceof MethodHandle && TRANSITIONAL_BEFORE_PFD) {
// Transitional! // Transitional!
MethodHandle target = (MethodHandle) binding; MethodHandle target = (MethodHandle) binding;
site = new ConstantCallSite(target); site = new ConstantCallSite(target);
} else { } 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, if (TRANSITIONAL_BEFORE_PFD)
callerMethod, callerBCI); PRIVATE_INITIALIZE_CALL_SITE.invokeExact(site, name, type,
callerMethod, callerBCI);
assert(site.getTarget() != null); assert(site.getTarget() != null);
assert(site.getTarget().type().equals(type)); assert(site.getTarget().type().equals(type));
} catch (Throwable ex) { } catch (Throwable ex) {
...@@ -93,6 +98,8 @@ public class CallSiteImpl { ...@@ -93,6 +98,8 @@ public class CallSiteImpl {
return site; return site;
} }
private static boolean TRANSITIONAL_BEFORE_PFD = true; // FIXME: remove for PFD
private static Object maybeReBox(Object x) { private static Object maybeReBox(Object x) {
if (x instanceof Integer) { if (x instanceof Integer) {
int xi = (int) x; int xi = (int) x;
...@@ -117,6 +124,7 @@ public class CallSiteImpl { ...@@ -117,6 +124,7 @@ public class CallSiteImpl {
static { static {
try { try {
PRIVATE_INITIALIZE_CALL_SITE = PRIVATE_INITIALIZE_CALL_SITE =
!TRANSITIONAL_BEFORE_PFD ? null :
MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM", MethodHandleImpl.IMPL_LOOKUP.findVirtual(CallSite.class, "initializeFromJVM",
MethodType.methodType(void.class, MethodType.methodType(void.class,
String.class, MethodType.class, String.class, MethodType.class,
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -108,7 +108,7 @@ class InvokeGeneric { ...@@ -108,7 +108,7 @@ class InvokeGeneric {
*/ */
private MethodHandle dispatch(MethodType callerType, MethodHandle target) { private MethodHandle dispatch(MethodType callerType, MethodHandle target) {
MethodType targetType = target.type(); 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); MethodHandle newTarget = target.asType(callerType);
targetType = callerType; targetType = callerType;
Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType); Invokers invokers = MethodTypeImpl.invokers(Access.TOKEN, targetType);
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -47,7 +47,7 @@ public class Invokers { ...@@ -47,7 +47,7 @@ public class Invokers {
private /*lazy*/ MethodHandle genericInvoker; private /*lazy*/ MethodHandle genericInvoker;
// generic (untyped) invoker for the outgoing call; accepts a single Object[] // 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 // invoker for an unbound callsite
private /*lazy*/ MethodHandle uninitializedCallSite; private /*lazy*/ MethodHandle uninitializedCallSite;
...@@ -55,10 +55,9 @@ public class Invokers { ...@@ -55,10 +55,9 @@ public class Invokers {
/** Compute and cache information common to all collecting adapters /** Compute and cache information common to all collecting adapters
* that implement members of the erasure-family of the given erased type. * that implement members of the erasure-family of the given erased type.
*/ */
public Invokers(Access token, MethodType targetType) { /*non-public*/ Invokers(MethodType targetType) {
Access.check(token);
this.targetType = targetType; this.targetType = targetType;
this.varargsInvokers = new MethodHandle[targetType.parameterCount()+1]; this.spreadInvokers = new MethodHandle[targetType.parameterCount()+1];
} }
public static MethodType invokerType(MethodType targetType) { public static MethodType invokerType(MethodType targetType) {
...@@ -69,7 +68,7 @@ public class Invokers { ...@@ -69,7 +68,7 @@ public class Invokers {
MethodHandle invoker = exactInvoker; MethodHandle invoker = exactInvoker;
if (invoker != null) return invoker; if (invoker != null) return invoker;
try { try {
invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invoke", targetType); invoker = MethodHandleImpl.IMPL_LOOKUP.findVirtual(MethodHandle.class, "invokeExact", targetType);
} catch (NoAccessException ex) { } catch (NoAccessException ex) {
throw new InternalError("JVM cannot find invoker for "+targetType); throw new InternalError("JVM cannot find invoker for "+targetType);
} }
...@@ -101,13 +100,13 @@ public class Invokers { ...@@ -101,13 +100,13 @@ public class Invokers {
return invoker; return invoker;
} }
public MethodHandle varargsInvoker(int objectArgCount) { public MethodHandle spreadInvoker(int objectArgCount) {
MethodHandle vaInvoker = varargsInvokers[objectArgCount]; MethodHandle vaInvoker = spreadInvokers[objectArgCount];
if (vaInvoker != null) return vaInvoker; if (vaInvoker != null) return vaInvoker;
MethodHandle gInvoker = genericInvoker(); MethodHandle gInvoker = genericInvoker();
MethodType vaType = MethodType.genericMethodType(objectArgCount, true); MethodType vaType = MethodType.genericMethodType(objectArgCount, true);
vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType)); vaInvoker = MethodHandles.spreadArguments(gInvoker, invokerType(vaType));
varargsInvokers[objectArgCount] = vaInvoker; spreadInvokers[objectArgCount] = vaInvoker;
return vaInvoker; return vaInvoker;
} }
...@@ -118,7 +117,7 @@ public class Invokers { ...@@ -118,7 +117,7 @@ public class Invokers {
if (invoker != null) return invoker; if (invoker != null) return invoker;
if (targetType.parameterCount() > 0) { if (targetType.parameterCount() > 0) {
MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount()); MethodType type0 = targetType.dropParameterTypes(0, targetType.parameterCount());
Invokers invokers0 = MethodTypeImpl.invokers(Access.TOKEN, type0); Invokers invokers0 = MethodTypeImpl.invokers(type0);
invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(), invoker = MethodHandles.dropArguments(invokers0.uninitializedCallSite(),
0, targetType.parameterList()); 0, targetType.parameterList());
assert(invoker.type().equals(targetType)); assert(invoker.type().equals(targetType));
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -184,7 +184,10 @@ public abstract class MethodHandleImpl { ...@@ -184,7 +184,10 @@ public abstract class MethodHandleImpl {
if (!mh.isValid()) if (!mh.isValid())
throw newNoAccessException(method, lookupClass); throw newNoAccessException(method, lookupClass);
assert(mh.type() == mtype); assert(mh.type() == mtype);
return mh; if (!method.isVarargs())
return mh;
else
return mh.asVarargsCollector(mtype.parameterType(mtype.parameterCount()-1));
} }
public static public static
...@@ -1263,8 +1266,8 @@ public abstract class MethodHandleImpl { ...@@ -1263,8 +1266,8 @@ public abstract class MethodHandleImpl {
return MethodHandleNatives.getBootstrap(callerClass); 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); Access.check(token);
return AdapterMethodHandle.makeTypeHandler(token, target, typeHandler); return AdapterMethodHandle.makeVarargsCollector(token, target, arrayType);
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -498,9 +498,12 @@ public class MethodTypeImpl { ...@@ -498,9 +498,12 @@ public class MethodTypeImpl {
public static Invokers invokers(Access token, MethodType type) { public static Invokers invokers(Access token, MethodType type) {
Access.check(token); Access.check(token);
return invokers(type);
}
/*non-public*/ static Invokers invokers(MethodType type) {
Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type); Invokers inv = METHOD_TYPE_FRIEND.getInvokers(type);
if (inv != null) return inv; if (inv != null) return inv;
inv = new Invokers(token, type); inv = new Invokers(type);
METHOD_TYPE_FRIEND.setInvokers(type, inv); METHOD_TYPE_FRIEND.setInvokers(type, inv);
return inv; return inv;
} }
......
...@@ -23,15 +23,19 @@ ...@@ -23,15 +23,19 @@
/* @test /* @test
* @summary smoke test for invokedynamic instructions * @summary smoke test for invokedynamic instructions
* @library indify * @build indify.Indify
* @compile InvokeDynamicPrintArgs.java * @compile InvokeDynamicPrintArgs.java
* @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
* indify.Indify * indify.Indify
* --verify-specifier-count=3 --transitionalJSR292=false * --verify-specifier-count=3 --transitionalJSR292=false
* --expand-properties --classpath ${test.classes} * --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.util.*;
import java.io.*; import java.io.*;
...@@ -53,6 +57,20 @@ public class InvokeDynamicPrintArgs { ...@@ -53,6 +57,20 @@ public class InvokeDynamicPrintArgs {
closeBuf(); 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 PrintStream oldOut;
private static ByteArrayOutputStream buf; private static ByteArrayOutputStream buf;
private static void openBuf() { private static void openBuf() {
...@@ -79,11 +97,11 @@ public class InvokeDynamicPrintArgs { ...@@ -79,11 +97,11 @@ public class InvokeDynamicPrintArgs {
} }
private static final String[] EXPECT_OUTPUT = { private static final String[] EXPECT_OUTPUT = {
"Printing some argument lists, starting with a empty one:", "Printing some argument lists, starting with a empty one:",
"[InvokeDynamicPrintArgs, nothing, ()void][]", "[test.java.dyn.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]", "[test.java.dyn.InvokeDynamicPrintArgs, bar, (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]", "[test.java.dyn.InvokeDynamicPrintArgs, bar2, (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]", "[test.java.dyn.InvokeDynamicPrintArgs, baz, (String,int,double)void, 1234.5][baz arg, 2, 3.14]",
"[InvokeDynamicPrintArgs, foo, (java.lang.String)void][foo arg]", "[test.java.dyn.InvokeDynamicPrintArgs, foo, (String)void][foo arg]",
"Done printing argument lists." "Done printing argument lists."
}; };
...@@ -110,18 +128,15 @@ public class InvokeDynamicPrintArgs { ...@@ -110,18 +128,15 @@ public class InvokeDynamicPrintArgs {
return lookup().findStatic(lookup().lookupClass(), "bsm", MT_bsm()); 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: // ignore caller and name, but match the type:
List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type)); List<Object> bsmInfo = new ArrayList<>(Arrays.asList(caller, name, type));
if (arg instanceof Object[]) bsmInfo.addAll(Arrays.asList((Object[])arg));
bsmInfo.addAll(Arrays.asList((Object[])arg));
else
bsmInfo.add(arg);
return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type)); return new ConstantCallSite(MH_printArgs().bindTo(bsmInfo).asCollector(Object[].class, type.parameterCount()).asType(type));
} }
private static MethodType MT_bsm2() { private static MethodType MT_bsm2() {
shouldNotCallThis(); 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 { private static MethodHandle MH_bsm2() throws ReflectiveOperationException {
shouldNotCallThis(); shouldNotCallThis();
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -142,37 +142,80 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY ...@@ -142,37 +142,80 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
Assert.assertEquals(exp, act); Assert.assertEquals(exp, act);
} }
static MethodHandle asList; @Test public void testMethodHandlesSummary() throws Throwable {
@Test public void testWithTypeHandler() throws Throwable {
{{ {{
{} /// JAVADOC {} /// JAVADOC
MethodHandle makeEmptyList = MethodHandles.constant(List.class, Arrays.asList()); Object x, y; String s; int i;
MethodHandle asList = lookup() MethodType mt; MethodHandle mh;
.findStatic(Arrays.class, "asList", methodType(List.class, Object[].class)); MethodHandles.Lookup lookup = MethodHandles.lookup();
// mt is (char,char)String
JavaDocExamplesTest.asList = asList; mt = MethodType.methodType(String.class, char.class, char.class);
/* mh = lookup.findVirtual(String.class, "replace", mt);
static MethodHandle collectingTypeHandler(MethodHandle base, MethodType newType) { // (Ljava/lang/String;CC)Ljava/lang/String;
return asList.asCollector(Object[].class, newType.parameterCount()).asType(newType); s = (String) mh.invokeExact("daddy",'d','n');
} assert(s.equals("nanny"));
*/ // weakly typed invocation (using MHs.invoke)
s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
MethodHandle collectingTypeHandler = lookup() assert(s.equals("savvy"));
.findStatic(lookup().lookupClass(), "collectingTypeHandler", // mt is (Object[])List
methodType(MethodHandle.class, MethodHandle.class, MethodType.class)); mt = MethodType.methodType(java.util.List.class, Object[].class);
MethodHandle makeAnyList = makeEmptyList.withTypeHandler(collectingTypeHandler); mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
assert(mh.isVarargsCollector());
assertEquals("[]", makeAnyList.invokeGeneric().toString()); x = mh.invokeGeneric("one", "two");
assertEquals("[1]", makeAnyList.invokeGeneric(1).toString()); assert(x.equals(java.util.Arrays.asList("one","two")));
assertEquals("[two, too]", makeAnyList.invokeGeneric("two", "too").toString()); // 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 { =&gt; 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) { @Test public void testAsVarargsCollector() throws Throwable {
//System.out.println("Converting "+asList+" to "+newType); {{
MethodHandle conv = asList.asCollector(Object[].class, newType.parameterCount()).asType(newType); {} /// JAVADOC
//System.out.println(" =>"+conv); MethodHandle asList = publicLookup()
return conv; .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);
{}
}}
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -1108,18 +1108,6 @@ public class MethodHandlesTest { ...@@ -1108,18 +1108,6 @@ public class MethodHandlesTest {
} }
} }
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 @Test
public void testConvertArguments() throws Throwable { public void testConvertArguments() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -1137,23 +1125,6 @@ public class MethodHandlesTest { ...@@ -1137,23 +1125,6 @@ public class MethodHandlesTest {
testConvert(true, true, id, rtype, name, params); 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, void testConvert(boolean positive, boolean useAsType,
MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable {
countTest(positive); countTest(positive);
...@@ -1183,9 +1154,9 @@ public class MethodHandlesTest { ...@@ -1183,9 +1154,9 @@ public class MethodHandlesTest {
RuntimeException error = null; RuntimeException error = null;
try { try {
if (useAsType) if (useAsType)
target = MethodHandles.convertArguments(id, newType);
else
target = id.asType(newType); target = id.asType(newType);
else
target = MethodHandles.convertArguments(id, newType);
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
error = ex; error = ex;
} }
...@@ -1204,6 +1175,20 @@ public class MethodHandlesTest { ...@@ -1204,6 +1175,20 @@ public class MethodHandlesTest {
System.out.print(':'); System.out.print(':');
} }
@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 @Test
public void testPermuteArguments() throws Throwable { public void testPermuteArguments() throws Throwable {
if (CAN_SKIP_WORKING) return; if (CAN_SKIP_WORKING) return;
...@@ -1644,14 +1629,14 @@ public class MethodHandlesTest { ...@@ -1644,14 +1629,14 @@ public class MethodHandlesTest {
assertCalled("invokee", args); assertCalled("invokee", args);
// varargs invoker #0 // varargs invoker #0
calledLog.clear(); calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 0); inv = MethodHandles.spreadInvoker(type, 0);
result = inv.invokeExact(target, args); result = inv.invokeExact(target, args);
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
if (nargs >= 1) { if (nargs >= 1) {
// varargs invoker #1 // varargs invoker #1
calledLog.clear(); calledLog.clear();
inv = MethodHandles.varargsInvoker(type, 1); inv = MethodHandles.spreadInvoker(type, 1);
result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs)); result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs));
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
...@@ -1659,7 +1644,7 @@ public class MethodHandlesTest { ...@@ -1659,7 +1644,7 @@ public class MethodHandlesTest {
if (nargs >= 2) { if (nargs >= 2) {
// varargs invoker #2 // varargs invoker #2
calledLog.clear(); 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)); result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs));
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
...@@ -1667,7 +1652,7 @@ public class MethodHandlesTest { ...@@ -1667,7 +1652,7 @@ public class MethodHandlesTest {
if (nargs >= 3) { if (nargs >= 3) {
// varargs invoker #3 // varargs invoker #3
calledLog.clear(); 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)); result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs));
if (testRetCode) assertEquals(code, result); if (testRetCode) assertEquals(code, result);
assertCalled("invokee", args); assertCalled("invokee", args);
...@@ -1676,7 +1661,7 @@ public class MethodHandlesTest { ...@@ -1676,7 +1661,7 @@ public class MethodHandlesTest {
// varargs invoker #0..N // varargs invoker #0..N
countTest(); countTest();
calledLog.clear(); calledLog.clear();
inv = MethodHandles.varargsInvoker(type, k); inv = MethodHandles.spreadInvoker(type, k);
List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs); List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs);
List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs); List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs);
Object[] tail = tailList.toArray(); Object[] tail = tailList.toArray();
...@@ -2089,19 +2074,6 @@ public class MethodHandlesTest { ...@@ -2089,19 +2074,6 @@ public class MethodHandlesTest {
} }
} }
// Test error checking: // 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, for (Class<?> nonSAM : new Class[] { Object.class,
String.class, String.class,
CharSequence.class, CharSequence.class,
......
...@@ -98,8 +98,9 @@ $ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic ...@@ -98,8 +98,9 @@ $ $JAVA_HOME/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableInvokeDynamic
(same output as above) (same output as above)
* </pre></blockquote> * </pre></blockquote>
* <p> * <p>
* Until the format of {@code CONSTANT_InvokeDynamic} entries is finalized, * Before OpenJDK build b123, the format of {@code CONSTANT_InvokeDynamic} is in transition,
* the {@code --transitionalJSR292} switch is recommended (and turned on by default). * and the switch {@code --transitionalJSR292=yes} is recommended.
* It is turned <em>off</em> by default, but users of earlier builds may need to turn it on.
* <p> * <p>
* A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome. * A version of this transformation built on top of <a href="http://asm.ow2.org/">http://asm.ow2.org/</a> would be welcome.
* @author John Rose * @author John Rose
...@@ -116,7 +117,7 @@ public class Indify { ...@@ -116,7 +117,7 @@ public class Indify {
public boolean overwrite = false; public boolean overwrite = false;
public boolean quiet = false; public boolean quiet = false;
public boolean verbose = 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 boolean all = false;
public int verifySpecifierCount = -1; public int verifySpecifierCount = -1;
...@@ -158,6 +159,7 @@ public class Indify { ...@@ -158,6 +159,7 @@ public class Indify {
av = avl.toArray(new String[0]); av = avl.toArray(new String[0]);
Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader()); Class<?> mainClass = Class.forName(mainClassName, true, makeClassLoader());
java.lang.reflect.Method main = mainClass.getMethod("main", String[].class); java.lang.reflect.Method main = mainClass.getMethod("main", String[].class);
try { main.setAccessible(true); } catch (SecurityException ex) { }
main.invoke(null, (Object) av); main.invoke(null, (Object) av);
} }
...@@ -223,8 +225,8 @@ public class Indify { ...@@ -223,8 +225,8 @@ public class Indify {
private boolean booleanOption(String s) { private boolean booleanOption(String s) {
if (s == null) return true; if (s == null) return true;
switch (s) { switch (s) {
case "true": case "yes": case "1": return true; case "true": case "yes": case "on": case "1": return true;
case "false": case "no": case "0": return false; case "false": case "no": case "off": case "0": return false;
} }
throw new IllegalArgumentException("unrecognized boolean flag="+s); throw new IllegalArgumentException("unrecognized boolean flag="+s);
} }
...@@ -284,7 +286,7 @@ public class Indify { ...@@ -284,7 +286,7 @@ public class Indify {
} }
File classPathFile(File pathDir, String className) { File classPathFile(File pathDir, String className) {
String qualname = className+".class"; String qualname = className.replace('.','/')+".class";
qualname = qualname.replace('/', File.separatorChar); qualname = qualname.replace('/', File.separatorChar);
return new File(pathDir, qualname); return new File(pathDir, qualname);
} }
...@@ -339,6 +341,7 @@ public class Indify { ...@@ -339,6 +341,7 @@ public class Indify {
private File findClassInPath(String name) { private File findClassInPath(String name) {
for (String s : classpath) { for (String s : classpath) {
File f = classPathFile(new File(s), name); File f = classPathFile(new File(s), name);
//System.out.println("Checking for "+f);
if (f.exists() && f.canRead()) { if (f.exists() && f.canRead()) {
return f; return f;
} }
...@@ -353,11 +356,11 @@ public class Indify { ...@@ -353,11 +356,11 @@ public class Indify {
} }
} }
private Class<?> transformAndLoadClass(File f) throws ClassNotFoundException, IOException { 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); ClassFile cf = new ClassFile(f);
Logic logic = new Logic(cf); Logic logic = new Logic(cf);
boolean changed = logic.transform(); boolean changed = logic.transform();
if (verbose && !changed) System.out.println("(no change)"); if (verbose && !changed) System.err.println("(no change)");
logic.reportPatternMethods(!verbose, keepgoing); logic.reportPatternMethods(!verbose, keepgoing);
byte[] bytes = cf.toByteArray(); byte[] bytes = cf.toByteArray();
return defineClass(null, bytes, 0, bytes.length); return defineClass(null, bytes, 0, bytes.length);
...@@ -525,6 +528,7 @@ public class Indify { ...@@ -525,6 +528,7 @@ public class Indify {
throw new IllegalArgumentException("BootstrapMethods length is "+specsLen+" but should be "+verifySpecifierCount); 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 // mark constant pool entries according to participation in patterns
...@@ -696,6 +700,18 @@ public class Indify { ...@@ -696,6 +700,18 @@ public class Indify {
args.clear(); args.clear();
break; 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: case opc_getstatic:
{ {
// int.class compiles to getstatic Integer.TYPE // int.class compiles to getstatic Integer.TYPE
...@@ -732,8 +748,9 @@ public class Indify { ...@@ -732,8 +748,9 @@ public class Indify {
case opc_invokestatic: case opc_invokestatic:
case opc_invokevirtual: case opc_invokevirtual:
case opc_invokespecial:
{ {
boolean hasRecv = (bc == opc_invokevirtual); boolean hasRecv = (bc != opc_invokestatic);
int methi = i.u2At(1); int methi = i.u2At(1);
char mark = poolMarks[methi]; char mark = poolMarks[methi];
Short[] ref = pool.getMemberRef((short)methi); Short[] ref = pool.getMemberRef((short)methi);
...@@ -770,6 +787,7 @@ public class Indify { ...@@ -770,6 +787,7 @@ public class Indify {
if (mark == 'T' || mark == 'H' || mark == 'I') { if (mark == 'T' || mark == 'H' || mark == 'I') {
ownMethod = findMember(cf.methods, ref[1], ref[2]); ownMethod = findMember(cf.methods, ref[1], ref[2]);
} }
//if (intrinsic != null) System.out.println("intrinsic = "+intrinsic);
switch (intrinsic == null ? "" : intrinsic) { switch (intrinsic == null ? "" : intrinsic) {
case "fromMethodDescriptorString": case "fromMethodDescriptorString":
con = makeMethodTypeCon(args.get(0)); con = makeMethodTypeCon(args.get(0));
...@@ -860,6 +878,15 @@ public class Indify { ...@@ -860,6 +878,15 @@ public class Indify {
} }
} }
break decode; 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) { if (!hasRecv && ownMethod != null && patternMark != 0) {
con = constants.get(ownMethod); con = constants.get(ownMethod);
...@@ -1506,6 +1533,7 @@ public class Indify { ...@@ -1506,6 +1533,7 @@ public class Indify {
out.write(bytes); out.write(bytes);
} else { } else {
trueSize = flatten(out); 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) if (trueSize != size && size >= 0)
System.err.println("warning: attribute size changed "+size+" to "+trueSize); System.err.println("warning: attribute size changed "+size+" to "+trueSize);
...@@ -1525,7 +1553,7 @@ public class Indify { ...@@ -1525,7 +1553,7 @@ public class Indify {
} }
public List<Attr> attrs() { return null; } // Code overrides this public List<Attr> attrs() { return null; } // Code overrides this
public byte[] flatten() { public byte[] flatten() {
ByteArrayOutputStream buf = new ByteArrayOutputStream(size); ByteArrayOutputStream buf = new ByteArrayOutputStream(Math.max(20, size));
flatten(buf); flatten(buf);
return buf.toByteArray(); return buf.toByteArray();
} }
...@@ -1642,6 +1670,7 @@ public class Indify { ...@@ -1642,6 +1670,7 @@ public class Indify {
opc_invokestatic = 184, opc_invokestatic = 184,
opc_invokeinterface = 185, opc_invokeinterface = 185,
opc_invokedynamic = 186, opc_invokedynamic = 186,
opc_new = 187,
opc_anewarray = 189, opc_anewarray = 189,
opc_checkcast = 192, opc_checkcast = 192,
opc_ifnull = 198, opc_ifnull = 198,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册