-package org.simantics.scl.compiler.constants;\r
-\r
-import java.util.Arrays;\r
-\r
-import org.cojen.classfile.TypeDesc;\r
-import org.objectweb.asm.Label;\r
-import org.objectweb.asm.Opcodes;\r
-import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;\r
-import org.simantics.scl.compiler.internal.codegen.continuations.Cont;\r
-import org.simantics.scl.compiler.internal.codegen.references.IVal;\r
-import org.simantics.scl.compiler.internal.codegen.references.Val;\r
-import org.simantics.scl.compiler.internal.codegen.utils.Constants;\r
-import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;\r
-import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;\r
-import org.simantics.scl.compiler.types.TVar;\r
-import org.simantics.scl.compiler.types.Type;\r
-import org.simantics.scl.compiler.types.Types;\r
-\r
-public class StringInterpolation extends FunctionValue {\r
-\r
- public static final TypeDesc STRING_BUILDER = TypeDesc.forClass(StringBuilder.class);\r
- \r
- private final String[] textParts;\r
- \r
- public StringInterpolation(Type[] parameterTypes, String[] textParts) {\r
- super(TVar.EMPTY_ARRAY, Types.NO_EFFECTS, Types.STRING, parameterTypes);\r
- this.textParts = textParts;\r
- }\r
- \r
- public StringInterpolation(String[] textParts) {\r
- this(stringTypeArray(textParts.length-1), textParts);\r
- }\r
- \r
- @Override\r
- public String toString() {\r
- StringBuilder b = new StringBuilder();\r
- b.append('"');\r
- boolean first = true;\r
- for(String textPart : textParts) {\r
- if(first)\r
- first = false;\r
- else\r
- b.append("\\(.)");\r
- b.append(textPart);\r
- }\r
- b.append('"');\r
- return b.toString();\r
- }\r
-\r
- private static Type[] stringTypeArray(int length) {\r
- Type[] result = new Type[length];\r
- for(int i=0;i<length;++i)\r
- result[i] = Types.STRING;\r
- return result;\r
- }\r
-\r
- @Override\r
- public Type applyExact(MethodBuilder mb, Val[] parameters) {\r
- if(textParts.length==1) {\r
- mb.loadConstant(textParts[0]);\r
- }\r
- else if(textParts.length==2) {\r
- if(parameters[0].getType() == Types.STRING) {\r
- // Optimized special cases "asd" + x, x + "asd"\r
- if(textParts[0].isEmpty()) {\r
- mb.push(parameters[0], Types.STRING);\r
- if(!textParts[1].isEmpty()) {\r
- mb.loadConstant(textParts[1]);\r
- mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});\r
- }\r
- return Types.STRING;\r
- }\r
- else if(textParts[1].isEmpty()) {\r
- mb.loadConstant(textParts[0]);\r
- mb.push(parameters[0], Types.STRING);\r
- mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});\r
- return Types.STRING;\r
- }\r
- }\r
- }\r
- else if(textParts.length==3) {\r
- if(parameters[0].getType() == Types.STRING && parameters[1].getType() == Types.STRING\r
- && textParts[0].isEmpty() && textParts[1].isEmpty() && textParts[2].isEmpty()) {\r
- mb.push(parameters[0], Types.STRING);\r
- mb.push(parameters[1], Types.STRING);\r
- mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});\r
- return Types.STRING;\r
- }\r
- }\r
- mb.newObject(STRING_BUILDER);\r
- mb.dup();\r
- mb.invokeConstructor(STRING_BUILDER, Constants.EMPTY_TYPEDESC_ARRAY);\r
- \r
- for(int i=0;i<parameters.length;++i) {\r
- String textPart = textParts[i]; \r
- if(!textPart.isEmpty()) {\r
- mb.loadConstant(textPart);\r
- mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});\r
- }\r
- \r
- mb.push(parameters[i], parameterTypes[i]);\r
- mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER,\r
- new TypeDesc[] {mb.getJavaTypeTranslator().toTypeDesc(parameterTypes[i])});\r
- }\r
- \r
- {\r
- String textPart = textParts[parameters.length]; \r
- if(!textPart.isEmpty()) {\r
- mb.loadConstant(textPart);\r
- mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});\r
- }\r
- }\r
- \r
- mb.invokeVirtual(STRING_BUILDER, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);\r
- \r
- return Types.STRING;\r
- }\r
- \r
- @Override\r
- public int hashCode() {\r
- return Arrays.hashCode(textParts) ^ Arrays.hashCode(parameterTypes);\r
- }\r
-\r
- @Override\r
- public boolean equals(Object obj) {\r
- if(this == obj)\r
- return true;\r
- if(obj == null || obj.getClass() != getClass())\r
- return false;\r
- StringInterpolation other = (StringInterpolation)obj;\r
- return Arrays.equals(textParts, other.textParts) && Arrays.equals(parameterTypes, other.parameterTypes);\r
- }\r
- \r
- @Override\r
- public int constructorTag() {\r
- return textParts.length == 2 && parameterTypes[0] == Types.STRING ? 0 : -1;\r
- }\r
- \r
- @Override\r
- public void deconstruct(MethodBuilder mb, IVal parameter, Cont success, Label failure) {\r
- if(textParts.length != 2)\r
- throw new InternalCompilerError("String interpolation with more than one open string parts cannot be used as a pattern.");\r
- mb.push(parameter, Types.STRING);\r
- LocalVariable temp = mb.createLocalVariable(null, TypeDesc.STRING);\r
- mb.storeLocal(temp);\r
- if(!textParts[0].isEmpty()) {\r
- mb.loadLocal(temp);\r
- mb.loadConstant(textParts[0]);\r
- mb.invokeVirtual("java/lang/String", "startsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});\r
- mb.ifZeroComparisonBranch(failure, "==");\r
- }\r
- if(!textParts[1].isEmpty()) {\r
- mb.loadLocal(temp);\r
- mb.loadConstant(textParts[1]);\r
- mb.invokeVirtual("java/lang/String", "endsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});\r
- mb.ifZeroComparisonBranch(failure, "==");\r
- }\r
- mb.loadLocal(temp);\r
- mb.loadConstant(textParts[0].length());\r
- mb.loadLocal(temp);\r
- mb.invokeVirtual("java/lang/String", "length", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);\r
- mb.loadConstant(textParts[1].length());\r
- mb.math(Opcodes.ISUB);\r
- mb.invokeVirtual("java/lang/String", "substring", TypeDesc.STRING, new TypeDesc[] {TypeDesc.INT, TypeDesc.INT});\r
- mb.storeLocal(temp);\r
- mb.jump(success, new LocalVariableConstant(Types.STRING, temp));\r
- }\r
- \r
-}\r
+package org.simantics.scl.compiler.constants;
+
+import java.util.Arrays;
+
+import org.cojen.classfile.TypeDesc;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
+import org.simantics.scl.compiler.internal.codegen.continuations.Cont;
+import org.simantics.scl.compiler.internal.codegen.references.IVal;
+import org.simantics.scl.compiler.internal.codegen.references.Val;
+import org.simantics.scl.compiler.internal.codegen.utils.Constants;
+import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
+import org.simantics.scl.compiler.types.TVar;
+import org.simantics.scl.compiler.types.Type;
+import org.simantics.scl.compiler.types.Types;
+
+public class StringInterpolation extends FunctionValue {
+
+ public static final TypeDesc STRING_BUILDER = TypeDesc.forClass(StringBuilder.class);
+
+ private final String[] textParts;
+
+ public StringInterpolation(Type[] parameterTypes, String[] textParts) {
+ super(TVar.EMPTY_ARRAY, Types.NO_EFFECTS, Types.STRING, parameterTypes);
+ this.textParts = textParts;
+ }
+
+ public StringInterpolation(String[] textParts) {
+ this(stringTypeArray(textParts.length-1), textParts);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append('"');
+ boolean first = true;
+ for(String textPart : textParts) {
+ if(first)
+ first = false;
+ else
+ b.append("\\(.)");
+ b.append(textPart);
+ }
+ b.append('"');
+ return b.toString();
+ }
+
+ private static Type[] stringTypeArray(int length) {
+ Type[] result = new Type[length];
+ for(int i=0;i<length;++i)
+ result[i] = Types.STRING;
+ return result;
+ }
+
+ @Override
+ public Type applyExact(MethodBuilder mb, Val[] parameters) {
+ if(textParts.length==1) {
+ mb.loadConstant(textParts[0]);
+ }
+ else if(textParts.length==2) {
+ if(parameters[0].getType() == Types.STRING) {
+ // Optimized special cases "asd" + x, x + "asd"
+ if(textParts[0].isEmpty()) {
+ mb.push(parameters[0], Types.STRING);
+ if(!textParts[1].isEmpty()) {
+ mb.loadConstant(textParts[1]);
+ mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
+ }
+ return Types.STRING;
+ }
+ else if(textParts[1].isEmpty()) {
+ mb.loadConstant(textParts[0]);
+ mb.push(parameters[0], Types.STRING);
+ mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
+ return Types.STRING;
+ }
+ }
+ }
+ else if(textParts.length==3) {
+ if(parameters[0].getType() == Types.STRING && parameters[1].getType() == Types.STRING
+ && textParts[0].isEmpty() && textParts[1].isEmpty() && textParts[2].isEmpty()) {
+ mb.push(parameters[0], Types.STRING);
+ mb.push(parameters[1], Types.STRING);
+ mb.invokeVirtual("java/lang/String", "concat", TypeDesc.STRING, new TypeDesc[] {TypeDesc.STRING});
+ return Types.STRING;
+ }
+ }
+ mb.newObject(STRING_BUILDER);
+ mb.dup();
+ mb.invokeConstructor(STRING_BUILDER, Constants.EMPTY_TYPEDESC_ARRAY);
+
+ for(int i=0;i<parameters.length;++i) {
+ String textPart = textParts[i];
+ if(!textPart.isEmpty()) {
+ mb.loadConstant(textPart);
+ mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
+ }
+
+ mb.push(parameters[i], parameterTypes[i]);
+ mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER,
+ new TypeDesc[] {mb.getJavaTypeTranslator().toTypeDesc(parameterTypes[i])});
+ }
+
+ {
+ String textPart = textParts[parameters.length];
+ if(!textPart.isEmpty()) {
+ mb.loadConstant(textPart);
+ mb.invokeVirtual(STRING_BUILDER, "append", STRING_BUILDER, new TypeDesc[] {TypeDesc.STRING});
+ }
+ }
+
+ mb.invokeVirtual(STRING_BUILDER, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
+
+ return Types.STRING;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(textParts) ^ Arrays.hashCode(parameterTypes);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(this == obj)
+ return true;
+ if(obj == null || obj.getClass() != getClass())
+ return false;
+ StringInterpolation other = (StringInterpolation)obj;
+ return Arrays.equals(textParts, other.textParts) && Arrays.equals(parameterTypes, other.parameterTypes);
+ }
+
+ @Override
+ public int constructorTag() {
+ return textParts.length == 2 && parameterTypes[0] == Types.STRING ? 0 : -1;
+ }
+
+ @Override
+ public void deconstruct(MethodBuilder mb, IVal parameter, Cont success, Label failure) {
+ if(textParts.length != 2)
+ throw new InternalCompilerError("String interpolation with more than one open string parts cannot be used as a pattern.");
+ mb.push(parameter, Types.STRING);
+ LocalVariable temp = mb.createLocalVariable(null, TypeDesc.STRING);
+ mb.storeLocal(temp);
+ if(!textParts[0].isEmpty()) {
+ mb.loadLocal(temp);
+ mb.loadConstant(textParts[0]);
+ mb.invokeVirtual("java/lang/String", "startsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
+ mb.ifZeroComparisonBranch(failure, "==");
+ }
+ if(!textParts[1].isEmpty()) {
+ mb.loadLocal(temp);
+ mb.loadConstant(textParts[1]);
+ mb.invokeVirtual("java/lang/String", "endsWith", TypeDesc.BOOLEAN, new TypeDesc[] {TypeDesc.STRING});
+ mb.ifZeroComparisonBranch(failure, "==");
+ }
+ mb.loadLocal(temp);
+ mb.loadConstant(textParts[0].length());
+ mb.loadLocal(temp);
+ mb.invokeVirtual("java/lang/String", "length", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
+ mb.loadConstant(textParts[1].length());
+ mb.math(Opcodes.ISUB);
+ mb.invokeVirtual("java/lang/String", "substring", TypeDesc.STRING, new TypeDesc[] {TypeDesc.INT, TypeDesc.INT});
+ mb.storeLocal(temp);
+ mb.jump(success, new LocalVariableConstant(Types.STRING, temp));
+ }
+
+}