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