1 package org.simantics.scl.compiler.constants;
3 import java.util.Arrays;
5 import org.cojen.classfile.TypeDesc;
6 import org.objectweb.asm.Label;
7 import org.objectweb.asm.Opcodes;
8 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
9 import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
10 import org.simantics.scl.compiler.internal.codegen.references.IVal;
11 import org.simantics.scl.compiler.internal.codegen.references.Val;
12 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
13 import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;
14 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
15 import org.simantics.scl.compiler.types.TVar;
16 import org.simantics.scl.compiler.types.Type;
17 import org.simantics.scl.compiler.types.Types;
19 public class StringInterpolation extends FunctionValue {
21 public static final TypeDesc STRING_BUILDER = TypeDesc.forClass(StringBuilder.class);
23 private final String[] textParts;
25 public StringInterpolation(Type[] parameterTypes, String[] textParts) {
26 super(TVar.EMPTY_ARRAY, Types.NO_EFFECTS, Types.STRING, parameterTypes);
27 this.textParts = textParts;
30 public StringInterpolation(String[] textParts) {
31 this(stringTypeArray(textParts.length-1), textParts);
35 public String toString() {
36 StringBuilder b = new StringBuilder();
39 for(String textPart : textParts) {
50 private static Type[] stringTypeArray(int length) {
51 Type[] result = new Type[length];
52 for(int i=0;i<length;++i)
53 result[i] = Types.STRING;
58 public Type applyExact(MethodBuilder mb, Val[] parameters) {
59 if(textParts.length==1) {
60 mb.loadConstant(textParts[0]);
62 else if(textParts.length==2) {
63 if(parameters[0].getType() == Types.STRING) {
64 // Optimized special cases "asd" + x, x + "asd"
65 if(textParts[0].isEmpty()) {
66 mb.push(parameters[0], Types.STRING);
67 if(!textParts[1].isEmpty()) {
68 mb.loadConstant(textParts[1]);
69 mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
73 else if(textParts[1].isEmpty()) {
74 mb.loadConstant(textParts[0]);
75 mb.push(parameters[0], Types.STRING);
76 mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
81 else if(textParts.length==3) {
82 if(parameters[0].getType() == Types.STRING && parameters[1].getType() == Types.STRING
83 && textParts[0].isEmpty() && textParts[1].isEmpty() && textParts[2].isEmpty()) {
84 mb.push(parameters[0], Types.STRING);
85 mb.push(parameters[1], Types.STRING);
86 mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
90 mb.newObject(STRING_BUILDER);
92 mb.invokeConstructor(STRING_BUILDER, Constants.EMPTY_TYPEDESC_ARRAY);
94 for(int i=0;i<parameters.length;++i) {
95 String textPart = textParts[i];
96 if(!textPart.isEmpty()) {
97 mb.loadConstant(textPart);
98 mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
101 mb.push(parameters[i], parameterTypes[i]);
102 mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER,
103 new TypeDesc[] {mb.getJavaTypeTranslator().toTypeDesc(parameterTypes[i])});
107 String textPart = textParts[parameters.length];
108 if(!textPart.isEmpty()) {
109 mb.loadConstant(textPart);
110 mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
114 mb.invokeVirtual(STRING_BUILDER, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
120 public int hashCode() {
121 return Arrays.hashCode(textParts) ^ Arrays.hashCode(parameterTypes);
125 public boolean equals(Object obj) {
128 if(obj == null || obj.getClass() != getClass())
130 StringInterpolation other = (StringInterpolation)obj;
131 return Arrays.equals(textParts, other.textParts) && Arrays.equals(parameterTypes, other.parameterTypes);
135 public int constructorTag() {
136 return textParts.length == 2 && parameterTypes[0] == Types.STRING ? 0 : -1;
140 public void deconstruct(MethodBuilder mb, IVal parameter, Cont success, Label failure) {
141 if(textParts.length != 2)
142 throw new InternalCompilerError("String interpolation with more than one open string parts cannot be used as a pattern.");
143 mb.push(parameter, Types.STRING);
144 LocalVariable temp = mb.createLocalVariable(null, TypeDesc.STRING);
146 if(!textParts[0].isEmpty()) {
148 mb.loadConstant(textParts[0]);
149 mb.invokeVirtual("java/lang/String", "startsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
150 mb.ifZeroComparisonBranch(failure, "==");
152 if(!textParts[1].isEmpty()) {
154 mb.loadConstant(textParts[1]);
155 mb.invokeVirtual("java/lang/String", "endsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
156 mb.ifZeroComparisonBranch(failure, "==");
159 mb.loadConstant(textParts[0].length());
161 mb.invokeVirtual("java/lang/String", "length", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
162 mb.loadConstant(textParts[1].length());
163 mb.math(Opcodes.ISUB);
164 mb.invokeVirtual("java/lang/String", "substring", TypeDesc.STRING, new TypeDesc[] {TypeDesc.INT, TypeDesc.INT});
166 mb.jump(success, new LocalVariableConstant(Types.STRING, temp));