From da6b4a959586bcaecf4aab2cc220935aebbe8609 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 28 Apr 2011 16:27:13 +0200 Subject: [PATCH] += for strings --- .../jet/codegen/ExpressionCodegen.java | 47 ++++++++++++++----- .../org/jetbrains/jet/codegen/StackValue.java | 22 +++++++-- idea/testData/codegen/fieldSetterPlusEq.jet | 7 +++ .../jet/codegen/NamespaceGenTest.java | 7 +++ .../jet/codegen/PropertyGenTest.java | 9 ++++ 5 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 idea/testData/codegen/fieldSetterPlusEq.jet diff --git a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index 400cda7b555..344fa1e5a9c 100644 --- a/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/idea/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -828,25 +828,44 @@ public class ExpressionCodegen extends JetVisitor { private void generateAugmentedAssignment(JetBinaryExpression expression) { DeclarationDescriptor op = bindingContext.resolveReferenceExpression(expression.getOperationReference()); final JetExpression lhs = expression.getLeft(); - Type asmType = expressionType(lhs); - StackValue value = generateIntermediateValue(lhs); - value.dupReceiver(v); - value.put(asmType, v); - genToJVMStack(expression.getRight()); - v.visitInsn(asmType.getOpcode(opcodeForMethod(op.getName()))); - value.store(v); + Type lhsType = expressionType(lhs); + if (isNumberPrimitive(lhsType)) { + StackValue value = generateIntermediateValue(lhs); // receiver + value.dupReceiver(v, 0); // receiver receiver + value.put(lhsType, v); // receiver lhs + genToJVMStack(expression.getRight()); // receiver lhs rhs + v.visitInsn(lhsType.getOpcode(opcodeForMethod(op.getName()))); // receiver result + value.store(v); + } + else if ("java.lang.String".equals(lhsType.getClassName()) && op.getName().equals("plus")) { + generateStringBuilderConstructor(); // StringBuilder + StackValue value = generateIntermediateValue(lhs); // StringBuilder receiver + value.dupReceiver(v, 1); // receiver StringBuilder receiver + value.put(lhsType, v); // receiver StringBuilder value + invokeAppendMethod(lhsType); // receiver StringBuilder + invokeAppend(expression.getRight()); // receiver StringBuilder + v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;"); + value.store(v); + } + else { + throw new UnsupportedOperationException("Augmented assignment for non-primitive types not yet implemented"); + } } private void generateConcatenation(JetBinaryExpression expression) { + generateStringBuilderConstructor(); + invokeAppend(expression.getLeft()); + invokeAppend(expression.getRight()); + v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;"); + myStack.push(StackValue.onStack(Type.getObjectType(CLASS_STRING))); + } + + private void generateStringBuilderConstructor() { Type type = Type.getObjectType(CLASS_STRING_BUILDER); v.anew(type); v.dup(); Method method = new Method("", Type.VOID_TYPE, new Type[0]); v.invokespecial(CLASS_STRING_BUILDER, method.getName(), method.getDescriptor()); - invokeAppend(expression.getLeft()); - invokeAppend(expression.getRight()); - v.invokevirtual(CLASS_STRING_BUILDER, "toString", "()Ljava/lang/String;"); - myStack.push(StackValue.onStack(Type.getObjectType(CLASS_STRING))); } private void invokeAppend(final JetExpression expr) { @@ -860,6 +879,10 @@ public class ExpressionCodegen extends JetVisitor { } Type exprType = expressionType(expr); gen(expr, exprType); + invokeAppendMethod(exprType); + } + + private void invokeAppendMethod(Type exprType) { Method appendDescriptor = new Method("append", Type.getObjectType(CLASS_STRING_BUILDER), new Type[] { exprType.getSort() == Type.OBJECT ? JetTypeMapper.TYPE_OBJECT : exprType}); v.invokevirtual(CLASS_STRING_BUILDER, "append", appendDescriptor.getDescriptor()); @@ -934,7 +957,7 @@ public class ExpressionCodegen extends JetVisitor { } } StackValue value = generateIntermediateValue(operand); - value.dupReceiver(v); + value.dupReceiver(v, 0); value.put(asmType, v); if (asmType == Type.LONG_TYPE) { v.aconst(Long.valueOf(increment)); diff --git a/idea/src/org/jetbrains/jet/codegen/StackValue.java b/idea/src/org/jetbrains/jet/codegen/StackValue.java index ae3ea002e42..dc33d4e9daa 100644 --- a/idea/src/org/jetbrains/jet/codegen/StackValue.java +++ b/idea/src/org/jetbrains/jet/codegen/StackValue.java @@ -24,7 +24,7 @@ public abstract class StackValue { throw new UnsupportedOperationException("cannot store to value " + this); } - public void dupReceiver(InstructionAdapter v) { + public void dupReceiver(InstructionAdapter v, int below) { } public void condJump(Label label, boolean jumpIfFalse, InstructionAdapter v) { @@ -341,8 +341,13 @@ public abstract class StackValue { } @Override - public void dupReceiver(InstructionAdapter v) { - v.dup2(); // array and index + public void dupReceiver(InstructionAdapter v, int below) { + if (below == 1) { + v.dup2X1(); + } + else { + v.dup2(); // array and index + } } } @@ -364,8 +369,15 @@ public abstract class StackValue { } @Override - public void dupReceiver(InstructionAdapter v) { - if (!isStatic) v.dup(); + public void dupReceiver(InstructionAdapter v, int below) { + if (!isStatic) { + if (below == 1) { + v.dupX1(); + } + else { + v.dup(); + } + } } @Override diff --git a/idea/testData/codegen/fieldSetterPlusEq.jet b/idea/testData/codegen/fieldSetterPlusEq.jet new file mode 100644 index 00000000000..407adaf7f5e --- /dev/null +++ b/idea/testData/codegen/fieldSetterPlusEq.jet @@ -0,0 +1,7 @@ +var collector: String = "" + set(it) { $collector += it } + +fun append(s: String): String { + collector = s; + return collector; +} diff --git a/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java b/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java index abba7b42a1d..e3179f32814 100644 --- a/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/NamespaceGenTest.java @@ -259,6 +259,13 @@ public class NamespaceGenTest extends CodegenTestCase { assertEquals("jet Lang", main.invoke(null, "jet", " ", "Lang")); } + public void testStringPlusEq() throws Exception { + loadText("fun foo(s: String) { val result = s; result += s; return result; } "); + System.out.println(generateToText()); + final Method main = generateFunction(); + assertEquals("JarJar", main.invoke(null, "Jar")); + } + public void testStringCompare() throws Exception { loadText("fun foo(s1: String, s2: String) = s1 < s2"); System.out.println(generateToText()); diff --git a/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java b/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java index 34047039861..11377e758b2 100644 --- a/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java +++ b/idea/tests/org/jetbrains/jet/codegen/PropertyGenTest.java @@ -81,6 +81,15 @@ public class PropertyGenTest extends CodegenTestCase { assertEquals(value, "IntelliJ IDEA"); } + public void testFieldSetterPlusEq() throws Exception { + loadFile("fieldSetterPlusEq.jet"); + System.out.println(generateToText()); + final Method method = generateFunction("append"); + method.invoke(null, "IntelliJ "); + String value = (String) method.invoke(null, "IDEA"); + assertEquals(value, "IntelliJ IDEA"); + } + public void testAccessorsWithoutBody() throws Exception { loadText("class AccessorsWithoutBody() { public var foo: Int = 349\n get\n private set\n fun setter() { foo = 610; } } "); final Class aClass = loadImplementationClass(generateClassesInFile(), "AccessorsWithoutBody");