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