1 /*
2 * Copyright (c) 2024, 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 jdk.incubator.code
27 * @run junit TestTransitiveInvokeModule
28 * @run junit/othervm -Dbabylon.ssa=cytron TestTransitiveInvokeModule
29 */
30
31 import jdk.incubator.code.CodeReflection;
32 import jdk.incubator.code.Op;
33 import jdk.incubator.code.OpTransformer;
34 import jdk.incubator.code.analysis.SSA;
35 import jdk.incubator.code.dialect.core.CoreOp;
36 import jdk.incubator.code.dialect.java.JavaOp;
37 import jdk.incubator.code.dialect.java.MethodRef;
38 import jdk.incubator.code.interpreter.Interpreter;
39 import org.junit.jupiter.api.Assertions;
40 import org.junit.jupiter.api.Test;
41
42 import java.lang.invoke.MethodHandles;
43 import java.lang.reflect.Method;
44 import java.util.*;
45 import java.util.stream.Stream;
46
47 public class TestTransitiveInvokeModule {
48
49 @CodeReflection
50 static int m(int i, List<Integer> l) {
51 if (i < 0) {
52 return i;
53 }
54
55 return n(i - 1, l);
56 }
57
58 @CodeReflection
59 static int n(int i, List<Integer> l) {
60 l.add(i);
61 return m(i - 1, l);
62 }
63
64 @Test
65 public void test() {
66 Optional<Method> om = Stream.of(TestTransitiveInvokeModule.class.getDeclaredMethods())
67 .filter(m -> m.getName().equals("m"))
68 .findFirst();
69
70 CoreOp.ModuleOp module = createTransitiveInvokeModule(MethodHandles.lookup(), om.get());
71 System.out.println(module.toText());
72 module = module.transform(OpTransformer.LOWERING_TRANSFORMER);
73 System.out.println(module.toText());
74 module = SSA.transform(module);
75 System.out.println(module.toText());
76
77 module.functionTable().forEach((s, funcOp) -> {
78 System.out.println(s + " -> " + funcOp);
79 });
80
81 List<Integer> r = new ArrayList<>();
82 Object result = Interpreter.invoke(MethodHandles.lookup(), module.functionTable().firstEntry().getValue(), 10, r);
83 Assertions.assertEquals(List.of(9, 7, 5, 3, 1, -1), r);
84 Assertions.assertEquals(-2, result);
85 }
86
87 static CoreOp.ModuleOp createTransitiveInvokeModule(MethodHandles.Lookup l,
88 Method m) {
89 Optional<CoreOp.FuncOp> codeModel = Op.ofMethod(m);
90 if (codeModel.isPresent()) {
91 return createTransitiveInvokeModule(l, MethodRef.method(m), codeModel.get());
92 } else {
93 return CoreOp.module(List.of());
94 }
95 }
96
97 static CoreOp.ModuleOp createTransitiveInvokeModule(MethodHandles.Lookup l,
98 MethodRef entryRef, CoreOp.FuncOp entry) {
99 LinkedHashSet<MethodRef> funcsVisited = new LinkedHashSet<>();
100 List<CoreOp.FuncOp> funcs = new ArrayList<>();
101
102 record RefAndFunc(MethodRef r, CoreOp.FuncOp f) {
103 }
104 Deque<RefAndFunc> work = new ArrayDeque<>();
105 work.push(new RefAndFunc(entryRef, entry));
106 while (!work.isEmpty()) {
107 RefAndFunc rf = work.pop();
108 if (!funcsVisited.add(rf.r)) {
109 continue;
110 }
111
112 CoreOp.FuncOp tf = rf.f.transform(rf.r.toString(), (block, op) -> {
113 if (op instanceof JavaOp.InvokeOp iop) {
114 MethodRef r = iop.invokeDescriptor();
115 Method em = null;
116 try {
117 em = r.resolveToMethod(l);
118 } catch (ReflectiveOperationException _) {
119 }
120 if (em instanceof Method m) {
121 Optional<CoreOp.FuncOp> f = Op.ofMethod(m);
122 if (f.isPresent()) {
123 RefAndFunc call = new RefAndFunc(r, f.get());
124 // Place model on work queue
125 work.push(call);
126
127 // Replace invocation with function call
128 Op.Result result = block.op(CoreOp.funcCall(
129 call.r.toString(),
130 call.f.invokableType(),
131 block.context().getValues(iop.operands())));
132 // Map invocation result to function call result
133 block.context().mapValue(op.result(), result);
134 return block;
135 }
136 }
137 }
138 block.op(op);
139 return block;
140 });
141 funcs.add(tf);
142 }
143
144 return CoreOp.module(funcs);
145 }
146 }