| /* | |
| * Copyright (c) 2013, 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. | |
| */ | |
| /* @test | |
| * @bug 7087570 | |
| * @summary REF_invokeSpecial DMHs (which are unusual) get marked explicitly; tweak the MHI to use this bit | |
| * | |
| * @run main Test7087570 | |
| */ | |
| import java.lang.invoke.*; | |
| import java.lang.reflect.*; | |
| import java.util.*; | |
| import static java.lang.invoke.MethodHandles.*; | |
| import static java.lang.invoke.MethodType.*; | |
| import static java.lang.invoke.MethodHandleInfo.*; | |
| public class Test7087570 { | |
| private static final TestMethodData[] TESTS = new TestMethodData[] { | |
| // field accessors | |
| data(DummyFieldHolder.class, "instanceField", getterMethodType(String.class), DummyFieldHolder.class, REF_getField), | |
| data(DummyFieldHolder.class, "instanceField", setterMethodType(String.class), DummyFieldHolder.class, REF_putField), | |
| data(DummyFieldHolder.class, "staticField", getterMethodType(Integer.class), DummyFieldHolder.class, REF_getStatic), | |
| data(DummyFieldHolder.class, "staticField", setterMethodType(Integer.class), DummyFieldHolder.class, REF_putStatic), | |
| data(DummyFieldHolder.class, "instanceByteField", getterMethodType(byte.class), DummyFieldHolder.class, REF_getField), | |
| data(DummyFieldHolder.class, "instanceByteField", setterMethodType(byte.class), DummyFieldHolder.class, REF_putField), | |
| // REF_invokeVirtual | |
| data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeVirtual), | |
| // REF_invokeVirtual strength-reduced to REF_invokeSpecial, | |
| // test if it normalizes back to REF_invokeVirtual in MethodHandleInfo as expected | |
| data(String.class, "hashCode", methodType(int.class), String.class, REF_invokeVirtual), | |
| // REF_invokeStatic | |
| data(Collections.class, "sort", methodType(void.class, List.class), Collections.class, REF_invokeStatic), | |
| data(Arrays.class, "asList", methodType(List.class, Object[].class), Arrays.class, REF_invokeStatic), // varargs case | |
| // REF_invokeSpecial | |
| data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeSpecial), | |
| // REF_newInvokeSpecial | |
| data(String.class, "<init>", methodType(void.class, char[].class), String.class, REF_newInvokeSpecial), | |
| data(DummyFieldHolder.class, "<init>", methodType(void.class, byte.class, Long[].class), DummyFieldHolder.class, REF_newInvokeSpecial), // varargs case | |
| // REF_invokeInterface | |
| data(List.class, "size", methodType(int.class), List.class, REF_invokeInterface) | |
| }; | |
| public static void main(String... args) throws Throwable { | |
| testWithLookup(); | |
| testWithUnreflect(); | |
| } | |
| private static void doTest(MethodHandle mh, TestMethodData testMethod) { | |
| MethodHandleInfo mhi = LOOKUP.revealDirect(mh); | |
| System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n", | |
| testMethod.clazz.getName(), testMethod.name, testMethod.methodType, | |
| referenceKindToString(testMethod.referenceKind), | |
| referenceKindToString(mhi.getReferenceKind())); | |
| assertEquals(testMethod.name, mhi.getName()); | |
| assertEquals(testMethod.methodType, mhi.getMethodType()); | |
| assertEquals(testMethod.declaringClass, mhi.getDeclaringClass()); | |
| assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh)); | |
| assertRefKindEquals(testMethod.referenceKind, mhi.getReferenceKind()); | |
| } | |
| private static void testWithLookup() throws Throwable { | |
| for (TestMethodData testMethod : TESTS) { | |
| MethodHandle mh = lookupFrom(testMethod); | |
| doTest(mh, testMethod); | |
| } | |
| } | |
| private static void testWithUnreflect() throws Throwable { | |
| for (TestMethodData testMethod : TESTS) { | |
| MethodHandle mh = unreflectFrom(testMethod); | |
| doTest(mh, testMethod); | |
| } | |
| } | |
| private static MethodType getterMethodType(Class<?> clazz) { | |
| return methodType(clazz); | |
| } | |
| private static MethodType setterMethodType(Class<?> clazz) { | |
| return methodType(void.class, clazz); | |
| } | |
| private static final Lookup LOOKUP = lookup(); | |
| private static class TestMethodData { | |
| final Class<?> clazz; | |
| final String name; | |
| final MethodType methodType; | |
| final Class<?> declaringClass; | |
| final int referenceKind; // the nominal refKind | |
| public TestMethodData(Class<?> clazz, String name, | |
| MethodType methodType, Class<?> declaringClass, | |
| int referenceKind) { | |
| this.clazz = clazz; | |
| this.name = name; | |
| this.methodType = methodType; | |
| this.declaringClass = declaringClass; | |
| this.referenceKind = referenceKind; | |
| } | |
| } | |
| private static TestMethodData data(Class<?> clazz, String name, | |
| MethodType methodType, Class<?> declaringClass, | |
| int referenceKind) { | |
| return new TestMethodData(clazz, name, methodType, declaringClass, referenceKind); | |
| } | |
| private static MethodHandle lookupFrom(TestMethodData testMethod) | |
| throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException { | |
| switch (testMethod.referenceKind) { | |
| case REF_getField: | |
| return LOOKUP.findGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType()); | |
| case REF_putField: | |
| return LOOKUP.findSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0)); | |
| case REF_getStatic: | |
| return LOOKUP.findStaticGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType()); | |
| case REF_putStatic: | |
| return LOOKUP.findStaticSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0)); | |
| case REF_invokeVirtual: | |
| case REF_invokeInterface: | |
| return LOOKUP.findVirtual(testMethod.clazz, testMethod.name, testMethod.methodType); | |
| case REF_invokeStatic: | |
| return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType); | |
| case REF_invokeSpecial: | |
| Class<?> thisClass = LOOKUP.lookupClass(); | |
| MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass); | |
| noteInvokeSpecial(smh); | |
| return smh; | |
| case REF_newInvokeSpecial: | |
| return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType); | |
| default: | |
| throw new Error("ERROR: unexpected referenceKind in test data"); | |
| } | |
| } | |
| private static MethodHandle unreflectFrom(TestMethodData testMethod) | |
| throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException { | |
| switch (testMethod.referenceKind) { | |
| case REF_getField: | |
| case REF_getStatic: { | |
| Field f = testMethod.clazz.getDeclaredField(testMethod.name); | |
| return LOOKUP.unreflectGetter(f); | |
| } | |
| case REF_putField: | |
| case REF_putStatic: { | |
| Field f = testMethod.clazz.getDeclaredField(testMethod.name); | |
| return LOOKUP.unreflectSetter(f); | |
| } | |
| case REF_invokeVirtual: | |
| case REF_invokeStatic: | |
| case REF_invokeInterface: { | |
| Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray()); | |
| return LOOKUP.unreflect(m); | |
| } | |
| case REF_invokeSpecial: { | |
| Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray()); | |
| Class<?> thisClass = LOOKUP.lookupClass(); | |
| MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass); | |
| noteInvokeSpecial(smh); | |
| return smh; | |
| } | |
| case REF_newInvokeSpecial: { | |
| Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray()); | |
| return LOOKUP.unreflectConstructor(c); | |
| } | |
| default: | |
| throw new Error("ERROR: unexpected referenceKind in test data"); | |
| } | |
| } | |
| private static List<MethodHandle> specialMethodHandles = new ArrayList<>(); | |
| private static void noteInvokeSpecial(MethodHandle mh) { | |
| specialMethodHandles.add(mh); | |
| assert(isInvokeSpecial(mh)); | |
| } | |
| private static boolean isInvokeSpecial(MethodHandle mh) { | |
| return specialMethodHandles.contains(mh); | |
| } | |
| private static void assertRefKindEquals(int expect, int observed) { | |
| if (expect == observed) return; | |
| String msg = "expected " + referenceKindToString(expect) + | |
| " but observed " + referenceKindToString(observed); | |
| System.out.println("FAILED: " + msg); | |
| throw new AssertionError(msg); | |
| } | |
| private static void assertEquals(Object expect, Object observed) { | |
| if (java.util.Objects.equals(expect, observed)) return; | |
| String msg = "expected " + expect + " but observed " + observed; | |
| System.out.println("FAILED: " + msg); | |
| throw new AssertionError(msg); | |
| } | |
| } | |
| class DummyFieldHolder { | |
| public static Integer staticField; | |
| public String instanceField; | |
| public byte instanceByteField; | |
| public DummyFieldHolder(byte unused1, Long... unused2) { | |
| } | |
| } | |