1 /*
   2  * Copyright (c) 2011, 2012, 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  * @summary unit tests for method handles which permute their arguments
  26  * @run testng test.java.lang.invoke.ThrowExceptionsTest
  27  */
  28 
  29 package test.java.lang.invoke;
  30 
  31 import org.testng.*;
  32 import org.testng.annotations.*;
  33 
  34 import java.util.*;
  35 import java.lang.reflect.*;
  36 
  37 import java.lang.invoke.*;
  38 import static java.lang.invoke.MethodHandles.*;
  39 import static java.lang.invoke.MethodType.*;
  40 
  41 public class ThrowExceptionsTest {
  42     private static final Class<?> CLASS = ThrowExceptionsTest.class;
  43     private static final Lookup LOOKUP = lookup();
  44 
  45     public static void main(String argv[]) throws Throwable {
  46         new ThrowExceptionsTest().testAll((argv.length == 0 ? null : Arrays.asList(argv).toString()));
  47     }
  48 
  49     @Test
  50     public void testWMT() throws Throwable {
  51         // mostly call testWMTCallee, but sometimes call its void-returning variant
  52         MethodHandle mh = testWMTCallee();
  53         MethodHandle mh1 = mh.asType(mh.type().changeReturnType(void.class));
  54         assert(mh1 != mh);
  55         testWMT(mh, mh1, 1000);
  56     }
  57 
  58     @Test
  59     public void testBoundWMT() throws Throwable {
  60         // mostly call exactInvoker.bindTo(testWMTCallee), but sometimes call its void-returning variant
  61         MethodHandle callee = testWMTCallee();
  62         MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
  63         MethodHandle invoker = exactInvoker(callee.type());
  64         MethodHandle mh  = invoker.bindTo(callee);
  65         MethodHandle mh1 = invoker.bindTo(callee1);
  66         testWMT(mh, mh1, 1000);
  67     }
  68 
  69     @Test
  70     public void testFoldWMT() throws Throwable {
  71         // mostly call exactInvoker.fold(constant(testWMTCallee)), but sometimes call its void-returning variant
  72         MethodHandle callee = testWMTCallee();
  73         MethodHandle callee1 = callee.asType(callee.type().changeReturnType(void.class));
  74         MethodHandle invoker = exactInvoker(callee.type());
  75         MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
  76         MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
  77         testWMT(mh, mh1, 1000);
  78     }
  79 
  80     @Test
  81     public void testFoldCCE() throws Throwable {
  82         MethodHandle callee = testWMTCallee();
  83         MethodHandle callee1 = callee.asType(callee.type().changeParameterType(1, Number.class)).asType(callee.type());
  84         MethodHandle invoker = exactInvoker(callee.type());
  85         MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
  86         MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
  87         testWMT(mh, mh1, 1000);
  88     }
  89 
  90     @Test
  91     public void testStackOverflow() throws Throwable {
  92         MethodHandle callee = testWMTCallee();
  93         MethodHandle callee1 = makeStackOverflow().asType(callee.type());
  94         MethodHandle invoker = exactInvoker(callee.type());
  95         MethodHandle mh  = foldArguments(invoker, constant(MethodHandle.class, callee));
  96         MethodHandle mh1 = foldArguments(invoker, constant(MethodHandle.class, callee1));
  97         for (int i = 0; i < REPEAT; i++) {
  98             try {
  99                 testWMT(mh, mh1, 1000);
 100             } catch (StackOverflowError ex) {
 101                 // OK, try again
 102             }
 103         }
 104     }
 105 
 106     private static MethodHandle makeStackOverflow() {
 107         MethodType cellType = methodType(void.class);
 108         MethodHandle[] cell = { null };  // recursion point
 109         MethodHandle getCell = insertArguments(arrayElementGetter(cell.getClass()), 0, cell, 0);
 110         MethodHandle invokeCell = foldArguments(exactInvoker(cellType), getCell);
 111         assert(invokeCell.type() == cellType);
 112         cell[0] = invokeCell;
 113         // make it conformable to any type:
 114         invokeCell = dropArguments(invokeCell, 0, Object[].class).asVarargsCollector(Object[].class);
 115         return invokeCell;
 116     }
 117 
 118     static int testCases;
 119 
 120     private void testAll(String match) throws Throwable {
 121         testCases = 0;
 122         Lookup lookup = lookup();
 123         for (Method m : CLASS.getDeclaredMethods()) {
 124             String name = m.getName();
 125             if (name.startsWith("test") &&
 126                 (match == null || match.contains(name.substring("test".length()))) &&
 127                 m.getParameterTypes().length == 0 &&
 128                 Modifier.isPublic(m.getModifiers()) &&
 129                 !Modifier.isStatic(m.getModifiers())) {
 130                 System.out.println("["+name+"]");
 131                 int tc = testCases;
 132                 try {
 133                     m.invoke(this);
 134                 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
 135                     System.out.println("*** "+ex);
 136                     ex.printStackTrace(System.out);
 137                 }
 138                 if (testCases == tc)  testCases++;
 139             }
 140         }
 141         if (testCases == 0)  throw new RuntimeException("no test cases found");
 142         System.out.println("ran a total of "+testCases+" test cases");
 143     }
 144 
 145     private static MethodHandle findStatic(String name) {
 146         return findMethod(name, true);
 147     }
 148     private static MethodHandle findVirtual(String name) {
 149         return findMethod(name, false);
 150     }
 151     private static MethodHandle findMethod(String name, boolean isStatic) {
 152         MethodHandle mh = null;
 153         for (Method m : CLASS.getDeclaredMethods()) {
 154             if (m.getName().equals(name) &&
 155                 Modifier.isStatic(m.getModifiers()) == isStatic) {
 156                 if (mh != null)
 157                     throw new RuntimeException("duplicate methods: "+name);
 158                 try {
 159                     mh = LOOKUP.unreflect(m);
 160                 } catch (ReflectiveOperationException ex) {
 161                     throw new RuntimeException(ex);
 162                 }
 163             }
 164         }
 165         if (mh == null)
 166             throw new RuntimeException("no method: "+name);
 167         return mh;
 168     }
 169 
 170     int testWMTCallee;
 171     private int testWMTCallee(String x) {
 172         return testWMTCallee++;
 173     }
 174     private static MethodHandle testWMTCallee() {
 175         MethodHandle callee = findVirtual("testWMTCallee");
 176         // FIXME: should not have to retype callee
 177         callee = callee.asType(callee.type().changeParameterType(0, Object.class));
 178         return callee;
 179     }
 180 
 181     private Exception testWMT(MethodHandle[] mhs, int reps) throws Throwable {
 182         testCases += 1;
 183         testWMTCallee = 0;
 184         int catches = 0;
 185         Exception savedEx = null;
 186         for (int i = 0; i < reps; i++) {
 187             MethodHandle mh = mhs[i % mhs.length];
 188             int n;
 189             try {
 190                 // FIXME: should not have to retype this
 191                 n = (int) mh.invokeExact((Object)this, "x");
 192                 assertEquals(n, i - catches);
 193                 // Using the exact type for this causes endless deopt due to
 194                 // 'non_cached_result' in SystemDictionary::find_method_handle_invoke.
 195                 // The problem is that the compiler thread needs to access a cached
 196                 // invoke method, but invoke methods are not cached if one of the
 197                 // component types is not on the BCP.
 198             } catch (Exception ex) {
 199                 savedEx = ex;
 200                 catches++;
 201             }
 202         }
 203         //VERBOSE: System.out.println("reps="+reps+" catches="+catches);
 204         return savedEx;
 205     }
 206 
 207     private static final int REPEAT = Integer.getInteger(CLASS.getSimpleName()+".REPEAT", 10);
 208 
 209     private Exception testWMT(MethodHandle mh, MethodHandle mh1, int reps) throws Throwable {
 210         //VERBOSE: System.out.println("mh="+mh+" mh1="+mh1);
 211         MethodHandle[] mhs = new MethodHandle[100];
 212         Arrays.fill(mhs, mh);
 213         int patch = mhs.length-1;
 214         Exception savedEx = null;
 215         for (int i = 0; i < REPEAT; i++) {
 216             mhs[patch] = mh;
 217             testWMT(mhs, 10000);
 218             mhs[patch] = mh1;
 219             savedEx = testWMT(mhs, reps);
 220         }
 221         return savedEx;
 222     }
 223 
 224     private static void assertEquals(Object x, Object y) {
 225         if (x == y || x != null && x.equals(y))  return;
 226         throw new RuntimeException(x+" != "+y);
 227     }
 228 }