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