提交 2b94dd73 编写于 作者: J jrose

8019417: JSR 292 javadoc should clarify method handle arity limits

Summary: clarification of erroneous reading of spec. that led to 7194534
Reviewed-by: twisti, darcy
上级 d28f1918
......@@ -392,7 +392,7 @@ mh.invokeExact(System.out, "Hello, world.");
* Java types.
* <ul>
* <li>Method types range over all possible arities,
* from no arguments to up to 255 of arguments (a limit imposed by the JVM).
* from no arguments to up to the <a href="MethodHandle.html#maxarity">maximum number</a> of allowed arguments.
* Generics are not variadic, and so cannot represent this.</li>
* <li>Method types can specify arguments of primitive types,
* which Java generic types cannot range over.</li>
......@@ -402,6 +402,22 @@ mh.invokeExact(System.out, "Hello, world.");
* genericity with a Java type parameter.</li>
* </ul>
*
* <h1><a name="maxarity"></a>Arity limits</h1>
* The JVM imposes on all methods and constructors of any kind an absolute
* limit of 255 stacked arguments. This limit can appear more restrictive
* in certain cases:
* <ul>
* <li>A {@code long} or {@code double} argument counts (for purposes of arity limits) as two argument slots.
* <li>A non-static method consumes an extra argument for the object on which the method is called.
* <li>A constructor consumes an extra argument for the object which is being constructed.
* <li>Since a method handle&rsquo;s {@code invoke} method (or other signature-polymorphic method) is non-virtual,
* it consumes an extra argument for the method handle itself, in addition to any non-virtual receiver object.
* </ul>
* These limits imply that certain method handles cannot be created, solely because of the JVM limit on stacked arguments.
* For example, if a static JVM method accepts exactly 255 arguments, a method handle cannot be created for it.
* Attempts to create method handles with impossible method types lead to an {@link IllegalArgumentException}.
* In particular, a method handle&rsquo;s type must not have an arity of the exact maximum 255.
*
* @see MethodType
* @see MethodHandles
* @author John Rose, JSR 292 EG
......@@ -831,10 +847,12 @@ assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray
* @return a new method handle which spreads its final array argument,
* before calling the original method handle
* @throws NullPointerException if {@code arrayType} is a null reference
* @throws IllegalArgumentException if {@code arrayType} is not an array type
* @throws IllegalArgumentException if target does not have at least
* @throws IllegalArgumentException if {@code arrayType} is not an array type,
* or if target does not have at least
* {@code arrayLength} parameter types,
* or if {@code arrayLength} is negative
* or if {@code arrayLength} is negative,
* or if the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
* @throws WrongMethodTypeException if the implied {@code asType} call fails
* @see #asCollector
*/
......@@ -947,7 +965,9 @@ assertEquals("[123]", (String) longsToString.invokeExact((long)123));
* @throws NullPointerException if {@code arrayType} is a null reference
* @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 arrayLength} is not a legal array size
* or {@code arrayLength} is not a legal array size,
* or the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
* @throws WrongMethodTypeException if the implied {@code asType} call fails
* @see #asSpreader
* @see #asVarargsCollector
......
......@@ -245,6 +245,10 @@ public class MethodHandles {
* on various grounds (<a href="#secmgr">see below</a>).
* By contrast, the {@code ldc} instruction is not subject to
* security manager checks.
* <li>If the looked-up method has a
* <a href="MethodHandle.html#maxarity">very large arity</a>,
* the method handle creation may fail, due to the method handle
* type having too many parameters.
* </ul>
*
* <h1><a name="access"></a>Access checking</h1>
......@@ -1522,7 +1526,9 @@ return invoker;
* @return a method handle suitable for invoking any method handle of the given type
* @throws NullPointerException if {@code type} is null
* @throws IllegalArgumentException if {@code leadingArgCount} is not in
* the range from 0 to {@code type.parameterCount()} inclusive
* the range from 0 to {@code type.parameterCount()} inclusive,
* or if the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
*/
static public
MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
......@@ -1565,6 +1571,8 @@ publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)
* This method throws no reflective or security exceptions.
* @param type the desired target type
* @return a method handle suitable for invoking any method handle of the given type
* @throws IllegalArgumentException if the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
*/
static public
MethodHandle exactInvoker(MethodType type) {
......@@ -1598,6 +1606,8 @@ publicLookup().findVirtual(MethodHandle.class, "invoke", type)
* This method throws no reflective or security exceptions.
* @param type the desired target type
* @return a method handle suitable for invoking any method handle convertible to the given type
* @throws IllegalArgumentException if the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
*/
static public
MethodHandle invoker(MethodType type) {
......@@ -1966,7 +1976,8 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
* or if the {@code valueTypes} array or any of its elements is null
* @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
* or if {@code pos} is negative or greater than the arity of the target,
* or if the new method handle's type would have too many parameters
* or if the new method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
......@@ -2034,7 +2045,9 @@ assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
* or if the {@code filters} array is null
* @throws IllegalArgumentException if a non-null element of {@code filters}
* does not match a corresponding argument type of target as described above,
* or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()}
* or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()},
* or if the resulting method handle's type would have
* <a href="MethodHandle.html#maxarity">too many parameters</a>
*/
public static
MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
......
......@@ -92,6 +92,65 @@ public class BigArityTest {
return args;
}
@Test
public void asCollectorIAE01() throws ReflectiveOperationException {
final int [] INVALID_ARRAY_LENGTHS = {
Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -2, -1, 255, 256, Integer.MAX_VALUE - 1, Integer.MAX_VALUE
};
MethodHandle target = MethodHandles.publicLookup().findStatic(Arrays.class,
"deepToString", MethodType.methodType(String.class, Object[].class));
int minbig = Integer.MAX_VALUE;
for (int invalidLength : INVALID_ARRAY_LENGTHS) {
if (minbig > invalidLength && invalidLength > 100) minbig = invalidLength;
try {
target.asCollector(Object[].class, invalidLength);
assert(false) : invalidLength;
} catch (IllegalArgumentException ex) {
System.out.println("OK: "+ex);
}
}
// Sizes not in the above array are good:
target.asCollector(Object[].class, minbig-1);
for (int i = 2; i <= 10; i++)
target.asCollector(Object[].class, minbig-i);
}
@Test
public void invoker02() {
for (int i = 0; i < 255; i++) {
MethodType mt = MethodType.genericMethodType(i);
MethodType expMT = mt.insertParameterTypes(0, MethodHandle.class);
if (i < 254) {
assertEquals(expMT, MethodHandles.invoker(mt).type());
} else {
try {
MethodHandles.invoker(mt);
assert(false) : i;
} catch (IllegalArgumentException ex) {
System.out.println("OK: "+ex);
}
}
}
}
@Test
public void exactInvoker02() {
for (int i = 0; i < 255; i++) {
MethodType mt = MethodType.genericMethodType(i);
MethodType expMT = mt.insertParameterTypes(0, MethodHandle.class);
if (i < 254) {
assertEquals(expMT, MethodHandles.exactInvoker(mt).type());
} else {
try {
MethodHandles.exactInvoker(mt);
assert(false) : i;
} catch (IllegalArgumentException ex) {
System.out.println("OK: "+ex);
}
}
}
}
@Test
public void testBoundaryValues() throws Throwable {
for (int badArity : new int[]{ -1, MAX_JVM_ARITY+1, MAX_JVM_ARITY }) {
......@@ -102,6 +161,37 @@ public class BigArityTest {
System.out.println("OK: "+ex);
}
}
final int MAX_MH_ARITY = MAX_JVM_ARITY - 1; // mh.invoke(arg*[N])
final int MAX_INVOKER_ARITY = MAX_MH_ARITY - 1; // inv.invoke(mh, arg*[N])
for (int arity : new int[]{ 0, 1, MAX_MH_ARITY-2, MAX_MH_ARITY-1, MAX_MH_ARITY }) {
MethodHandle mh = MH_hashArguments(arity);
if (arity < MAX_INVOKER_ARITY) {
MethodHandle ximh = MethodHandles.exactInvoker(mh.type());
MethodHandle gimh = MethodHandles.invoker(mh.type());
MethodHandle simh = MethodHandles.spreadInvoker(mh.type(), 0);
if (arity != 0) {
simh = MethodHandles.spreadInvoker(mh.type(), 1);
} else {
try {
simh = MethodHandles.spreadInvoker(mh.type(), 1);
assert(false) : arity;
} catch (IllegalArgumentException ex) {
System.out.println("OK: "+ex);
}
}
if (arity != 0) {
simh = MethodHandles.spreadInvoker(mh.type(), arity-1);
} else {
try {
simh = MethodHandles.spreadInvoker(mh.type(), arity-1);
assert(false) : arity;
} catch (IllegalArgumentException ex) {
System.out.println("OK: "+ex);
}
}
simh = MethodHandles.spreadInvoker(mh.type(), arity);
}
}
}
// Make sure the basic argument spreading and varargs mechanisms are working.
......@@ -133,7 +223,7 @@ public class BigArityTest {
if (cls == Object[].class)
r = smh.invokeExact(tail);
else if (cls == Integer[].class)
r = smh.invokeExact((Integer[]) tail);
r = smh.invokeExact((Integer[]) tail); //warning OK, see 8019340
else
r = smh.invoke(tail);
assertEquals(r0, r);
......@@ -235,21 +325,41 @@ public class BigArityTest {
MethodHandle mh_VA = mh.asSpreader(cls, arity);
assert(mh_VA.type().parameterType(0) == cls);
testArities(cls, arity, iterations, verbose, mh, mh_VA);
// mh_CA will collect arguments of a particular type and pass them to mh_VA
MethodHandle mh_CA = mh_VA.asCollector(cls, arity);
MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity);
assert(mh_CA.type().equals(mh.type()));
assert(mh_VA2.type().equals(mh_VA.type()));
if (cls != Object[].class) {
// mh_CA will collect arguments of a particular type and pass them to mh_VA
MethodHandle mh_CA = mh_VA.asCollector(cls, arity);
MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity);
try {
mh_VA2.invokeWithArguments(new Object[arity]);
throw new AssertionError("should not reach");
} catch (ClassCastException | WrongMethodTypeException ex) {
}
assert(mh_CA.type().equals(mh.type()));
assert(mh_VA2.type().equals(mh_VA.type()));
testArities(cls, arity, iterations, false, mh_CA, mh_VA2);
}
int iterations_VA = iterations / 100;
testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2);
}
}
/**
* Tests calls to {@link BigArityTest#hashArguments hashArguments} as related to a single given arity N.
* Applies the given {@code mh} to a set of N integer arguments, checking the answer.
* Also applies the varargs variation {@code mh_VA} to an array of type C[] (given by {@code cls}).
* Test steps:
* <ul>
* <li>mh_VA.invokeExact(new C[]{ arg, ... })</li>
* <li>mh.invokeWithArguments((Object[]) new C[]{ arg, ... })</li>
* <li>exactInvoker(mh.type()).invokeWithArguments(new Object[]{ mh, arg, ... })</li>
* <li>invoker(mh.type()).invokeWithArguments(new Object[]{ mh, arg, ... })</li>
* </ul>
* @param cls array type for varargs call (one of Object[], Number[], Integer[], Comparable[])
* @param arity N, the number of arguments to {@code mh} and length of its varargs array, in [0..255]
* @param iterations number of times to repeat each test step (at least 4)
* @param verbose are we printing extra output?
* @param mh a fixed-arity version of {@code hashArguments}
* @param mh_VA a variable-arity version of {@code hashArguments}, accepting the given array type {@code cls}
*/
private void testArities(Class<? extends Object[]> cls,
int arity,
int iterations,
......@@ -292,7 +402,7 @@ public class BigArityTest {
if (cls == Object[].class)
r = mh_VA.invokeExact(args);
else if (cls == Integer[].class)
r = mh_VA.invokeExact((Integer[])args);
r = mh_VA.invokeExact((Integer[])args); //warning OK, see 8019340
else
r = mh_VA.invoke(args);
assertEquals(r0, r);
......@@ -392,10 +502,16 @@ public class BigArityTest {
a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB]);
a[0xF8], a[0xF9], a[0xFA], a[0xFB]); // hashArguments_252
assertEquals(r0, r);
MethodType mt = MethodType.genericMethodType(ARITY);
MethodHandle mh = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY, mt);
test252(mh, a, r0);
MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
test252(mh_CA, a, r0);
}
public void test252(MethodHandle mh, Object[] a, Object r0) throws Throwable {
Object r;
r = mh.invokeExact(
// <editor-fold defaultstate="collapsed" desc="a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], ...">
a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F],
......@@ -599,10 +715,16 @@ public class BigArityTest {
a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC]);
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC]); // hashArguments_253
assertEquals(r0, r);
MethodType mt = MethodType.genericMethodType(ARITY);
MethodHandle mh = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY, mt);
test253(mh, a, r0);
MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
test253(mh_CA, a, r0);
}
public void test253(MethodHandle mh, Object[] a, Object r0) throws Throwable {
Object r;
r = mh.invokeExact(
// <editor-fold defaultstate="collapsed" desc="a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], ...">
a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F],
......@@ -648,7 +770,6 @@ public class BigArityTest {
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC]);
assertEquals(r0, r);
// FIXME: This next one fails, because it uses an internal invoker of arity 255.
r = ximh.invokeWithArguments(cat(mh,a));
assertEquals(r0, r);
MethodHandle gimh = MethodHandles.invoker(mh.type());
......@@ -674,7 +795,6 @@ public class BigArityTest {
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC]);
assertEquals(r0, r);
// FIXME: This next one fails, because it uses an internal invoker of arity 255.
r = gimh.invokeWithArguments(cat(mh,a));
assertEquals(r0, r);
mh = mh.asType(mh.type().changeParameterType(0x10, Integer.class));
......@@ -808,10 +928,16 @@ public class BigArityTest {
a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD]);
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD]); // hashArguments_254
assertEquals(r0, r);
MethodType mt = MethodType.genericMethodType(ARITY);
MethodHandle mh = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY, mt);
test254(mh, a, r0);
MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
test254(mh_CA, a, r0);
}
public void test254(MethodHandle mh, Object[] a, Object r0) throws Throwable {
Object r;
r = mh.invokeExact(
// <editor-fold defaultstate="collapsed" desc="a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], ...">
a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F],
......@@ -833,7 +959,6 @@ public class BigArityTest {
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD]);
assertEquals(r0, r);
// FIXME: This next one fails, because it uses an internal invoker of arity 255.
r = mh.invokeWithArguments(a);
assertEquals(r0, r);
try {
......@@ -998,7 +1123,7 @@ public class BigArityTest {
a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
// </editor-fold>
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]);
a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]); // hashArguments_255
assertEquals(r0, r);
MethodType mt = MethodType.genericMethodType(ARITY);
MethodHandle mh;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册