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