--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Mon May 16 11:42:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java Wed May 18 10:42:29 2016 +0200
@@ -706,6 +706,9 @@
case ARRAY_STORE:
emitArrayStore(name);
continue;
+ case ARRAY_LENGTH:
+ emitArrayLength(name);
+ continue;
case IDENTITY:
assert(name.arguments.length == 1);
emitPushArguments(name);
@@ -740,15 +743,16 @@
return classFile;
}
- void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); }
- void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
+ void emitArrayLoad(Name name) { emitArrayOp(name, Opcodes.AALOAD); }
+ void emitArrayStore(Name name) { emitArrayOp(name, Opcodes.AASTORE); }
+ void emitArrayLength(Name name) { emitArrayOp(name, Opcodes.ARRAYLENGTH); }
void emitArrayOp(Name name, int arrayOpcode) {
- assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE;
+ assert arrayOpcode == Opcodes.AALOAD || arrayOpcode == Opcodes.AASTORE || arrayOpcode == Opcodes.ARRAYLENGTH;
Class<?> elementType = name.function.methodType().parameterType(0).getComponentType();
assert elementType != null;
emitPushArguments(name);
- if (elementType.isPrimitive()) {
+ if (arrayOpcode != Opcodes.ARRAYLENGTH && elementType.isPrimitive()) {
Wrapper w = Wrapper.forPrimitiveType(elementType);
arrayOpcode = arrayInsnOpcode(arrayTypeCode(w), arrayOpcode);
}
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Mon May 16 11:42:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java Wed May 18 10:42:29 2016 +0200
@@ -66,25 +66,28 @@
/// Factory methods to create method handles:
- static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, boolean isSetter) {
- if (arrayClass == Object[].class)
- return (isSetter ? ArrayAccessor.OBJECT_ARRAY_SETTER : ArrayAccessor.OBJECT_ARRAY_GETTER);
+ static MethodHandle makeArrayElementAccessor(Class<?> arrayClass, ArrayAccess access) {
+ if (arrayClass == Object[].class) {
+ return ArrayAccess.objectAccessor(access);
+ }
if (!arrayClass.isArray())
throw newIllegalArgumentException("not an array: "+arrayClass);
MethodHandle[] cache = ArrayAccessor.TYPED_ACCESSORS.get(arrayClass);
- int cacheIndex = (isSetter ? ArrayAccessor.SETTER_INDEX : ArrayAccessor.GETTER_INDEX);
+ int cacheIndex = ArrayAccess.cacheIndex(access);
MethodHandle mh = cache[cacheIndex];
if (mh != null) return mh;
- mh = ArrayAccessor.getAccessor(arrayClass, isSetter);
- MethodType correctType = ArrayAccessor.correctType(arrayClass, isSetter);
+ mh = ArrayAccessor.getAccessor(arrayClass, access);
+ MethodType correctType = ArrayAccessor.correctType(arrayClass, access);
if (mh.type() != correctType) {
assert(mh.type().parameterType(0) == Object[].class);
- assert((isSetter ? mh.type().parameterType(2) : mh.type().returnType()) == Object.class);
- assert(isSetter || correctType.parameterType(0).getComponentType() == correctType.returnType());
+ /* if access == SET */ assert(access != ArrayAccess.SET || mh.type().parameterType(2) == Object.class);
+ /* if access == GET */ assert(access != ArrayAccess.GET ||
+ (mh.type().returnType() == Object.class &&
+ correctType.parameterType(0).getComponentType() == correctType.returnType()));
// safe to view non-strictly, because element type follows from array type
mh = mh.viewAsType(correctType, false);
}
- mh = makeIntrinsic(mh, (isSetter ? Intrinsic.ARRAY_STORE : Intrinsic.ARRAY_LOAD));
+ mh = makeIntrinsic(mh, ArrayAccess.intrinsic(access));
// Atomically update accessor cache.
synchronized(cache) {
if (cache[cacheIndex] == null) {
@@ -97,9 +100,52 @@
return mh;
}
+ enum ArrayAccess {
+ GET, SET, LENGTH;
+
+ // As ArrayAccess and ArrayAccessor have a circular dependency, the ArrayAccess properties cannot be stored in
+ // final fields.
+
+ static String opName(ArrayAccess a) {
+ switch (a) {
+ case GET: return "getElement";
+ case SET: return "setElement";
+ case LENGTH: return "length";
+ }
+ throw new AssertionError();
+ }
+
+ static MethodHandle objectAccessor(ArrayAccess a) {
+ switch (a) {
+ case GET: return ArrayAccessor.OBJECT_ARRAY_GETTER;
+ case SET: return ArrayAccessor.OBJECT_ARRAY_SETTER;
+ case LENGTH: return ArrayAccessor.OBJECT_ARRAY_LENGTH;
+ }
+ throw new AssertionError();
+ }
+
+ static int cacheIndex(ArrayAccess a) {
+ switch (a) {
+ case GET: return ArrayAccessor.GETTER_INDEX;
+ case SET: return ArrayAccessor.SETTER_INDEX;
+ case LENGTH: return ArrayAccessor.LENGTH_INDEX;
+ }
+ throw new AssertionError();
+ }
+
+ static Intrinsic intrinsic(ArrayAccess a) {
+ switch (a) {
+ case GET: return Intrinsic.ARRAY_LOAD;
+ case SET: return Intrinsic.ARRAY_STORE;
+ case LENGTH: return Intrinsic.ARRAY_LENGTH;
+ }
+ throw new AssertionError();
+ }
+ }
+
static final class ArrayAccessor {
- /// Support for array element access
- static final int GETTER_INDEX = 0, SETTER_INDEX = 1, INDEX_LIMIT = 2;
+ /// Support for array element and length access
+ static final int GETTER_INDEX = 0, SETTER_INDEX = 1, LENGTH_INDEX = 2, INDEX_LIMIT = 3;
static final ClassValue<MethodHandle[]> TYPED_ACCESSORS
= new ClassValue<MethodHandle[]>() {
@Override
@@ -107,14 +153,16 @@
return new MethodHandle[INDEX_LIMIT];
}
};
- static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER;
+ static final MethodHandle OBJECT_ARRAY_GETTER, OBJECT_ARRAY_SETTER, OBJECT_ARRAY_LENGTH;
static {
MethodHandle[] cache = TYPED_ACCESSORS.get(Object[].class);
- cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = makeIntrinsic(getAccessor(Object[].class, false), Intrinsic.ARRAY_LOAD);
- cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = makeIntrinsic(getAccessor(Object[].class, true), Intrinsic.ARRAY_STORE);
+ cache[GETTER_INDEX] = OBJECT_ARRAY_GETTER = makeIntrinsic(getAccessor(Object[].class, ArrayAccess.GET), Intrinsic.ARRAY_LOAD);
+ cache[SETTER_INDEX] = OBJECT_ARRAY_SETTER = makeIntrinsic(getAccessor(Object[].class, ArrayAccess.SET), Intrinsic.ARRAY_STORE);
+ cache[LENGTH_INDEX] = OBJECT_ARRAY_LENGTH = makeIntrinsic(getAccessor(Object[].class, ArrayAccess.LENGTH), Intrinsic.ARRAY_LENGTH);
assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_GETTER.internalMemberName()));
assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_SETTER.internalMemberName()));
+ assert(InvokerBytecodeGenerator.isStaticallyInvocable(ArrayAccessor.OBJECT_ARRAY_LENGTH.internalMemberName()));
}
static int getElementI(int[] a, int i) { return a[i]; }
@@ -137,31 +185,47 @@
static void setElementC(char[] a, int i, char x) { a[i] = x; }
static void setElementL(Object[] a, int i, Object x) { a[i] = x; }
- static String name(Class<?> arrayClass, boolean isSetter) {
+ static int lengthI(int[] a) { return a.length; }
+ static int lengthJ(long[] a) { return a.length; }
+ static int lengthF(float[] a) { return a.length; }
+ static int lengthD(double[] a) { return a.length; }
+ static int lengthZ(boolean[] a) { return a.length; }
+ static int lengthB(byte[] a) { return a.length; }
+ static int lengthS(short[] a) { return a.length; }
+ static int lengthC(char[] a) { return a.length; }
+ static int lengthL(Object[] a) { return a.length; }
+
+ static String name(Class<?> arrayClass, ArrayAccess access) {
Class<?> elemClass = arrayClass.getComponentType();
if (elemClass == null) throw newIllegalArgumentException("not an array", arrayClass);
- return (!isSetter ? "getElement" : "setElement") + Wrapper.basicTypeChar(elemClass);
+ return ArrayAccess.opName(access) + Wrapper.basicTypeChar(elemClass);
}
- static MethodType type(Class<?> arrayClass, boolean isSetter) {
+ static MethodType type(Class<?> arrayClass, ArrayAccess access) {
Class<?> elemClass = arrayClass.getComponentType();
Class<?> arrayArgClass = arrayClass;
if (!elemClass.isPrimitive()) {
arrayArgClass = Object[].class;
elemClass = Object.class;
}
- return !isSetter ?
- MethodType.methodType(elemClass, arrayArgClass, int.class) :
- MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
+ switch (access) {
+ case GET: return MethodType.methodType(elemClass, arrayArgClass, int.class);
+ case SET: return MethodType.methodType(void.class, arrayArgClass, int.class, elemClass);
+ case LENGTH: return MethodType.methodType(int.class, arrayArgClass);
+ }
+ throw new IllegalStateException("should not reach here");
}
- static MethodType correctType(Class<?> arrayClass, boolean isSetter) {
+ static MethodType correctType(Class<?> arrayClass, ArrayAccess access) {
Class<?> elemClass = arrayClass.getComponentType();
- return !isSetter ?
- MethodType.methodType(elemClass, arrayClass, int.class) :
- MethodType.methodType(void.class, arrayClass, int.class, elemClass);
+ switch (access) {
+ case GET: return MethodType.methodType(elemClass, arrayClass, int.class);
+ case SET: return MethodType.methodType(void.class, arrayClass, int.class, elemClass);
+ case LENGTH: return MethodType.methodType(int.class, arrayClass);
+ }
+ throw new IllegalStateException("should not reach here");
}
- static MethodHandle getAccessor(Class<?> arrayClass, boolean isSetter) {
- String name = name(arrayClass, isSetter);
- MethodType type = type(arrayClass, isSetter);
+ static MethodHandle getAccessor(Class<?> arrayClass, ArrayAccess access) {
+ String name = name(arrayClass, access);
+ MethodType type = type(arrayClass, access);
try {
return IMPL_LOOKUP.findStatic(ArrayAccessor.class, name, type);
} catch (ReflectiveOperationException ex) {
@@ -1282,6 +1346,7 @@
NEW_ARRAY,
ARRAY_LOAD,
ARRAY_STORE,
+ ARRAY_LENGTH,
IDENTITY,
ZERO,
NONE // no intrinsic associated
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Mon May 16 11:42:31 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed May 18 10:42:29 2016 +0200
@@ -2245,6 +2245,20 @@
}
/**
+ * Produces a method handle returning the length of an array.
+ * The type of the method handle will have {@code int} as return type,
+ * and its sole argument will be the array type.
+ * @param arrayClass an array type
+ * @return a method handle which can retrieve the length of an array of the given array type
+ * @throws NullPointerException if the argument is {@code null}
+ * @throws IllegalArgumentException if arrayClass is not an array type
+ */
+ public static
+ MethodHandle arrayLength(Class<?> arrayClass) throws IllegalArgumentException {
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.LENGTH);
+ }
+
+ /**
* Produces a method handle giving read access to elements of an array.
* The type of the method handle will have a return type of the array's
* element type. Its first argument will be the array type,
@@ -2256,7 +2270,7 @@
*/
public static
MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.makeArrayElementAccessor(arrayClass, false);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.GET);
}
/**
@@ -2271,7 +2285,7 @@
*/
public static
MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
- return MethodHandleImpl.makeArrayElementAccessor(arrayClass, true);
+ return MethodHandleImpl.makeArrayElementAccessor(arrayClass, MethodHandleImpl.ArrayAccess.SET);
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/ArrayLengthTest.java Wed May 18 10:42:29 2016 +0200
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run testng/othervm -ea -esa test.java.lang.invoke.ArrayLengthTest
+ */
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+public class ArrayLengthTest {
+
+ @DataProvider
+ Object[][] arrayClasses() {
+ return new Object[][] {
+ {int[].class},
+ {long[].class},
+ {float[].class},
+ {double[].class},
+ {boolean[].class},
+ {byte[].class},
+ {short[].class},
+ {char[].class},
+ {Object[].class},
+ {StringBuffer[].class}
+ };
+ }
+
+ @Test(dataProvider = "arrayClasses")
+ public void testArrayLength(Class<?> arrayClass) throws Throwable {
+ MethodHandle arrayLength = MethodHandles.arrayLength(arrayClass);
+ assertEquals(int.class, arrayLength.type().returnType());
+ assertEquals(arrayClass, arrayLength.type().parameterType(0));
+ Object array = MethodHandles.arrayConstructor(arrayClass).invoke(10);
+ assertEquals(10, arrayLength.invoke(array));
+ }
+
+ @Test(dataProvider = "arrayClasses", expectedExceptions = NullPointerException.class)
+ public void testArrayLengthInvokeNPE(Class<?> arrayClass) throws Throwable {
+ MethodHandle arrayLength = MethodHandles.arrayLength(arrayClass);
+ arrayLength.invoke(null);
+ }
+
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void testArrayLengthNoArray() {
+ MethodHandles.arrayLength(String.class);
+ }
+
+ @Test(expectedExceptions = NullPointerException.class)
+ public void testArrayLengthNPE() {
+ MethodHandles.arrayLength(null);
+ }
+
+}