1 /*
  2  * Copyright (c) 2022, 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  * @modules java.base/java.lang.runtime:open
 27  * @run testng TestRecursive
 28  */
 29 
 30 import org.testng.annotations.Test;
 31 
 32 import java.lang.invoke.MethodHandle;
 33 import java.lang.reflect.Method;
 34 
 35 import static java.lang.invoke.MethodHandles.lookup;
 36 import static java.lang.invoke.MethodType.methodType;
 37 import static org.testng.Assert.assertEquals;
 38 
 39 public class TestRecursive {
 40     static MethodHandle recursive(MethodHandle base, MethodHandle... moreBases) throws ReflectiveOperationException {
 41         // temporarily place this API in an internal class
 42         Class<?> c = Class.forName("java.lang.runtime.ValueObjectMethods");
 43         Method m = c.getDeclaredMethod("recursive", MethodHandle.class, MethodHandle[].class);
 44         m.setAccessible(true);
 45         return (MethodHandle) m.invoke(null, base, moreBases);
 46     }
 47 
 48     static class Snippet1 {
 49         // classic recursive implementation of the factorial function
 50         static int base(MethodHandle recur, int k) throws Throwable {
 51             if (k <= 1)  return 1;
 52             return k * (int) recur.invokeExact(k - 1);
 53         }
 54         static void doTest() throws Throwable {
 55             var MT_base = methodType(int.class, MethodHandle.class, int.class);
 56             var MH_base = lookup().findStatic(Snippet1.class, "base", MT_base);
 57             // assume MH_base is a handle to the above method
 58             MethodHandle recur = recursive(MH_base);
 59             assertEquals(120, (int) recur.invoke(5));
 60         }
 61     }
 62 
 63     @Test
 64     public void testSingleRecursion() throws Throwable {
 65         Snippet1.doTest();
 66     }
 67 
 68     static class DoubleRecursion {
 69         static long entryPoint(MethodHandle entryPoint,
 70                                MethodHandle factorialOdd,
 71                                MethodHandle factorialEven,
 72                                long k) throws Throwable {
 73             if ((k & 1) == 0)
 74                 return (long) factorialEven.invokeExact(k, "even0", 2.2f);
 75             else
 76                 return (long) factorialOdd.invokeExact(k, "odd0");
 77         }
 78         static long factorialOdd(MethodHandle entryPoint,
 79                                  MethodHandle factorialOdd,
 80                                  MethodHandle factorialEven,
 81                                  long k,
 82                                  // change up the signature:
 83                                  String ignore) throws Throwable {
 84             assertEquals(k & 1, 1);
 85             if (k < 3)  return 1;
 86             return k * (long) factorialEven.invokeExact(k - 1, "even1", 3.3f);
 87         }
 88         static long factorialEven(MethodHandle entryPoint,
 89                                   MethodHandle factorialOdd,
 90                                   MethodHandle factorialEven,
 91                                   long k,
 92                                  // change up the signature again:
 93                                   String ignore, float ig2) throws Throwable {
 94             assertEquals(k & 1, 0);
 95             if (k < 2)  return 1;
 96             return k * (long) factorialOdd.invokeExact(k - 1, "odd1");
 97         }
 98         static void doTest() throws Throwable {
 99             var mt = methodType(long.class,
100                                 MethodHandle.class,
101                                 MethodHandle.class,
102                                 MethodHandle.class,
103                                 long.class);
104             var MH_entryPoint = lookup().findStatic(DoubleRecursion.class,
105                                                     "entryPoint", mt);
106             mt = mt.appendParameterTypes(String.class);
107             var MH_factorialOdd = lookup().findStatic(DoubleRecursion.class,
108                                                       "factorialOdd", mt);
109             mt = mt.appendParameterTypes(float.class);
110             var MH_factorialEven = lookup().findStatic(DoubleRecursion.class,
111                                                        "factorialEven", mt);
112             MethodHandle recur = recursive(MH_entryPoint,
113                                            MH_factorialOdd,
114                                            MH_factorialEven);
115             long fact = 1;
116             for (long k = 0; k < 20; k++) {
117                 assertEquals(fact, (long) recur.invoke(k));
118                 fact *= k+1;
119             }
120         }
121     }
122 
123     @Test
124     public void testDoubleRecursion() throws Throwable {
125         DoubleRecursion.doTest();
126     }
127 }
128