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 testng TestTransitiveInvokeModule
 28  * @run testng/othervm -Dbabylon.ssa=cytron TestTransitiveInvokeModule
 29  */
 30 
 31 import jdk.incubator.code.Op;
 32 import jdk.incubator.code.dialect.java.JavaOp;
 33 import org.testng.Assert;
 34 import org.testng.annotations.Test;
 35 
 36 import java.lang.invoke.MethodHandles;
 37 import java.lang.reflect.Method;
 38 import jdk.incubator.code.OpTransformer;
 39 import jdk.incubator.code.analysis.SSA;
 40 import jdk.incubator.code.interpreter.Interpreter;
 41 import jdk.incubator.code.dialect.core.CoreOp;
 42 import jdk.incubator.code.dialect.java.MethodRef;
 43 import jdk.incubator.code.CodeReflection;
 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         Assert.assertEquals(r, List.of(9, 7, 5, 3, 1, -1));
 84         Assert.assertEquals(result, -2);
 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, iop.invokeKind());
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 }