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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang.reflect.code.analysis; 27 28 import java.lang.reflect.code.*; 29 import java.lang.reflect.code.op.CoreOp; 30 import java.lang.reflect.code.type.JavaType; 31 import java.lang.reflect.code.type.FunctionType; 32 import java.lang.reflect.code.type.MethodRef; 33 import java.lang.reflect.code.type.PrimitiveType; 34 import java.util.*; 35 36 /** 37 * StringConcatTransformer is an {@link java.lang.reflect.code.OpTransformer} that removes concatenation operations 38 * from blocks and replaces them with equivalent {@link java.lang.StringBuilder} operations. This provides a pathway 39 * to remove {@link java.lang.reflect.code.op.CoreOp.ConcatOp} for easier lowering to Bytecode. 40 */ 41 public class StringConcatTransformer implements OpTransformer { 42 43 private static final JavaType J_L_STRING_BUILDER = JavaType.type(StringBuilder.class); 44 private static final MethodRef SB_TO_STRING_REF = MethodRef.method( 45 J_L_STRING_BUILDER, "toString", JavaType.J_L_STRING); 46 47 public StringConcatTransformer() {} 48 49 @Override 50 public Block.Builder apply(Block.Builder block, Op op) { 51 switch (op) { 52 case CoreOp.ConcatOp cz when isRootConcat(cz) -> { 53 // Create a string builder and build by traversing tree of operands 54 Op.Result builder = block.apply(CoreOp._new(FunctionType.functionType(J_L_STRING_BUILDER))); 55 buildFromTree(block, builder, cz); 56 // Convert to string 57 Value s = block.op(CoreOp.invoke(SB_TO_STRING_REF, builder)); 58 block.context().mapValue(cz.result(), s); 59 } 60 case CoreOp.ConcatOp _ -> { 61 // Process later when building from root concat 62 } 63 default -> block.op(op); 64 } 65 return block; 66 } 67 68 static boolean isRootConcat(CoreOp.ConcatOp cz) { 69 // Root of concat tree, zero uses, two or more uses, 70 // or one use that is not a subsequent concat op 71 Set<Op.Result> uses = cz.result().uses(); 72 return uses.size() != 1 || !(uses.iterator().next().op() instanceof CoreOp.ConcatOp); 73 } 74 75 static void buildFromTree(Block.Builder block, Op.Result builder, CoreOp.ConcatOp cz) { 76 // Process concat op's operands from left to right 77 buildFromTree(block, builder, cz.operands().get(0)); 78 buildFromTree(block, builder, cz.operands().get(1)); 79 } 80 81 static void buildFromTree(Block.Builder block, Op.Result builder, Value v) { 82 if (v instanceof Op.Result r && 83 r.op() instanceof CoreOp.ConcatOp cz && 84 r.uses().size() == 1) { 85 // Node of tree, recursively traverse the operands 86 buildFromTree(block, builder, cz); 87 } else { 88 // Leaf of tree, append value to builder 89 // Note leaf can be the result of a ConcatOp with multiple uses 90 block.op(append(block, builder, block.context().getValue(v))); 91 } 92 } 93 94 private static Op append(Block.Builder block, Value builder, Value arg) { 95 // Check if we need to widen unsupported integer types in the StringBuilder API 96 // Strings are fed in as-is, everything else given as an Object. 97 TypeElement type = arg.type(); 98 if (type instanceof PrimitiveType) { 99 //Widen Short and Byte to Int. 100 if (type.equals(JavaType.BYTE) || type.equals(JavaType.SHORT)) { 101 arg = block.op(CoreOp.conv(JavaType.INT, arg)); 102 type = JavaType.INT; 103 } 104 } else if (!type.equals(JavaType.J_L_STRING)){ 105 type = JavaType.J_L_OBJECT; 106 } 107 108 MethodRef methodDesc = MethodRef.method(J_L_STRING_BUILDER, "append", J_L_STRING_BUILDER, type); 109 return CoreOp.invoke(methodDesc, builder, arg); 110 } 111 112 113 }