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 import jdk.incubator.code.Reflect;
 25 import jdk.incubator.code.Op;
 26 import jdk.incubator.code.dialect.core.CoreOp;
 27 import jdk.incubator.code.interpreter.Interpreter;
 28 import org.junit.jupiter.api.Assertions;
 29 import org.junit.jupiter.params.ParameterizedTest;
 30 import org.junit.jupiter.params.provider.MethodSource;
 31 
 32 import java.lang.invoke.MethodHandles;
 33 import java.lang.reflect.InvocationTargetException;
 34 import java.lang.reflect.Method;
 35 import java.util.HashMap;
 36 import java.util.Map;
 37 import java.util.Set;
 38 import java.util.stream.Stream;
 39 
 40 /*
 41  * @test
 42  * @modules jdk.incubator.code
 43  * @run junit TestConcat
 44  * @run main Unreflect TestConcat
 45  * @run junit TestConcat
 46  */
 47 
 48 public class TestConcat {
 49 
 50     static final String TESTSTR = "TESTING STRING";
 51 
 52     @Reflect
 53     static String f() {
 54        int test = 1;
 55        String res = "HI " + TESTSTR + test;
 56        return res;
 57     }
 58 
 59 
 60     @Reflect
 61     public static String byteConcat1(byte b, String s) {
 62        return b + s;
 63     }
 64 
 65     @Reflect
 66     public static String byteConcat2(String s, byte b) {
 67         return s + b;
 68     }
 69 
 70     @Reflect
 71     public static String shortConcat1(short b, String s) {
 72         return b + s;
 73     }
 74 
 75     @Reflect
 76     public static String shortConcat2(String s, short b) {
 77         return s + b;
 78     }
 79 
 80     @Reflect
 81     public static String intConcat1(int b, String s) {
 82         return b + s;
 83     }
 84 
 85     @Reflect
 86     public static String intConcat2(String s, int b) {
 87         return s + b;
 88     }
 89 
 90     @Reflect
 91     public static String longConcat1(long b, String s) {
 92         return b + s;
 93     }
 94 
 95     @Reflect
 96     public static String longConcat2(String s, long b) {
 97         return s + b;
 98     }
 99 
100     @Reflect
101     public static String floatConcat1(float b, String s) {
102         return b + s;
103     }
104 
105     @Reflect
106     public static String floatConcat2(String s, float b) {
107         return s + b;
108     }
109 
110     @Reflect
111     public static String doubleConcat1(double b, String s) {
112         return b + s;
113     }
114 
115     @Reflect
116     public static String doubleConcat2(String s, double b) {
117         return s + b;
118     }
119 
120     @Reflect
121     public static String booleanConcat1(boolean b, String s) {
122         return b + s;
123     }
124 
125     @Reflect
126     public static String booleanConcat2(String s, boolean b) {
127         return s + b;
128     }
129 
130     @Reflect
131     public static String charConcat1(char b, String s) {
132         return b + s;
133     }
134     @Reflect
135     public static String charConcat2(String s, char b) {
136         return s + b;
137     }
138     @Reflect
139     public static String objectConcat1(Object b, String s) {
140         return b + s;
141     }
142 
143     @Reflect
144     public static String objectConcat2(String s, Object b) {
145         return s + b;
146     }
147 
148     @Reflect
149     public static String objectConcat3(TestObject b, String s) {
150         return b + s;
151     }
152 
153     @Reflect
154     public static String objectConcat4(String s, TestObject b) {
155         return s + b;
156     }
157 
158     @Reflect
159     public static String stringConcat(String b, String s) {
160         return b + s;
161     }
162 
163 
164     record TestMethodData(Class<?> first, Class<?> second, String third) {
165     }
166 
167     static final Map<Class<?>, Object> valMap;
168     static {
169         valMap = new HashMap<>();
170         valMap.put(byte.class, (byte) 42);
171         valMap.put(short.class, (short) 42);
172         valMap.put(int.class, 42);
173         valMap.put(long.class, (long) 42);
174         valMap.put(float.class, 42f);
175         valMap.put(double.class, 42d);
176         valMap.put(char.class, 'z');
177         valMap.put(boolean.class, false);
178         valMap.put(Object.class, new Object() {
179                     @Override
180                     public String toString() {
181                         return "I'm a test string.";
182                     }
183                 });
184         valMap.put(TestObject.class, new TestObject());
185         valMap.put(String.class, TESTSTR);
186     }
187     private static String testName(Class<?> n, Integer i){
188         return n.getSimpleName().toLowerCase() + "Concat" + i;
189     }
190 
191     public static Stream<TestMethodData> testData() {
192         Set<Class<?>> types = Set.of(byte.class, short.class, int.class, long.class, float.class,
193                 double.class, char.class, boolean.class, Object.class);
194 
195         //Types from types concatenated to strings left-to-right and right-to-left
196         Stream<TestMethodData> s1 = types.stream().map(t -> new TestMethodData(t, String.class, testName(t, 1)));
197         Stream<TestMethodData> s2 = types.stream().map(t -> new TestMethodData(String.class, t, testName(t, 2)));
198 
199         //Custom Object and basic string concat tests
200         Stream<TestMethodData> s3 = Stream.of(new TestMethodData(TestObject.class, String.class, testName(Object.class, 3)),
201                                       new TestMethodData(String.class, TestObject.class, testName(Object.class, 4)),
202                                       new TestMethodData(String.class, String.class, "stringConcat"));
203 
204         return Stream.concat(Stream.concat(s1,s2),s3);
205     }
206 
207     @ParameterizedTest
208     @MethodSource("testData")
209     public static void testRun(TestMethodData t) {
210         try {
211             Object[] args = new Object[] {valMap.get(t.first), valMap.get(t.second)};
212             Class<TestConcat> clazz = TestConcat.class;
213             Method method = clazz.getDeclaredMethod(t.third, t.first, t.second);
214             CoreOp.FuncOp f = Op.ofMethod(method).orElseThrow();
215             var res1 = Interpreter.invoke(MethodHandles.lookup(), f, args);
216             var res2 = method.invoke(null, args);
217 
218             Assertions.assertEquals(res2, res1);
219 
220         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
221             throw new RuntimeException(e);
222         }
223     }
224 
225     public static final class TestObject {
226         TestObject(){}
227 
228         @Override
229         public String toString() {
230             return "TestObject String";
231         }
232     }
233 }