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