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 TestStringConcatTransform
 28  * @run testng/othervm -Dbabylon.ssa=cytron TestStringConcatTransform
 29  */
 30 
 31 import jdk.incubator.code.Op;
 32 import org.testng.Assert;
 33 import org.testng.annotations.DataProvider;
 34 import org.testng.annotations.NoInjection;
 35 import org.testng.annotations.Test;
 36 
 37 import java.lang.invoke.MethodHandles;
 38 import java.lang.reflect.InvocationTargetException;
 39 import jdk.incubator.code.OpTransformer;
 40 import jdk.incubator.code.analysis.StringConcatTransformer;
 41 
 42 import java.lang.reflect.Method;
 43 import jdk.incubator.code.analysis.SSA;
 44 import jdk.incubator.code.interpreter.Interpreter;
 45 import jdk.incubator.code.op.CoreOp;
 46 import jdk.incubator.code.CodeReflection;
 47 import java.util.Arrays;
 48 import java.util.HashMap;
 49 import java.util.Map;
 50 
 51 public class TestStringConcatTransform {
 52 
 53     static final String TESTSTR = "TESTING STRING";
 54 
 55     static final Map<Class<?>, Object> valMap;
 56 
 57     static {
 58         valMap = new HashMap<>();
 59         valMap.put(byte.class, (byte) 42);
 60         valMap.put(short.class, (short) 42);
 61         valMap.put(int.class, 42);
 62         valMap.put(long.class, (long) 42);
 63         valMap.put(float.class, 42f);
 64         valMap.put(double.class, 42d);
 65         valMap.put(char.class, 'z');
 66         valMap.put(boolean.class, false);
 67 
 68         valMap.put(Byte.class, (byte) 42);
 69         valMap.put(Short.class, (short) 42);
 70         valMap.put(Integer.class, 42);
 71         valMap.put(Long.class, (long) 42);
 72         valMap.put(Float.class, 42f);
 73         valMap.put(Double.class, 42d);
 74         valMap.put(Character.class, 'z');
 75         valMap.put(Boolean.class, false);
 76 
 77         valMap.put(Object.class, new Object() {
 78             @Override
 79             public String toString() {
 80                 return "I'm a test string.";
 81             }
 82         });
 83         valMap.put(TestObject.class, new TestObject());
 84         valMap.put(String.class, TESTSTR);
 85         valMap.put(StringBuilder.class, new StringBuilder("test"));
 86     }
 87 
 88     public static final class TestObject {
 89         TestObject() {
 90         }
 91 
 92         @Override
 93         public String toString() {
 94             return "TestObject String";
 95         }
 96     }
 97 
 98     @Test(dataProvider = "getClassMethods")
 99     public void testModelTransform(@NoInjection Method method) {
100         CoreOp.FuncOp model = Op.ofMethod(method).orElseThrow();
101         CoreOp.FuncOp f_transformed = model.transform(new StringConcatTransformer());
102         Object[] args = prepArgs(method);
103 
104         model.writeTo(System.out);
105         f_transformed.writeTo(System.out);
106 
107         var interpreted = Interpreter.invoke(MethodHandles.lookup(), model, args);
108         var transformed_interpreted = Interpreter.invoke(MethodHandles.lookup(), f_transformed, args);
109 
110         Assert.assertEquals(interpreted, transformed_interpreted);
111 
112     }
113 
114     @Test(dataProvider = "getClassMethods")
115     public void testSSAModelTransform(@NoInjection Method method) {
116         Object[] args = prepArgs(method);
117         testStringConcat(method, args);
118     }
119 
120     //Testing to make sure StringBuilders aren't caught up in the concat transformation
121     @Test
122     public void testStringBuilderUnchanged() {
123         Method method;
124 
125         try {
126             method = TestStringConcatTransform.class.getMethod("stringBuilderArgCheck", String.class, String.class, StringBuilder.class);
127         } catch (NoSuchMethodException e) {
128            throw new RuntimeException(e);
129         }
130         Object[] args = {"Foo", "Bar", new StringBuilder("test")};
131         testStringConcat(method, args);
132 
133         Assert.assertEquals("test", args[2].toString());
134     }
135 
136     private void testStringConcat(Method method, Object[] args) {
137         CoreOp.FuncOp model = Op.ofMethod(method).orElseThrow();
138         CoreOp.FuncOp transformed_model = model.transform(new StringConcatTransformer());
139         CoreOp.FuncOp ssa_model = generateSSA(model);
140         CoreOp.FuncOp ssa_transformed_model = ssa_model.transform(new StringConcatTransformer());
141 
142         var model_interpreted = Interpreter.invoke(MethodHandles.lookup(), model, args);
143         var transformed_model_interpreted = Interpreter.invoke(MethodHandles.lookup(), transformed_model, args);
144         var ssa_interpreted = Interpreter.invoke(MethodHandles.lookup(), ssa_model, args);
145         var ssa_transformed_interpreted = Interpreter.invoke(MethodHandles.lookup(), ssa_transformed_model, args);
146         Object jvm_interpreted;
147         try {
148             jvm_interpreted = method.invoke(null, args);
149         } catch (IllegalAccessException | InvocationTargetException e) {
150             throw new RuntimeException(e);
151         }
152         Assert.assertEquals(model_interpreted, transformed_model_interpreted);
153         Assert.assertEquals(transformed_model_interpreted, ssa_interpreted);
154         Assert.assertEquals(ssa_interpreted, ssa_transformed_interpreted);
155         Assert.assertEquals(ssa_transformed_interpreted, jvm_interpreted);
156 
157     }
158 
159     public static Object[] prepArgs(Method m) {
160         Class<?>[] argTypes = m.getParameterTypes();
161         Object[] args = new Object[argTypes.length];
162         for (int i = 0; i < argTypes.length; i++) {
163             args[i] = valMap.get(argTypes[i]);
164         }
165         return args;
166     }
167 
168     @DataProvider(name = "getClassMethods")
169     public static Object[][] getClassMethods() {
170         return getTestMethods(TestStringConcatTransform.class);
171     }
172 
173     public static Object[][] getTestMethods(Class<?> clazz) {
174         Object[][] res = Arrays.stream(clazz.getMethods())
175                 .filter((method) -> method.isAnnotationPresent(CodeReflection.class))
176                 .map(m -> new Object[]{m})
177                 .toArray(Object[][]::new);
178         return res;
179     }
180 
181     static CoreOp.FuncOp generateSSA(CoreOp.FuncOp f) {
182         CoreOp.FuncOp lf = f.transform(OpTransformer.LOWERING_TRANSFORMER);
183         lf = SSA.transform(lf);
184         lf.writeTo(System.out);
185         return lf;
186     }
187 
188     @CodeReflection
189     public static String intConcat(int i, String s) {
190         return i + s + "hello" + 52;
191     }
192 
193 
194     @CodeReflection
195     public static String intConcatAssignment(int i, String s) {
196         String s1 = i + s;
197         return s1 + "hello" + 52;
198     }
199 
200     @CodeReflection
201     public static String intConcatExprAssignment(int i, String s) {
202         String r;
203         String inter = i + (r = s + "hello") + 52;
204         return r + inter;
205     }
206 
207     @CodeReflection
208     public static String intConcatWideExpr(int i, String s) {
209         String s1 = i + s;
210         return s1 + "hello" + 52 + "world" + 26 + "!";
211     }
212 
213     @CodeReflection
214     public static String intConcatDoubVar(int i, String s) {
215         String r;
216         String inter = i + (r = s + "hello") + 52;
217         String inter2 = i + (r = s + "hello") + 52 + inter;
218         return r + inter2;
219     }
220 
221     @CodeReflection
222     public static String intConcatNestedSplit(int i, String s) {
223         String q, r;
224         String inter = i + (q = r = s + "hello") + 52;
225         return q + r + inter;
226     }
227 
228     @CodeReflection
229     public static String nonLeftAssociativeTree(String a, String b, String c, String d) {
230         String s = (a + b) + (c + d);
231         return s;
232     }
233 
234     @CodeReflection
235     public static String stringBuilderCheck(String a, String d) {
236         StringBuilder sb = new StringBuilder("test");
237         String s = sb + a;
238         String t = s + d;
239         return t;
240     }
241 
242     @CodeReflection
243     public static String stringBuilderArgCheck(String a, String d, StringBuilder c) {
244         StringBuilder sb = c;
245         String s = sb + a;
246         String t = s + d;
247         return t;
248     }
249 
250     @CodeReflection
251     public static String leftAssociativeTree(String a, String b, String c, String d) {
252         String s = ((a + b) + c) + d;
253         return s;
254     }
255 
256     @CodeReflection
257     public static String rightAssociativeTree(String a, String b, String c, String d) {
258         String s = (a + (b + (c + d)));
259         return s;
260     }
261 
262     @CodeReflection
263     public static String widenPrimitives(short a, byte b, int c, int d) {
264         String s = (a + (b + (c + d + "hi")));
265         return s;
266     }
267 }
268