--- old/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2016-04-27 10:40:13.000000000 +0200 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java 2016-04-27 10:40:13.000000000 +0200 @@ -25,6 +25,7 @@ package java.lang.invoke; +import java.lang.reflect.Array; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; @@ -1892,7 +1893,8 @@ MH_tryFinallyExec = 12, MH_tryFinallyVoidExec = 13, MH_decrementCounter = 14, - MH_LIMIT = 15; + MH_Array_newInstance = 15, + MH_LIMIT = 16; static MethodHandle getConstantHandle(int idx) { MethodHandle handle = HANDLES[idx]; @@ -1965,6 +1967,9 @@ case MH_decrementCounter: return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "decrementCounter", MethodType.methodType(int.class, int.class)); + case MH_Array_newInstance: + return IMPL_LOOKUP.findStatic(Array.class, "newInstance", + MethodType.methodType(Object.class, Class.class, int.class)); } } catch (ReflectiveOperationException ex) { throw newInternalError(ex); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2016-04-27 10:40:15.000000000 +0200 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2016-04-27 10:40:15.000000000 +0200 @@ -1010,6 +1010,9 @@ * @throws NullPointerException if any argument is null */ public MethodHandle findConstructor(Class refc, MethodType type) throws NoSuchMethodException, IllegalAccessException { + if (refc.isArray()) { + throw new NoSuchMethodException("no constructor for array class: " + refc.getName()); + } String name = ""; MemberName ctor = resolveOrFail(REF_newInvokeSpecial, refc, name, type); return getDirectConstructor(refc, ctor); @@ -2221,6 +2224,26 @@ } /** + * Produces a method handle that can construct arrays of a desired type. + * The return type of the method handle will be the array type. + * The type of its sole argument will be {@code int}, which specifies the size of the array. + * @param arrayClass an array type + * @return a method handle which can create arrays of the given type + * @throws NullPointerException if the argument is {@code null} + * @throws IllegalArgumentException if {@code arrayClass} is not an array type + * @since 9 + */ + public static + MethodHandle arrayConstructor(Class arrayClass) throws IllegalArgumentException { + if (!arrayClass.isArray()) { + throw newIllegalArgumentException("not an array class: " + arrayClass.getName()); + } + MethodHandle ani = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_Array_newInstance). + bindTo(arrayClass.getComponentType()); + return ani.asType(ani.type().changeReturnType(arrayClass)); + } + + /** * 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, --- /dev/null 2016-04-27 10:40:16.000000000 +0200 +++ new/test/java/lang/invoke/ArrayConstructorTest.java 2016-04-27 10:40:16.000000000 +0200 @@ -0,0 +1,91 @@ +/* + * 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 + * @bug 8155106 + * @run testng/othervm -ea -esa test.java.lang.invoke.ArrayConstructorTest + */ +package test.java.lang.invoke; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import static java.lang.invoke.MethodType.methodType; + +import static org.testng.AssertJUnit.*; + +import org.testng.annotations.*; + + +public class ArrayConstructorTest { + + static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); + + @Test + public static void testFindConstructorArray() { + boolean caught = false; + try { + MethodHandle h = LOOKUP.findConstructor(Object[].class, methodType(void.class)); + } catch (NoSuchMethodException nsme) { + assertEquals("no constructor for array class: [Ljava.lang.Object;", nsme.getMessage()); + caught = true; + } catch (Exception e) { + throw new AssertionError("unexpected exception: " + e); + } + assertTrue(caught); + } + + @DataProvider + static Object[][] arrayConstructorNegative() { + return new Object[][]{ + {String.class, IllegalArgumentException.class, "not an array class: java.lang.String"}, + {null, NullPointerException.class, null} + }; + } + + @Test(dataProvider = "arrayConstructorNegative") + public static void testArrayConstructorNegative(Class clazz, Class exceptionClass, String message) { + boolean caught = false; + try { + MethodHandle h = MethodHandles.arrayConstructor(clazz); + } catch (Exception e) { + assertEquals(exceptionClass, e.getClass()); + if (message != null) { + assertEquals(message, e.getMessage()); + } + caught = true; + } + assertTrue(caught); + } + + @Test + public static void testArrayConstructor() throws Throwable { + MethodHandle h = MethodHandles.arrayConstructor(String[].class); + assertEquals(methodType(String[].class, int.class), h.type()); + String[] a = (String[]) h.invoke(17); + assertEquals(17, a.length); + } + +}