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 
  25 /* @test
  26  * @bug 7087570
  27  * @summary REF_invokeSpecial DMHs (which are unusual) get marked explicitly; tweak the MHI to use this bit
  28  *
  29  * @run main Test7087570
  30  */
  31 
  32 import java.lang.invoke.*;
  33 import java.lang.reflect.*;
  34 import java.util.*;
  35 
  36 import static java.lang.invoke.MethodHandles.*;
  37 import static java.lang.invoke.MethodType.*;
  38 import static java.lang.invoke.MethodHandleInfo.*;
  39 
  40 public class Test7087570 {
  41 
  42     private static final TestMethodData[] TESTS = new TestMethodData[] {
  43         // field accessors
  44         data(DummyFieldHolder.class, "instanceField", getterMethodType(String.class), DummyFieldHolder.class, REF_getField),
  45         data(DummyFieldHolder.class, "instanceField", setterMethodType(String.class), DummyFieldHolder.class, REF_putField),
  46         data(DummyFieldHolder.class, "staticField", getterMethodType(Integer.class), DummyFieldHolder.class, REF_getStatic),
  47         data(DummyFieldHolder.class, "staticField", setterMethodType(Integer.class), DummyFieldHolder.class, REF_putStatic),
  48         data(DummyFieldHolder.class, "instanceByteField", getterMethodType(byte.class), DummyFieldHolder.class, REF_getField),
  49         data(DummyFieldHolder.class, "instanceByteField", setterMethodType(byte.class), DummyFieldHolder.class, REF_putField),
  50 
  51         // REF_invokeVirtual
  52         data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeVirtual),
  53 
  54         // REF_invokeVirtual strength-reduced to REF_invokeSpecial,
  55         // test if it normalizes back to REF_invokeVirtual in MethodHandleInfo as expected
  56         data(String.class, "hashCode", methodType(int.class), String.class, REF_invokeVirtual),
  57 
  58         // REF_invokeStatic
  59         data(Collections.class, "sort", methodType(void.class, List.class), Collections.class, REF_invokeStatic),
  60         data(Arrays.class, "asList", methodType(List.class, Object[].class), Arrays.class, REF_invokeStatic), // varargs case
  61 
  62         // REF_invokeSpecial
  63         data(Object.class, "hashCode", methodType(int.class), Object.class, REF_invokeSpecial),
  64 
  65         // REF_newInvokeSpecial
  66         data(String.class, "<init>", methodType(void.class, char[].class), String.class, REF_newInvokeSpecial),
  67         data(DummyFieldHolder.class, "<init>", methodType(void.class, byte.class, Long[].class), DummyFieldHolder.class, REF_newInvokeSpecial), // varargs case
  68 
  69         // REF_invokeInterface
  70         data(List.class, "size", methodType(int.class), List.class, REF_invokeInterface)
  71     };
  72 
  73     public static void main(String... args) throws Throwable {
  74         testWithLookup();
  75         testWithUnreflect();
  76     }
  77 
  78     private static void doTest(MethodHandle mh, TestMethodData testMethod) {
  79         MethodHandleInfo mhi = LOOKUP.revealDirect(mh);
  80 
  81         System.out.printf("%s.%s: %s, nominal refKind: %s, actual refKind: %s\n",
  82                           testMethod.clazz.getName(), testMethod.name, testMethod.methodType,
  83                           referenceKindToString(testMethod.referenceKind),
  84                           referenceKindToString(mhi.getReferenceKind()));
  85         assertEquals(testMethod.name,           mhi.getName());
  86         assertEquals(testMethod.methodType,     mhi.getMethodType());
  87         assertEquals(testMethod.declaringClass, mhi.getDeclaringClass());
  88         assertEquals(testMethod.referenceKind == REF_invokeSpecial, isInvokeSpecial(mh));
  89         assertRefKindEquals(testMethod.referenceKind,  mhi.getReferenceKind());
  90     }
  91 
  92     private static void testWithLookup() throws Throwable {
  93         for (TestMethodData testMethod : TESTS) {
  94             MethodHandle mh = lookupFrom(testMethod);
  95             doTest(mh, testMethod);
  96         }
  97     }
  98 
  99     private static void testWithUnreflect() throws Throwable {
 100         for (TestMethodData testMethod : TESTS) {
 101             MethodHandle mh = unreflectFrom(testMethod);
 102             doTest(mh, testMethod);
 103         }
 104     }
 105 
 106     private static MethodType getterMethodType(Class<?> clazz) {
 107         return methodType(clazz);
 108     }
 109 
 110     private static MethodType setterMethodType(Class<?> clazz) {
 111         return methodType(void.class, clazz);
 112     }
 113 
 114     private static final Lookup LOOKUP = lookup();
 115 
 116     private static class TestMethodData {
 117         final Class<?> clazz;
 118         final String name;
 119         final MethodType methodType;
 120         final Class<?> declaringClass;
 121         final int referenceKind; // the nominal refKind
 122 
 123         public TestMethodData(Class<?> clazz, String name,
 124                         MethodType methodType, Class<?> declaringClass,
 125                         int referenceKind) {
 126             this.clazz = clazz;
 127             this.name = name;
 128             this.methodType = methodType;
 129             this.declaringClass = declaringClass;
 130             this.referenceKind = referenceKind;
 131         }
 132     }
 133 
 134     private static TestMethodData data(Class<?> clazz, String name,
 135                                        MethodType methodType, Class<?> declaringClass,
 136                                        int referenceKind) {
 137         return new TestMethodData(clazz, name, methodType, declaringClass, referenceKind);
 138     }
 139 
 140     private static MethodHandle lookupFrom(TestMethodData testMethod)
 141             throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
 142         switch (testMethod.referenceKind) {
 143         case REF_getField:
 144             return LOOKUP.findGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
 145         case REF_putField:
 146             return LOOKUP.findSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
 147         case REF_getStatic:
 148             return LOOKUP.findStaticGetter(testMethod.clazz, testMethod.name, testMethod.methodType.returnType());
 149         case REF_putStatic:
 150             return LOOKUP.findStaticSetter(testMethod.clazz, testMethod.name, testMethod.methodType.parameterType(0));
 151         case REF_invokeVirtual:
 152         case REF_invokeInterface:
 153             return LOOKUP.findVirtual(testMethod.clazz, testMethod.name, testMethod.methodType);
 154         case REF_invokeStatic:
 155             return LOOKUP.findStatic(testMethod.clazz, testMethod.name, testMethod.methodType);
 156         case REF_invokeSpecial:
 157             Class<?> thisClass = LOOKUP.lookupClass();
 158             MethodHandle smh = LOOKUP.findSpecial(testMethod.clazz, testMethod.name, testMethod.methodType, thisClass);
 159             noteInvokeSpecial(smh);
 160             return smh;
 161         case REF_newInvokeSpecial:
 162             return LOOKUP.findConstructor(testMethod.clazz, testMethod.methodType);
 163         default:
 164             throw new Error("ERROR: unexpected referenceKind in test data");
 165         }
 166     }
 167 
 168     private static MethodHandle unreflectFrom(TestMethodData testMethod)
 169             throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
 170         switch (testMethod.referenceKind) {
 171         case REF_getField:
 172         case REF_getStatic: {
 173                 Field f = testMethod.clazz.getDeclaredField(testMethod.name);
 174                 return LOOKUP.unreflectGetter(f);
 175             }
 176         case REF_putField:
 177         case REF_putStatic: {
 178                 Field f = testMethod.clazz.getDeclaredField(testMethod.name);
 179                 return LOOKUP.unreflectSetter(f);
 180             }
 181         case REF_invokeVirtual:
 182         case REF_invokeStatic:
 183         case REF_invokeInterface: {
 184                 Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
 185                 return LOOKUP.unreflect(m);
 186             }
 187         case REF_invokeSpecial: {
 188                 Method m = testMethod.clazz.getDeclaredMethod(testMethod.name, testMethod.methodType.parameterArray());
 189                 Class<?> thisClass = LOOKUP.lookupClass();
 190                 MethodHandle smh = LOOKUP.unreflectSpecial(m, thisClass);
 191                 noteInvokeSpecial(smh);
 192                 return smh;
 193             }
 194         case REF_newInvokeSpecial: {
 195                 Constructor c = testMethod.clazz.getDeclaredConstructor(testMethod.methodType.parameterArray());
 196                 return LOOKUP.unreflectConstructor(c);
 197             }
 198         default:
 199             throw new Error("ERROR: unexpected referenceKind in test data");
 200         }
 201     }
 202 
 203     private static List<MethodHandle> specialMethodHandles = new ArrayList<>();
 204     private static void noteInvokeSpecial(MethodHandle mh) {
 205         specialMethodHandles.add(mh);
 206         assert(isInvokeSpecial(mh));
 207     }
 208     private static boolean isInvokeSpecial(MethodHandle mh) {
 209         return specialMethodHandles.contains(mh);
 210     }
 211 
 212     private static void assertRefKindEquals(int expect, int observed) {
 213         if (expect == observed) return;
 214 
 215         String msg = "expected " + referenceKindToString(expect) +
 216                      " but observed " + referenceKindToString(observed);
 217         System.out.println("FAILED: " + msg);
 218         throw new AssertionError(msg);
 219     }
 220 
 221     private static void assertEquals(Object expect, Object observed) {
 222         if (java.util.Objects.equals(expect, observed)) return;
 223 
 224         String msg = "expected " + expect + " but observed " + observed;
 225         System.out.println("FAILED: " + msg);
 226         throw new AssertionError(msg);
 227     }
 228 }
 229 
 230 class DummyFieldHolder {
 231     public static Integer staticField;
 232     public String instanceField;
 233     public byte instanceByteField;
 234 
 235     public DummyFieldHolder(byte unused1, Long... unused2) {
 236     }
 237 }
 238