--- old/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2017-07-10 15:59:04.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java 2017-07-10 15:59:04.000000000 +0300 @@ -114,7 +114,10 @@ BoundMethodHandle bindArgumentD(int pos, double value) { return editor().bindArgumentD(this, pos, value); } - + /*non-public*/ + BoundMethodHandle bindArgumentQ(int pos, Object value, MethodHandle unbox) { + return editor().bindArgumentQ(this, pos, value, unbox); + } @Override BoundMethodHandle rebind() { if (!tooComplex()) { --- old/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java 2017-07-10 15:59:05.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java 2017-07-10 15:59:04.000000000 +0300 @@ -387,6 +387,14 @@ return oldSpeciesData().extendWith(type); } + BoundMethodHandle bindArgumentQ(BoundMethodHandle mh, int pos, Object value, MethodHandle unbox) { + assert(mh.speciesData() == oldSpeciesData()); + BasicType bt = Q_TYPE; + MethodType type2 = bindArgumentType(mh, pos, bt); + LambdaForm form2 = bindArgumentForm(1+pos); + return mh.copyWithExtendL(type2, form2, value) + .copyWithExtendL(type2, form2, unbox); + } BoundMethodHandle bindArgumentL(BoundMethodHandle mh, int pos, Object value) { assert(mh.speciesData() == oldSpeciesData()); BasicType bt = L_TYPE; @@ -447,7 +455,9 @@ buf.startEdit(); BoundMethodHandle.SpeciesData oldData = oldSpeciesData(); - BoundMethodHandle.SpeciesData newData = newSpeciesData(lambdaForm.parameterType(pos)); + BasicType ptype = lambdaForm.parameterType(pos); + BoundMethodHandle.SpeciesData newData = (ptype != Q_TYPE) ? newSpeciesData(ptype) + : oldData.extendWith(L_TYPE).extendWith(L_TYPE); // value + unbox MH Name oldBaseAddress = lambdaForm.parameter(0); // BMH holding the values Name newBaseAddress; NamedFunction getter = newData.getterFunction(oldData.fieldCount()); @@ -458,7 +468,22 @@ buf.replaceFunctions(oldData.getterFunctions(), newData.getterFunctions(), oldBaseAddress); newBaseAddress = oldBaseAddress.withConstraint(newData); buf.renameParameter(0, newBaseAddress); - buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); + if (ptype == Q_TYPE) { + // Q-types are stored in boxed form. Unboxing step is required. + int exprPos = lambdaForm.arity(); + int UNBOX_MH = exprPos++; + int BOXED_VALUE = exprPos++; + int UNBOXED_VALUE = exprPos++; + NamedFunction unboxGetter = newData.getterFunction(oldData.fieldCount()+1); + buf.insertExpression(UNBOX_MH, new Name(unboxGetter, newBaseAddress)); + buf.insertExpression(BOXED_VALUE, new Name(getter, newBaseAddress)); + MethodType unboxInvokerType = MethodType.methodType(Q_TYPE.basicTypeClass(), L_TYPE.basicTypeClass()); + Name unbox = new Name(unboxInvokerType, buf.name(UNBOX_MH), buf.name(BOXED_VALUE)); + buf.insertExpression(UNBOXED_VALUE, unbox); + buf.replaceParameterByCopy(pos, UNBOXED_VALUE); + } else { + buf.replaceParameterByNewExpression(pos, new Name(getter, newBaseAddress)); + } } else { // cannot bind the MH arg itself, unless oldData is empty assert(oldData == BoundMethodHandle.SpeciesData.EMPTY); --- old/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2017-07-10 15:59:05.000000000 +0300 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandles.java 2017-07-10 15:59:05.000000000 +0300 @@ -3486,6 +3486,12 @@ Class ptype = ptypes[pos+i]; if (ptype.isPrimitive()) { result = insertArgumentPrimitive(result, pos, ptype, value); + } else if (MinimalValueTypes_1_0.isValueType(ptype)) { + Class vcc = MinimalValueTypes_1_0.getValueCapableClass(ptype); + Objects.requireNonNull(value); // throw NPE if needed + value = vcc.cast(value); // throw CCE if needed + MethodHandle unbox = ValueType.forClass(vcc).unbox(); + result = result.bindArgumentQ(pos, value, unbox); } else { value = ptype.cast(value); // throw CCE if needed result = result.bindArgumentL(pos, value); --- /dev/null 2017-07-10 15:59:06.000000000 +0300 +++ new/test/valhalla/mvt/MethodHandlesTest.java 2017-07-10 15:59:05.000000000 +0300 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2017, 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. + * + * 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. + */ +import jdk.experimental.value.ValueType; +import org.testng.annotations.Test; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; + +import static java.lang.invoke.MethodType.methodType; +import static org.testng.Assert.*; + +/* + * @test + * @run testng/othervm -Xverify:none -XX:+EnableMVT -Dvalhalla.enablePoolPatches=true MethodHandlesTest + */ + +@Test +public class MethodHandlesTest { + @jvm.internal.value.DeriveValueType + final class ValueCapable {} + + static final ValueType VT = ValueType.forClass(Point.class); + static final Class VCC = Point.class; + static final Class DVT = VT.valueClass(); + + static final MethodHandle DVT_MH = MethodHandles.identity(DVT); // (DVT)DVT + static final MethodHandle VCC_MH = DVT_MH.asType(methodType(VCC, VCC)); // (VCC)VCC + + static final Object ARG = new Point(0, (short)1, (short)2); + + @Test + void testInsertArgumentDVT() throws Throwable { + { + MethodHandle mh = MethodHandles.insertArguments(DVT_MH, 0, ARG); + assertEquals(mh.invokeWithArguments(), ARG); + } + + assertThrows(ClassCastException.class, + () -> MethodHandles.insertArguments(DVT_MH, 0, new ValueCapable())); + + assertThrows(NullPointerException.class, + () -> MethodHandles.insertArguments(DVT_MH, 0, new Object[] { null })); + } + + @Test + void testInsertArgumentVCC() throws Throwable { + assertEquals(MethodHandles.insertArguments(VCC_MH, 0, ARG).invokeWithArguments(), ARG); + + assertThrows(ClassCastException.class, + () -> MethodHandles.insertArguments(VCC_MH, 0, new ValueCapable())); + + { + MethodHandle mh = MethodHandles.insertArguments(VCC_MH, 0, new Object[]{null}); + assertThrows(NullPointerException.class, () -> mh.invokeWithArguments()); + } + } +}