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 */
29
30 import jdk.incubator.code.CodeReflection;
31 import jdk.incubator.code.Op;
32 import jdk.incubator.code.OpTransformer;
33 import jdk.incubator.code.dialect.core.CoreOp;
34 import jdk.incubator.code.dialect.java.JavaOp;
35 import jdk.incubator.code.dialect.java.JavaType;
36 import jdk.incubator.code.interpreter.Interpreter;
37 import org.junit.jupiter.api.Assertions;
38 import org.junit.jupiter.api.Test;
39
40 import java.lang.invoke.MethodHandles;
41 import java.lang.reflect.Method;
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 Assertions.assertFalse(iop.isVarArgs());
102 Assertions.assertNull(iop.varArgOperands());
103 });
104
105 String[] array = new String[]{"second", "third"};
106 for (MethodKind mk : MethodKind.values()) {
107 Assertions.assertEquals(
108 fArray(array, mk), Interpreter.invoke(MethodHandles.lookup(), f, this, array, mk)
109 );
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 Assertions.assertTrue(iop.isVarArgs());
130 Assertions.assertTrue(iop.varArgOperands().isEmpty());
131 });
132
133 String[] array = new String[]{"second", "third"};
134 for (MethodKind mk : MethodKind.values()) {
135 Assertions.assertEquals(
136 fEmpty(mk), Interpreter.invoke(MethodHandles.lookup(), f, this, mk)
137 );
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 Assertions.assertTrue(iop.isVarArgs());
158 Assertions.assertEquals(1, iop.varArgOperands().size());
159 });
160
161 for (MethodKind mk : MethodKind.values()) {
162 Assertions.assertEquals(
163 fOne("one", mk), Interpreter.invoke(MethodHandles.lookup(), f, this, "one", mk)
164 );
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 Assertions.assertTrue(iop.isVarArgs());
185 Assertions.assertEquals(2, iop.varArgOperands().size());
186 });
187
188 for (MethodKind mk : MethodKind.values()) {
189 Assertions.assertEquals(
190 fMany("one", "two", mk), Interpreter.invoke(MethodHandles.lookup(), f, this, "one", "two", mk)
191 );
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 }