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 TestVarArgsInvoke
 28  * @run main Unreflect TestVarArgsInvoke
 29  * @run junit TestVarArgsInvoke
 30  */
 31 
 32 import jdk.incubator.code.Reflect;
 33 import jdk.incubator.code.CodeTransformer;
 34 import jdk.incubator.code.Op;
 35 import jdk.incubator.code.dialect.core.CoreOp;
 36 import jdk.incubator.code.dialect.java.JavaOp;
 37 import jdk.incubator.code.dialect.java.JavaType;
 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.Optional;
 45 import java.util.stream.Stream;
 46 
 47 public class TestVarArgsInvoke {
 48 
 49     String m1(String... args) {
 50         StringBuilder sb = new StringBuilder("m1");
 51         for (String arg : args) {
 52             sb.append(arg);
 53         }
 54         return sb.toString();
 55     }
 56 
 57     static String sm1(String... args) {
 58         StringBuilder sb = new StringBuilder("sm1");
 59         for (String arg : args) {
 60             sb.append(arg);
 61         }
 62         return sb.toString();
 63     }
 64 
 65     String m2(String one, String... args) {
 66         StringBuilder sb = new StringBuilder("m2");
 67         sb.append(one);
 68         for (String arg : args) {
 69             sb.append(arg);
 70         }
 71         return sb.toString();
 72     }
 73 
 74     static String sm2(String one, String... args) {
 75         StringBuilder sb = new StringBuilder("sm2");
 76         sb.append(one);
 77         for (String arg : args) {
 78             sb.append(arg);
 79         }
 80         return sb.toString();
 81     }
 82 
 83     enum MethodKind {
 84         M1, SM1, M2, SM2;
 85     }
 86 
 87     @Reflect
 88     String fArray(String[] array, MethodKind m) {
 89         return switch (m) {
 90             case M1 -> m1(array);
 91             case SM1 -> sm1(array);
 92             case M2 -> m2("first", array);
 93             case SM2 -> sm2("first", array);
 94         };
 95     }
 96 
 97     @Test
 98     public void testArray() {
 99         CoreOp.FuncOp f = getFuncOp("fArray");
100         f = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
101 
102         invokes(f).forEach(iop -> {
103             Assertions.assertFalse(iop.isVarArgs());
104             Assertions.assertNull(iop.varArgOperands());
105         });
106 
107         String[] array = new String[]{"second", "third"};
108         for (MethodKind mk : MethodKind.values()) {
109             Assertions.assertEquals(
110                     fArray(array, mk), Interpreter.invoke(MethodHandles.lookup(), f, this, array, mk)
111             );
112         }
113     }
114 
115     @Reflect
116     String fEmpty(MethodKind m) {
117         return switch (m) {
118             case M1 -> m1();
119             case SM1 -> sm1();
120             case M2 -> m2("first");
121             case SM2 -> sm2("first");
122         };
123     }
124 
125     @Test
126     public void testEmpty() {
127         CoreOp.FuncOp f = getFuncOp("fEmpty");
128         f = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
129 
130         invokes(f).forEach(iop -> {
131             Assertions.assertTrue(iop.isVarArgs());
132             Assertions.assertTrue(iop.varArgOperands().isEmpty());
133         });
134 
135         String[] array = new String[]{"second", "third"};
136         for (MethodKind mk : MethodKind.values()) {
137             Assertions.assertEquals(
138                     fEmpty(mk), Interpreter.invoke(MethodHandles.lookup(), f, this, mk)
139             );
140         }
141     }
142 
143     @Reflect
144     String fOne(String one, MethodKind m) {
145         return switch (m) {
146             case M1 -> m1(one);
147             case SM1 -> sm1(one);
148             case M2 -> m2("first", one);
149             case SM2 -> sm2("first", one);
150         };
151     }
152 
153     @Test
154     public void testOne() {
155         CoreOp.FuncOp f = getFuncOp("fOne");
156         f = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
157 
158         invokes(f).forEach(iop -> {
159             Assertions.assertTrue(iop.isVarArgs());
160             Assertions.assertEquals(1, iop.varArgOperands().size());
161         });
162 
163         for (MethodKind mk : MethodKind.values()) {
164             Assertions.assertEquals(
165                     fOne("one", mk), Interpreter.invoke(MethodHandles.lookup(), f, this, "one", mk)
166             );
167         }
168     }
169 
170     @Reflect
171     String fMany(String one, String two, MethodKind m) {
172         return switch (m) {
173             case M1 -> m1(one, two);
174             case SM1 -> sm1(one, two);
175             case M2 -> m2("first", one, two);
176             case SM2 -> sm2("first", one, two);
177         };
178     }
179 
180     @Test
181     public void testMany() {
182         CoreOp.FuncOp f = getFuncOp("fMany");
183         f = f.transform(CodeTransformer.LOWERING_TRANSFORMER);
184 
185         invokes(f).forEach(iop -> {
186             Assertions.assertTrue(iop.isVarArgs());
187             Assertions.assertEquals(2, iop.varArgOperands().size());
188         });
189 
190         for (MethodKind mk : MethodKind.values()) {
191             Assertions.assertEquals(
192                     fMany("one", "two", mk), Interpreter.invoke(MethodHandles.lookup(), f, this, "one", "two", mk)
193             );
194         }
195     }
196 
197     static Stream<JavaOp.InvokeOp> invokes(CoreOp.FuncOp f) {
198         return f.elements().mapMulti((ce, c) -> {
199             if (ce instanceof JavaOp.InvokeOp iop &&
200                 iop.invokeDescriptor().refType().equals(JavaType.type(TestVarArgsInvoke.class))) {
201                 c.accept(iop);
202             }
203         });
204     }
205 
206     static CoreOp.FuncOp getFuncOp(String name) {
207         Optional<Method> om = Stream.of(TestVarArgsInvoke.class.getDeclaredMethods())
208                 .filter(m -> m.getName().equals(name))
209                 .findFirst();
210 
211         Method m = om.get();
212         return Op.ofMethod(m).get();
213     }
214 
215 }