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