1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/* @test
25 * @bug 7087570
26 * @summary REF_invokeSpecial DMHs (which are unusual) get marked explicitly; tweak the MHI to use this bit
27 *
28 * @run main Test7087570
29 */
30
31import java.lang.invoke.*;
32import java.lang.reflect.*;
33import java.util.*;
34
35import static java.lang.invoke.MethodHandles.*;
36import static java.lang.invoke.MethodType.*;
37import static java.lang.invoke.MethodHandleInfo.*;
38
39public class Test7087570 {
40
41    private static final TestMethodData[] TESTS = new TestMethodData[] {
42        // field accessors
43        data(DummyFieldHolder.class, "instanceField", getterMethodType(String.class), DummyFieldHolder.class, REF_getField),
44        data(DummyFieldHolder.class, "instanceField", setterMethodType(String.class), DummyFieldHolder.class, REF_putField),
45        data(DummyFieldHolder.class, "staticField", getterMethodType(Integer.class), DummyFieldHolder.class, REF_getStatic),
46        data(DummyFieldHolder.class, "staticField", setterMethodType(Integer.class), DummyFieldHolder.class, REF_putStatic),
47        data(DummyFieldHolder.class, "instanceByteField", getterMethodType(byte.class), DummyFieldHolder.class, REF_getField),
48        data(DummyFieldHolder.class, "instanceByteField", setterMethodType(byte.class), DummyFieldHolder.class, REF_putField),
49
50        // REF_invokeVirtual
51        data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeVirtual),
52
53        // REF_invokeVirtual strength-reduced to REF_invokeSpecial,
54        // test if it normalizes back to REF_invokeVirtual in MethodHandleInfo as expected
55        data(String.class, "hashCode", methodType(int.class), String.class, REF_invokeVirtual),
56
57        // REF_invokeStatic
58        data(Collections.class, "sort", methodType(void.class, List.class), Collections.class, REF_invokeStatic),
59        data(Arrays.class, "asList", methodType(List.class, Object[].class), Arrays.class, REF_invokeStatic), // varargs case
60
61        // REF_invokeSpecial
62        data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeSpecial),
63
64        // REF_newInvokeSpecial
65        data(String.class, "<init>", methodType(void.class, char[].class), String.class, REF_newInvokeSpecial),
66        data(DummyFieldHolder.class, "<init>", methodType(void.class, byte.class, Long[].class), DummyFieldHolder.class, REF_newInvokeSpecial), // varargs case
67
68        // REF_invokeInterface
69        data(List.class, "size", methodType(int.class), List.class, REF_invokeInterface)
70    };
71
72    public static void main(String... args) throws Throwable {
73        testWithLookup();
74        testWithUnreflect();
75    }
76
77    private static void doTest(MethodHandle mh, TestMethodData testMethod) {
78        MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
79
80        System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
81                          testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
82                          referenceKindToString(testMethod.referenceKind),
83                          referenceKindToString(mhi.getReferenceKind()));
84        assertEquals(testMethod.name,           mhi.getName());
85        assertEquals(testMethod.methodType,     mhi.getMethodType());
86        assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
87        assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
88        assertRefKindEquals(testMethod.referenceKind,  mhi.getReferenceKind());
89    }
90
91    private static void testWithLookup() throws Throwable {
92        for (TestMethodData testMethod : TESTS) {
93            MethodHandle mh = lookupFrom(testMethod);
94            doTest(mh, testMethod);
95        }
96    }
97
98    private static void testWithUnreflect() throws Throwable {
99        for (TestMethodData testMethod : TESTS) {
100            MethodHandle mh = unreflectFrom(testMethod);
101            doTest(mh, testMethod);
102        }
103    }
104
105    private static MethodType getterMethodType(Class<?> clazz) {
106        return methodType(clazz);
107    }
108
109    private static MethodType setterMethodType(Class<?> clazz) {
110        return methodType(void.class, clazz);
111    }
112
113    private static final Lookup LOOKUP = lookup();
114
115    private static class TestMethodData {
116        final Class<?> clazz;
117        final String name;
118        final MethodType methodType;
119        final Class<?> declaringClass;
120        final int referenceKind; // the nominal refKind
121
122        public TestMethodData(Class<?> clazz, String name,
123                        MethodType methodType, Class<?> declaringClass,
124                        int referenceKind) {
125            this.clazz = clazz;
126            this.name = name;
127            this.methodType = methodType;
128            this.declaringClass = declaringClass;
129            this.referenceKind = referenceKind;
130        }
131    }
132
133    private static TestMethodData data(Class<?> clazz, String name,
134                                       MethodType methodType, Class<?> declaringClass,
135                                       int referenceKind) {
136        return new TestMethodData(clazz, name, methodType, declaringClass, referenceKind);
137    }
138
139    private static MethodHandle lookupFrom(TestMethodData testMethod)
140            throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
141        switch (testMethod.referenceKind) {
142        case REF_getField:
143            return LOOKUP.findGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
144        case REF_putField:
145            return LOOKUP.findSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
146        case REF_getStatic:
147            return LOOKUP.findStaticGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
148        case REF_putStatic:
149            return LOOKUP.findStaticSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
150        case REF_invokeVirtual:
151        case REF_invokeInterface:
152            return LOOKUP.findVirtual(testMethod.clazz, testMethod.name, testMethod.methodType);
153        case REF_invokeStatic:
154            return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
155        case REF_invokeSpecial:
156            Class<?> thisClass = LOOKUP.lookupClass();
157            MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
158            noteInvokeSpecial(smh);
159            return smh;
160        case REF_newInvokeSpecial:
161            return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
162        default:
163            throw new Error("ERROR: unexpected referenceKind in test data");
164        }
165    }
166
167    private static MethodHandle unreflectFrom(TestMethodData testMethod)
168            throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
169        switch (testMethod.referenceKind) {
170        case REF_getField:
171        case REF_getStatic: {
172                Field f = testMethod.clazz.getDeclaredField(testMethod.name);
173                return LOOKUP.unreflectGetter(f);
174            }
175        case REF_putField:
176        case REF_putStatic: {
177                Field f = testMethod.clazz.getDeclaredField(testMethod.name);
178                return LOOKUP.unreflectSetter(f);
179            }
180        case REF_invokeVirtual:
181        case REF_invokeStatic:
182        case REF_invokeInterface: {
183                Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
184                return LOOKUP.unreflect(m);
185            }
186        case REF_invokeSpecial: {
187                Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
188                Class<?> thisClass = LOOKUP.lookupClass();
189                MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
190                noteInvokeSpecial(smh);
191                return smh;
192            }
193        case REF_newInvokeSpecial: {
194                Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
195                return LOOKUP.unreflectConstructor(c);
196            }
197        default:
198            throw new Error("ERROR: unexpected referenceKind in test data");
199        }
200    }
201
202    private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
203    private static void noteInvokeSpecial(MethodHandle mh) {
204        specialMethodHandles.add(mh);
205        assert(isInvokeSpecial(mh));
206    }
207    private static boolean isInvokeSpecial(MethodHandle mh) {
208        return specialMethodHandles.contains(mh);
209    }
210
211    private static void assertRefKindEquals(int expect, int observed) {
212        if (expect == observed) return;
213
214        String msg = "expected " + referenceKindToString(expect) +
215                     " but observed " + referenceKindToString(observed);
216        System.out.println("FAILED: " + msg);
217        throw new AssertionError(msg);
218    }
219
220    private static void assertEquals(Object expect, Object observed) {
221        if (java.util.Objects.equals(expect, observed)) return;
222
223        String msg = "expected " + expect + " but observed " + observed;
224        System.out.println("FAILED: " + msg);
225        throw new AssertionError(msg);
226    }
227}
228
229class DummyFieldHolder {
230    public static Integer staticField;
231    public String instanceField;
232    public byte instanceByteField;
233
234    public DummyFieldHolder(byte unused1, Long... unused2) {
235    }
236}
237
238