diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java index edda1e0f96b..1d58a515884 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/IntrinsicMethods.java @@ -75,6 +75,7 @@ public class IntrinsicMethods { declareOverload(myStdLib.getLibraryScope().getFunctions("toString"), 0, new ToString()); declareOverload(myStdLib.getLibraryScope().getFunctions("equals"), 1, new Equals()); + declareOverload(myStdLib.getLibraryScope().getFunctions("plus"), 1, new StringPlus()); // declareIntrinsicFunction("Any", "equals", 1, new Equals()); // diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/StringPlus.java b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/StringPlus.java new file mode 100644 index 00000000000..79995fe4abb --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/intrinsics/StringPlus.java @@ -0,0 +1,28 @@ +package org.jetbrains.jet.codegen.intrinsics; + +import com.intellij.psi.PsiElement; +import org.jetbrains.jet.codegen.ExpressionCodegen; +import org.jetbrains.jet.codegen.JetTypeMapper; +import org.jetbrains.jet.codegen.StackValue; +import org.jetbrains.jet.lang.psi.JetCallExpression; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lexer.JetTokens; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.InstructionAdapter; + +import java.util.List; + +/** + * @author alex.tkachman + */ +public class StringPlus implements IntrinsicMethod { + @Override + public StackValue generate(ExpressionCodegen codegen, InstructionAdapter v, Type expectedType, PsiElement element, List arguments, StackValue receiver) { + codegen.gen(arguments.get(0)).put(JetTypeMapper.JL_STRING_TYPE, v); + codegen.gen(arguments.get(1)).put(JetTypeMapper.TYPE_OBJECT, v); + v.invokestatic("jet/runtime/Intrinsics", "stringPlus", "(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;"); + return StackValue.onStack(JetTypeMapper.JL_STRING_TYPE); + } +} diff --git a/compiler/frontend/src/jet/Library.jet b/compiler/frontend/src/jet/Library.jet index 296cd1038e6..b88e5eb0287 100644 --- a/compiler/frontend/src/jet/Library.jet +++ b/compiler/frontend/src/jet/Library.jet @@ -40,6 +40,8 @@ fun Any?.equals(other : Any?) : Boolean// = this === other // Returns "null" for null fun Any?.toString() : String// = this === other +fun String?.plus(other: Any?) : String + trait Iterator { fun next() : T val hasNext : Boolean diff --git a/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java b/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java index da0ecc0dd4b..bc3b7ec5397 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java +++ b/compiler/tests/org/jetbrains/jet/codegen/FunctionGenTest.java @@ -26,6 +26,41 @@ public class FunctionGenTest extends CodegenTestCase { } + public void testNullableAnyToString () throws InvocationTargetException, IllegalAccessException { + loadText("fun foo(x: Any?) = x.toString()"); + System.out.println(generateToText()); + Method foo = generateFunction(); + assertEquals("something", foo.invoke(null, "something")); + assertEquals("null", foo.invoke(null, new Object[]{null})); + + } + + public void testNullableStringPlus () throws InvocationTargetException, IllegalAccessException { + loadText("fun foo(x: String?, y: Any?) = x + y"); + String text = generateToText(); + assertTrue(text.contains(".stringPlus")); + System.out.println(text); + Method foo = generateFunction(); + assertEquals("something239", foo.invoke(null, "something", 239)); + assertEquals("null239", foo.invoke(null, null, 239)); + assertEquals("239null", foo.invoke(null, "239", null)); + assertEquals("nullnull", foo.invoke(null, null, null)); + + } + + public void testNonNullableStringPlus () throws InvocationTargetException, IllegalAccessException { + loadText("fun foo(x: String, y: Any?) = x + y + 120"); + String text = generateToText(); + assertFalse(text.contains(".stringPlus")); + System.out.println(text); + Method foo = generateFunction(); + assertEquals("something239120", foo.invoke(null, "something", 239)); + assertEquals("null239120", foo.invoke(null, null, 239)); + assertEquals("239null120", foo.invoke(null, "239", null)); + assertEquals("nullnull120", foo.invoke(null, null, null)); + + } + public void testAnyEqualsNullable () throws InvocationTargetException, IllegalAccessException { loadText("fun foo(x: Any?) = x.equals(\"lala\")"); System.out.println(generateToText()); diff --git a/stdlib/src/jet/runtime/Intrinsics.java b/stdlib/src/jet/runtime/Intrinsics.java new file mode 100644 index 00000000000..9243275e611 --- /dev/null +++ b/stdlib/src/jet/runtime/Intrinsics.java @@ -0,0 +1,13 @@ +package jet.runtime; + +/** + * @author alex.tkachman + */ +public class Intrinsics { + private Intrinsics() { + } + + public static String stringPlus(String self, Object other) { + return ((self == null) ? "null" : self) + ((other == null) ? "null" : other.toString()); + } +}