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