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 acceptOp(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.op(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 }