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 jdk.incubator.code.analysis; 27 28 import jdk.incubator.code.*; 29 import jdk.incubator.code.dialect.java.*; 30 31 import java.util.*; 32 33 /** 34 * StringConcatTransformer is an {@link jdk.incubator.code.OpTransformer} that removes concatenation operations 35 * from blocks and replaces them with equivalent {@link java.lang.StringBuilder} operations. This provides a pathway 36 * to remove {@link jdk.incubator.code.dialect.java.JavaOp.ConcatOp} for easier lowering to Bytecode. 37 */ 38 public final class StringConcatTransformer implements OpTransformer { 39 40 private static final JavaType J_L_STRING_BUILDER = JavaType.type(StringBuilder.class); 41 private static final MethodRef SB_TO_STRING_REF = MethodRef.method( 42 J_L_STRING_BUILDER, "toString", JavaType.J_L_STRING); 43 44 public StringConcatTransformer() {} 45 46 @Override 47 public Block.Builder apply(Block.Builder block, Op op) { 48 switch (op) { 49 case JavaOp.ConcatOp cz when isRootConcat(cz) -> { 50 // Create a string builder and build by traversing tree of operands 51 Op.Result builder = block.apply(JavaOp._new(ConstructorRef.constructor(J_L_STRING_BUILDER))); 52 buildFromTree(block, builder, cz); 53 // Convert to string 54 Value s = block.op(JavaOp.invoke(SB_TO_STRING_REF, builder)); 55 block.context().mapValue(cz.result(), s); 56 } 57 case JavaOp.ConcatOp _ -> { 58 // Process later when building from root concat 59 } 60 default -> block.op(op); 61 } 62 return block; 63 } 64 65 static boolean isRootConcat(JavaOp.ConcatOp cz) { 66 // Root of concat tree, zero uses, two or more uses, 67 // or one use that is not a subsequent concat op 68 Set<Op.Result> uses = cz.result().uses(); 69 return uses.size() != 1 || !(uses.iterator().next().op() instanceof JavaOp.ConcatOp); 70 } 71 72 static void buildFromTree(Block.Builder block, Op.Result builder, JavaOp.ConcatOp cz) { 73 // Process concat op's operands from left to right 74 buildFromTree(block, builder, cz.operands().get(0)); 75 buildFromTree(block, builder, cz.operands().get(1)); 76 } 77 78 static void buildFromTree(Block.Builder block, Op.Result builder, Value v) { 79 if (v instanceof Op.Result r && 80 r.op() instanceof JavaOp.ConcatOp cz && 81 r.uses().size() == 1) { 82 // Node of tree, recursively traverse the operands 83 buildFromTree(block, builder, cz); 84 } else { 85 // Leaf of tree, append value to builder 86 // Note leaf can be the result of a ConcatOp with multiple uses 87 block.op(append(block, builder, block.context().getValue(v))); 88 } 89 } 90 91 private static Op append(Block.Builder block, Value builder, Value arg) { 92 // Check if we need to widen unsupported integer types in the StringBuilder API 93 // Strings are fed in as-is, everything else given as an Object. 94 TypeElement type = arg.type(); 95 if (type instanceof PrimitiveType) { 96 //Widen Short and Byte to Int. 97 if (type.equals(JavaType.BYTE) || type.equals(JavaType.SHORT)) { 98 arg = block.op(JavaOp.conv(JavaType.INT, arg)); 99 type = JavaType.INT; 100 } 101 } else if (!type.equals(JavaType.J_L_STRING)){ 102 type = JavaType.J_L_OBJECT; 103 } 104 105 MethodRef methodDesc = MethodRef.method(J_L_STRING_BUILDER, "append", J_L_STRING_BUILDER, type); 106 return JavaOp.invoke(methodDesc, builder, arg); 107 } 108 109 110 }