]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/StringInterpolation.java
Merged changes from feature/scl to master.
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / constants / StringInterpolation.java
1 package org.simantics.scl.compiler.constants;\r
2 \r
3 import java.util.Arrays;\r
4 \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
18 \r
19 public class StringInterpolation extends FunctionValue {\r
20 \r
21     public static final TypeDesc STRING_BUILDER = TypeDesc.forClass(StringBuilder.class);\r
22     \r
23     private final String[] textParts;\r
24     \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
28     }\r
29     \r
30     public StringInterpolation(String[] textParts) {\r
31         this(stringTypeArray(textParts.length-1), textParts);\r
32     }\r
33     \r
34     @Override\r
35     public String toString() {\r
36         StringBuilder b = new StringBuilder();\r
37         b.append('"');\r
38         boolean first = true;\r
39         for(String textPart : textParts) {\r
40             if(first)\r
41                 first = false;\r
42             else\r
43                 b.append("\\(.)");\r
44             b.append(textPart);\r
45         }\r
46         b.append('"');\r
47         return b.toString();\r
48     }\r
49 \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
54         return result;\r
55     }\r
56 \r
57     @Override\r
58     public Type applyExact(MethodBuilder mb, Val[] parameters) {\r
59         if(textParts.length==1) {\r
60             mb.loadConstant(textParts[0]);\r
61         }\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
70                     }\r
71                     return Types.STRING;\r
72                 }\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
78                 }\r
79             }\r
80         }\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
88             }\r
89         }\r
90         mb.newObject(STRING_BUILDER);\r
91         mb.dup();\r
92         mb.invokeConstructor(STRING_BUILDER, Constants.EMPTY_TYPEDESC_ARRAY);\r
93         \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
99             }\r
100             \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
104         }\r
105         \r
106         {\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
111             }\r
112         }\r
113         \r
114         mb.invokeVirtual(STRING_BUILDER, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);\r
115         \r
116         return Types.STRING;\r
117     }\r
118     \r
119     @Override\r
120     public int hashCode() {\r
121         return Arrays.hashCode(textParts) ^ Arrays.hashCode(parameterTypes);\r
122     }\r
123 \r
124     @Override\r
125     public boolean equals(Object obj) {\r
126         if(this == obj)\r
127             return true;\r
128         if(obj == null || obj.getClass() != getClass())\r
129             return false;\r
130         StringInterpolation other = (StringInterpolation)obj;\r
131         return Arrays.equals(textParts, other.textParts) && Arrays.equals(parameterTypes, other.parameterTypes);\r
132     }\r
133     \r
134     @Override\r
135     public int constructorTag() {\r
136         return textParts.length == 2 && parameterTypes[0] == Types.STRING ? 0 : -1;\r
137     }\r
138     \r
139     @Override\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
151         }\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
157         }\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
167     }\r
168     \r
169 }\r