From 8dae1b62dd988f8847e9e7b40a71d7ca5e6d48d7 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 27 Nov 2014 16:02:41 +0300 Subject: [PATCH] Add CharSequence.subSequence This is done primarily for JVM interoperability, otherwise it's impossible to inherit from CharSequence there. On JS subSequence at the moment just invokes substring. #KT-5956 Fixed --- compiler/testData/builtin-classes.txt | 2 ++ compiler/testData/codegen/box/strings/kt5956.kt | 15 +++++++++++++++ .../diagnostics/tests/MergePackagesWithJava.txt | 8 ++++---- .../tests/cast/neverSucceeds/MappedSubtypes.txt | 1 + .../tests/j+k/kt1730_implementCharSequence.txt | 2 +- .../testData/diagnostics/tests/scopes/kt955.txt | 8 ++++---- .../generated/BlackBoxCodegenTestGenerated.java | 6 ++++++ core/builtins/native/kotlin/CharSequence.kt | 2 ++ core/builtins/native/kotlin/String.kt | 2 ++ .../generators/evaluate/GenerateOperationsMap.kt | 11 +++++------ .../jetbrains/k2js/test/semantics/StringTest.java | 4 ++++ .../functions/factories/StringOperationFIF.java | 1 + .../expression/string/cases/subSequence.kt | 15 +++++++++++++++ libraries/stdlib/src/kotlin/text/StringsJVM.kt | 8 ++------ 14 files changed, 64 insertions(+), 21 deletions(-) create mode 100644 compiler/testData/codegen/box/strings/kt5956.kt create mode 100644 js/js.translator/testData/expression/string/cases/subSequence.kt diff --git a/compiler/testData/builtin-classes.txt b/compiler/testData/builtin-classes.txt index b5cfc7dcb97..b9280d7b721 100644 --- a/compiler/testData/builtin-classes.txt +++ b/compiler/testData/builtin-classes.txt @@ -296,6 +296,7 @@ public final class CharRange : kotlin.Range, kotlin.Progression, kotlin.CharSequenc public final fun get(/*0*/ index: kotlin.Int): kotlin.Char public open override /*1*/ fun length(): kotlin.Int public final fun plus(/*0*/ other: kotlin.Any?): kotlin.String + public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence } public open class Throwable { diff --git a/compiler/testData/codegen/box/strings/kt5956.kt b/compiler/testData/codegen/box/strings/kt5956.kt new file mode 100644 index 00000000000..d3bb27017da --- /dev/null +++ b/compiler/testData/codegen/box/strings/kt5956.kt @@ -0,0 +1,15 @@ +// KT-5956 java.lang.AbstractMethodError: test.Thing.subSequence(II)Ljava/lang/CharSequence + +class Thing(val delegate: CharSequence) : CharSequence { + override fun charAt(index: Int): Char { + throw UnsupportedOperationException() + } + override fun length(): Int = 0 + override fun subSequence(start: Int, end: Int) = delegate.subSequence(start, end) +} + +fun box(): String { + val txt = Thing("hello there") + val s = txt.subSequence(0, 1) + return if ("$s" == "h") "OK" else "Fail: $s" +} diff --git a/compiler/testData/diagnostics/tests/MergePackagesWithJava.txt b/compiler/testData/diagnostics/tests/MergePackagesWithJava.txt index c3d226eec9e..3f981f8a349 100644 --- a/compiler/testData/diagnostics/tests/MergePackagesWithJava.txt +++ b/compiler/testData/diagnostics/tests/MergePackagesWithJava.txt @@ -4508,7 +4508,7 @@ package java { public open fun reverse(): java.lang.AbstractStringBuilder! public open fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public abstract override /*2*/ fun toString(): kotlin.String! @@ -8017,7 +8017,7 @@ package java { public open fun split(/*0*/ regex: kotlin.String!, /*1*/ limit: kotlin.Int): kotlin.Array<(out) kotlin.String!>! public open fun startsWith(/*0*/ p0: kotlin.String!): kotlin.Boolean public open fun startsWith(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int): kotlin.Boolean - public open fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open fun toCharArray(): kotlin.CharArray! @@ -8120,7 +8120,7 @@ package java { public open override /*1*/ fun reverse(): java.lang.StringBuffer! public open override /*1*/ fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open override /*1*/ fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open override /*1*/ fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*2*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open override /*1*/ fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open override /*1*/ fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open override /*3*/ fun toString(): kotlin.String! @@ -8191,7 +8191,7 @@ package java { public open override /*1*/ fun reverse(): java.lang.StringBuilder! public open override /*1*/ /*fake_override*/ fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open override /*1*/ /*fake_override*/ fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open override /*1*/ /*fake_override*/ fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*2*/ /*fake_override*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence public open override /*1*/ /*fake_override*/ fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open override /*1*/ /*fake_override*/ fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open override /*3*/ fun toString(): kotlin.String! diff --git a/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt b/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt index 152b4a92a48..e60bfb7f8b3 100644 --- a/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt +++ b/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt @@ -18,5 +18,6 @@ internal final class Sub : kotlin.CharSequence { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int public abstract override /*1*/ /*fake_override*/ fun length(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } diff --git a/compiler/testData/diagnostics/tests/j+k/kt1730_implementCharSequence.txt b/compiler/testData/diagnostics/tests/j+k/kt1730_implementCharSequence.txt index 4f4c904b46f..437a3bf71db 100644 --- a/compiler/testData/diagnostics/tests/j+k/kt1730_implementCharSequence.txt +++ b/compiler/testData/diagnostics/tests/j+k/kt1730_implementCharSequence.txt @@ -6,7 +6,7 @@ public open class C : kotlin.CharSequence { public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int java.lang.Override() public open override /*1*/ fun length(): kotlin.Int - java.lang.Override() public open fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! + java.lang.Override() public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! java.lang.Override() public open override /*1*/ fun toString(): kotlin.String! } diff --git a/compiler/testData/diagnostics/tests/scopes/kt955.txt b/compiler/testData/diagnostics/tests/scopes/kt955.txt index 663757a206f..668b2ba2793 100644 --- a/compiler/testData/diagnostics/tests/scopes/kt955.txt +++ b/compiler/testData/diagnostics/tests/scopes/kt955.txt @@ -4513,7 +4513,7 @@ package java { public open fun reverse(): java.lang.AbstractStringBuilder! public open fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public abstract override /*2*/ fun toString(): kotlin.String! @@ -8022,7 +8022,7 @@ package java { public open fun split(/*0*/ regex: kotlin.String!, /*1*/ limit: kotlin.Int): kotlin.Array<(out) kotlin.String!>! public open fun startsWith(/*0*/ p0: kotlin.String!): kotlin.Boolean public open fun startsWith(/*0*/ p0: kotlin.String!, /*1*/ p1: kotlin.Int): kotlin.Boolean - public open fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open fun toCharArray(): kotlin.CharArray! @@ -8125,7 +8125,7 @@ package java { public open override /*1*/ fun reverse(): java.lang.StringBuffer! public open override /*1*/ fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open override /*1*/ fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open override /*1*/ fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*2*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence! public open override /*1*/ fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open override /*1*/ fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open override /*3*/ fun toString(): kotlin.String! @@ -8196,7 +8196,7 @@ package java { public open override /*1*/ fun reverse(): java.lang.StringBuilder! public open override /*1*/ /*fake_override*/ fun setCharAt(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Char): kotlin.Unit public open override /*1*/ /*fake_override*/ fun setLength(/*0*/ p0: kotlin.Int): kotlin.Unit - public open override /*1*/ /*fake_override*/ fun subSequence(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.CharSequence! + public open override /*2*/ /*fake_override*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence public open override /*1*/ /*fake_override*/ fun substring(/*0*/ p0: kotlin.Int): kotlin.String! public open override /*1*/ /*fake_override*/ fun substring(/*0*/ p0: kotlin.Int, /*1*/ p1: kotlin.Int): kotlin.String! public open override /*3*/ fun toString(): kotlin.String! diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 2c24cd7f25f..57c29806a8b 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -6298,6 +6298,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("kt5956.kt") + public void testKt5956() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/kt5956.kt"); + doTest(fileName); + } + @TestMetadata("kt881.kt") public void testKt881() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/strings/kt881.kt"); diff --git a/core/builtins/native/kotlin/CharSequence.kt b/core/builtins/native/kotlin/CharSequence.kt index 6dd18390e0c..0ff37eedaaa 100644 --- a/core/builtins/native/kotlin/CharSequence.kt +++ b/core/builtins/native/kotlin/CharSequence.kt @@ -20,4 +20,6 @@ public trait CharSequence { public fun length(): Int public fun charAt(index: Int): Char + + public fun subSequence(start: Int, end: Int): CharSequence } diff --git a/core/builtins/native/kotlin/String.kt b/core/builtins/native/kotlin/String.kt index 02f5523a203..0cf13e8e49f 100644 --- a/core/builtins/native/kotlin/String.kt +++ b/core/builtins/native/kotlin/String.kt @@ -25,5 +25,7 @@ public class String : Comparable, CharSequence { public override fun charAt(index: Int): Char + public override fun subSequence(start: Int, end: Int): CharSequence + public override fun compareTo(other: String): Int } diff --git a/generators/src/org/jetbrains/jet/generators/evaluate/GenerateOperationsMap.kt b/generators/src/org/jetbrains/jet/generators/evaluate/GenerateOperationsMap.kt index c3fb71aa627..8c9e14ee4ae 100644 --- a/generators/src/org/jetbrains/jet/generators/evaluate/GenerateOperationsMap.kt +++ b/generators/src/org/jetbrains/jet/generators/evaluate/GenerateOperationsMap.kt @@ -22,13 +22,12 @@ import com.intellij.openapi.util.io.FileUtil import org.jetbrains.jet.utils.Printer import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns import org.jetbrains.jet.lang.descriptors.ClassDescriptor -import java.util.Collections import org.jetbrains.jet.lang.descriptors.FunctionDescriptor import org.jetbrains.jet.lang.types.TypeUtils import org.jetbrains.jet.lang.types.JetType val DEST_FILE: File = File("compiler/frontend/src/org/jetbrains/jet/lang/evaluate/OperationsMapGenerated.kt") -private val EXCLUDED_FUNCTIONS = listOf("rangeTo", "hashCode", "inc", "dec") +private val EXCLUDED_FUNCTIONS = listOf("rangeTo", "hashCode", "inc", "dec", "subSequence") fun main(args: Array) { GeneratorsFileUtil.writeFileIfContentChanged(DEST_FILE, generate()) @@ -56,7 +55,7 @@ fun generate(): String { for (descriptor in allPrimitiveTypes + builtIns.getString()) { [suppress("UNCHECKED_CAST")] - val functions = descriptor.getMemberScope(Collections.emptyList()).getDescriptors() + val functions = descriptor.getMemberScope(listOf()).getDescriptors() .filter { it is FunctionDescriptor && !EXCLUDED_FUNCTIONS.contains(it.getName().asString()) } as List for (function in functions) { @@ -82,7 +81,7 @@ fun generate(): String { val (funcName, parameters) = unaryOperationsMapIterator.next() p.println( "unaryOperation(", - parameters.map { it.asSrting() }.makeString(", "), + parameters.map { it.asString() }.joinToString(", "), ", ", "\"$funcName\"", ", { a -> a.${funcName}() }, ", @@ -105,7 +104,7 @@ fun generate(): String { val (funcName, parameters) = binaryOperationsMapIterator.next() p.println( "binaryOperation(", - parameters.map { it.asSrting() }.makeString(", "), + parameters.map { it.asString() }.joinToString(", "), ", ", "\"$funcName\"", ", { a, b -> a.${funcName}(b) }, ", @@ -168,4 +167,4 @@ private fun FunctionDescriptor.getParametersTypes(): List { return list } -private fun JetType.asSrting(): String = getConstructor().getDeclarationDescriptor()!!.getName().asString().toUpperCase() +private fun JetType.asString(): String = getConstructor().getDeclarationDescriptor()!!.getName().asString().toUpperCase() diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/StringTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/StringTest.java index 9ed5e3b33dc..8257a090710 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/StringTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/StringTest.java @@ -90,4 +90,8 @@ public final class StringTest extends AbstractExpressionTest { public void testNullableTypeInStringTemplate() throws Exception { checkFooBoxIsOk(); } + + public void testSubSequence() throws Exception { + checkFooBoxIsOk(); + } } diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/StringOperationFIF.java b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/StringOperationFIF.java index 250d52f41e7..296d0f5952a 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/StringOperationFIF.java +++ b/js/js.translator/src/org/jetbrains/k2js/translate/intrinsic/functions/factories/StringOperationFIF.java @@ -30,6 +30,7 @@ public final class StringOperationFIF extends CompositeFIF { // This intrinsic is needed because charAt is a public function taking one parameter and thus its name would be mangled otherwise add(pattern("kotlin", "CharSequence", "charAt").checkOverridden(), new BuiltInFunctionIntrinsic("charAt")); add(pattern("kotlin", "CharSequence", "length").checkOverridden(), LENGTH_PROPERTY_INTRINSIC); + add(pattern("kotlin", "CharSequence", "subSequence").checkOverridden(), new BuiltInFunctionIntrinsic("substring")); add(pattern("kotlin.js", "isEmpty").isExtensionOf("kotlin.CharSequence"), IS_EMPTY_INTRINSIC); } } diff --git a/js/js.translator/testData/expression/string/cases/subSequence.kt b/js/js.translator/testData/expression/string/cases/subSequence.kt new file mode 100644 index 00000000000..c1496ae14f3 --- /dev/null +++ b/js/js.translator/testData/expression/string/cases/subSequence.kt @@ -0,0 +1,15 @@ +package foo + +fun box(): String { + val kotlin: String = "kotlin" + + if (kotlin.subSequence(0, kotlin.length()) != kotlin) return "Fail 0" + + val kot: CharSequence = kotlin.subSequence(0, 3) + if (kot.toString() != "kot") return "Fail 1: $kot" + + val tlin = (kotlin : CharSequence).subSequence(2, 6) + if (tlin.toString() != "tlin") return "Fail 2: $tlin" + + return "OK" +} diff --git a/libraries/stdlib/src/kotlin/text/StringsJVM.kt b/libraries/stdlib/src/kotlin/text/StringsJVM.kt index a1298e1ca52..510eee92eae 100644 --- a/libraries/stdlib/src/kotlin/text/StringsJVM.kt +++ b/libraries/stdlib/src/kotlin/text/StringsJVM.kt @@ -115,14 +115,10 @@ public fun String.regionMatches(toffset: Int, other: String, ooffset: Int, len: public fun String.replace(target: CharSequence, replacement: CharSequence): String = (this as java.lang.String).replace(target, replacement) -public fun String.subSequence(beginIndex: Int, endIndex: Int): CharSequence = (this as java.lang.String).subSequence(beginIndex, endIndex) - public fun String.toLowerCase(locale: java.util.Locale): String = (this as java.lang.String).toLowerCase(locale) public fun String.toUpperCase(locale: java.util.Locale): String = (this as java.lang.String).toUpperCase(locale) -public fun CharSequence.subSequence(start: Int, end: Int): CharSequence? = (this as java.lang.CharSequence).subSequence(start, end) - public val CharSequence.size: Int get() = this.length @@ -138,7 +134,7 @@ public fun String.toDouble(): Double = java.lang.Double.parseDouble(this) public fun String.toCharList(): List = toCharArray().toList() -public fun CharSequence.get(start: Int, end: Int): CharSequence? = subSequence(start, end) +public fun CharSequence.get(start: Int, end: Int): CharSequence = subSequence(start, end) public fun String.toByteArray(charset: String): ByteArray = (this as java.lang.String).getBytes(charset) public fun String.toByteArray(charset: Charset = Charsets.UTF_8): ByteArray = (this as java.lang.String).getBytes(charset) @@ -156,7 +152,7 @@ public fun String.getBytes(charset: String): ByteArray = (this as java.lang.Stri * Returns a subsequence specified by given range. */ public fun CharSequence.slice(range: IntRange): CharSequence { - return subSequence(range.start, range.end + 1)!! // inclusive + return subSequence(range.start, range.end + 1) // inclusive } /**