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