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