diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/OperationsMapGenerated.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/OperationsMapGenerated.kt index b1b585f14a2..001de35eb6b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/OperationsMapGenerated.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/OperationsMapGenerated.kt @@ -98,7 +98,6 @@ internal val unaryOperations: HashMap, Pair a.toLong() }, emptyUnaryFun), unaryOperation(SHORT, "toShort", { a -> a.toShort() }, emptyUnaryFun), unaryOperation(SHORT, "toString", { a -> a.toString() }, emptyUnaryFun), - unaryOperation(STRING, "length", { a -> a.length() }, emptyUnaryFun), unaryOperation(STRING, "toString", { a -> a.toString() }, emptyUnaryFun) ) diff --git a/compiler/testData/builtin-classes.txt b/compiler/testData/builtin-classes.txt index e31740d6317..6e2a52e4d8f 100644 --- a/compiler/testData/builtin-classes.txt +++ b/compiler/testData/builtin-classes.txt @@ -262,8 +262,9 @@ public final class CharRange : kotlin.Range, kotlin.Progression(): kotlin.Int public abstract operator fun get(/*0*/ index: kotlin.Int): kotlin.Char - public abstract fun length(): kotlin.Int public abstract fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence } @@ -1247,9 +1248,10 @@ public final class ShortRange : kotlin.Range, kotlin.Progression, kotlin.CharSequence { /*primary*/ public constructor String() + public open override /*1*/ val length: kotlin.Int + public open override /*1*/ fun (): kotlin.Int public open override /*1*/ fun compareTo(/*0*/ other: kotlin.String): kotlin.Int public open override /*1*/ fun get(/*0*/ index: kotlin.Int): kotlin.Char - public open override /*1*/ fun length(): kotlin.Int public final operator fun plus(/*0*/ other: kotlin.Any?): kotlin.String public open override /*1*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence diff --git a/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java b/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java index 5694c12077b..629aa1f0437 100644 --- a/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java +++ b/compiler/testData/codegen/boxWithJava/collections/charSequence/J.java @@ -3,6 +3,7 @@ import java.util.*; public class J { public static class B extends A { + public int getLength() { return 456; } public char get(int index) { if (index == 1) return 'a'; return super.get(index); @@ -13,6 +14,10 @@ public class J { B b = new B(); CharSequence cs = (CharSequence) b; + if (cs.length() != 456) return "fail 01"; + if (b.length() != 456) return "fail 02"; + if (b.getLength() != 456) return "fail 03"; + if (cs.charAt(0) != 'z') return "fail 1"; if (b.get(0) != 'z') return "fail 2"; diff --git a/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt b/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt index 8d94d1aedbf..079c77d6f2f 100644 --- a/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt +++ b/compiler/testData/codegen/boxWithJava/collections/charSequence/charSequence.kt @@ -1,7 +1,5 @@ open class A : CharSequence { - override fun length(): Int { - throw UnsupportedOperationException() - } + override val length: Int = 123 override fun get(index: Int) = 'z'; @@ -24,5 +22,13 @@ fun box(): String { if (b.get(1) != 'a') return "fail 12" if (a.get(1) != 'z') return "fail 13" + var cs: CharSequence = a + if (a.length != 123) return "fail 14" + if (cs.length != 123) return "fail 15" + + cs = b + if (b.length != 456) return "fail 16" + if (b.length != 456) return "fail 17" + return J.foo(); } diff --git a/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt b/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt index d19bbb152fc..e2ae3918dd2 100644 --- a/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt +++ b/compiler/testData/diagnostics/tests/cast/neverSucceeds/MappedSubtypes.txt @@ -14,10 +14,10 @@ public final class JSub : java.lang.CharSequence { public final class Sub : kotlin.CharSequence { public constructor Sub() + public abstract override /*1*/ /*fake_override*/ val length: kotlin.Int public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): kotlin.Char 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/collectionOverrides/getCharSequence.kt b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.kt index 7d025c439ac..40c49d09a1a 100644 --- a/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.kt +++ b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.kt @@ -1,17 +1,20 @@ // FILE: A.java abstract public class A implements CharSequence { public char charAt(int x) { } + public int length() { return 1; } } // FILE: C.java abstract public class C implements CharSequence { public char get(int x) { } + public int length() { return 1; } } // FILE: main.kt abstract class B : A(), CharSequence { override operator fun get(index: Int) = '1' + override val length: Int get() = 1 } fun main(a: A, b: B, c: C) { @@ -22,4 +25,6 @@ fun main(a: A, b: B, c: C) { a.get(0) b.get(0) c.get(0) + + a.length + b.length + c.length } diff --git a/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.txt b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.txt index 602cf8d2133..91dfbecd225 100644 --- a/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.txt +++ b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/getCharSequence.txt @@ -4,30 +4,30 @@ public fun main(/*0*/ a: A, /*1*/ b: B, /*2*/ c: C): kotlin.Unit public abstract class A : kotlin.CharSequence { public constructor A() + public open override /*1*/ val length: kotlin.Int public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ fun get(/*0*/ x: kotlin.Int): kotlin.Char 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 } public abstract class B : A, kotlin.CharSequence { public constructor B() + public open override /*2*/ val length: kotlin.Int public open override /*2*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*2*/ fun get(/*0*/ index: kotlin.Int): kotlin.Char public open override /*2*/ /*fake_override*/ fun hashCode(): kotlin.Int - public abstract override /*2*/ /*fake_override*/ fun length(): kotlin.Int public abstract override /*2*/ /*fake_override*/ fun subSequence(/*0*/ start: kotlin.Int, /*1*/ end: kotlin.Int): kotlin.CharSequence public open override /*2*/ /*fake_override*/ fun toString(): kotlin.String } public abstract class C : kotlin.CharSequence { public constructor C() + public open override /*1*/ val length: kotlin.Int public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ fun get(/*0*/ index: kotlin.Int): kotlin.Char 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/loadJava/compiledKotlin/annotations/types/SupertypesAndBounds.txt b/compiler/testData/loadJava/compiledKotlin/annotations/types/SupertypesAndBounds.txt index 414bf1625fe..45ac2ced2b0 100644 --- a/compiler/testData/loadJava/compiledKotlin/annotations/types/SupertypesAndBounds.txt +++ b/compiler/testData/loadJava/compiledKotlin/annotations/types/SupertypesAndBounds.txt @@ -5,8 +5,9 @@ package test } public interface Foo : @test.A() kotlin.CharSequence { + public abstract override /*1*/ /*fake_override*/ val length: kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun (): kotlin.Int public abstract fun bar(): kotlin.Unit public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): kotlin.Char - 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 } diff --git a/core/builtins/native/kotlin/CharSequence.kt b/core/builtins/native/kotlin/CharSequence.kt index 93002c27ae7..1067493c180 100644 --- a/core/builtins/native/kotlin/CharSequence.kt +++ b/core/builtins/native/kotlin/CharSequence.kt @@ -23,7 +23,7 @@ public interface CharSequence { /** * Returns the length of this character sequence. */ - public fun length(): Int + public val length: Int /** * Returns the character at the specified [index] in the sequence. diff --git a/core/builtins/native/kotlin/String.kt b/core/builtins/native/kotlin/String.kt index eea83aaa82b..e5370dba54c 100644 --- a/core/builtins/native/kotlin/String.kt +++ b/core/builtins/native/kotlin/String.kt @@ -28,7 +28,7 @@ public class String : Comparable, CharSequence { */ public operator fun plus(other: Any?): String - public override fun length(): Int + public override val length: Int public override fun get(index: Int): Char diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt index 69a6396909b..cd3b938a24b 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/descriptors/LazyJavaClassMemberScope.kt @@ -25,8 +25,7 @@ import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.incremental.components.LookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.load.java.* -import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.isBuiltinSpecialPropertyName -import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyAccessorName +import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName import org.jetbrains.kotlin.load.java.BuiltinSpecialMethods.sameAsRenamedInJvmBuiltin import org.jetbrains.kotlin.load.java.BuiltinSpecialMethods.isRemoveAtByIndex import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialJvmSignature.sameAsBuiltinMethodWithErasedValueParameters @@ -86,25 +85,13 @@ public class LazyJavaClassMemberScope( } override fun JavaMethodDescriptor.isVisibleAsFunction(): Boolean { - val nameAsString = name.asString() - - if (JvmAbi.isGetterName(nameAsString)) { - val propertyName = propertyNameByGetMethodName(name) ?: return true - return !doesClassOverrideAnyProperty(getPropertiesFromSupertypes(propertyName)) - } - - if (JvmAbi.isSetterName(nameAsString)) { - return propertyNamesBySetMethodName(name).none { - propertyName -> - getPropertiesFromSupertypes(propertyName).any { - property -> property.isVar && doesClassOverridesProperty(property) - } + if (getPropertyNamesCandidatesByAccessorName(name).any { + propertyName -> + getPropertiesFromSupertypes(propertyName).any { + property -> + doesClassOverridesProperty(property) && (property.isVar || !JvmAbi.isSetterName(name.asString())) } - } - - if (name.isBuiltinSpecialPropertyName) { - return !doesClassOverrideAnyProperty(getPropertiesFromSupertypes(name)) - } + }) return false val javaMethod = (source as? JavaSourceElement)?.javaElement as? JavaMethod if (javaMethod?.doesOverrideRenamedBuiltins() ?: false) { @@ -136,11 +123,8 @@ public class LazyJavaClassMemberScope( ).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE } - private fun doesClassOverrideAnyProperty(properties: Collection) - = properties.any { property -> doesClassOverridesProperty(property) } - private fun PropertyDescriptor.findGetterOverride(): JavaMethodDescriptor? { - val getterName = getter?.getBuiltinSpecialOverridden()?.getBuiltinSpecialPropertyAccessorName() ?: JvmAbi.getterName(name.asString()) + val getterName = getter?.getBuiltinSpecialOverridden()?.getBuiltinSpecialPropertyGetterName() ?: JvmAbi.getterName(name.asString()) return memberIndex().findMethodsByName(Name.identifier(getterName)).firstNotNullResult factory@{ javaMethod -> val descriptor = resolveMethodToFunctionDescriptor(javaMethod) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt index f4ac50d1f92..fdc9abe0471 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/propertiesConventionUtil.kt @@ -18,6 +18,9 @@ package org.jetbrains.kotlin.load.java import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart +import org.jetbrains.kotlin.utils.singletonOrEmptyList +import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getPropertyNameCandidatesBySpecialGetterName + fun propertyNameByGetMethodName(methodName: Name): Name? = propertyNameFromAccessorMethodName(methodName, "get") ?: propertyNameFromAccessorMethodName(methodName, "is", removePrefix = false) @@ -45,3 +48,17 @@ private fun propertyNameFromAccessorMethodName(methodName: Name, prefix: String, if (!Name.isValidIdentifier(name)) return null return Name.identifier(name) } + +fun getPropertyNamesCandidatesByAccessorName(name: Name): List { + val nameAsString = name.asString() + + if (JvmAbi.isGetterName(nameAsString)) { + return propertyNameByGetMethodName(name).singletonOrEmptyList() + } + + if (JvmAbi.isSetterName(nameAsString)) { + return propertyNamesBySetMethodName(name) + } + + return getPropertyNameCandidatesBySpecialGetterName(name) +} \ No newline at end of file diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt index 50466fad37c..9b9205ccd00 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/specialBuiltinMembers.kt @@ -28,10 +28,20 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.addToStdlib.check -import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyAccessorName +import org.jetbrains.kotlin.load.java.BuiltinSpecialProperties.getBuiltinSpecialPropertyGetterName +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe object BuiltinSpecialProperties { - private val FQ_NAMES = setOf(FqName("kotlin.Collection.size"), FqName("kotlin.Map.size")) + private val PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP = mapOf( + FqName("kotlin.Collection.size") to Name.identifier("size"), + FqName("kotlin.Map.size") to Name.identifier("size"), + FqName("kotlin.CharSequence.length") to Name.identifier("length") + ) + + private val GETTER_JVM_NAME_TO_PROPERTIES_SHORT_NAME_MAP: Map> = + PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.getInversedShortNamesMap() + + private val FQ_NAMES = PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP.keySet() private val SHORT_NAMES = FQ_NAMES.map { it.shortName() }.toSet() fun hasBuiltinSpecialPropertyFqName(callableMemberDescriptor: CallableMemberDescriptor): Boolean { @@ -47,12 +57,14 @@ object BuiltinSpecialProperties { return overriddenDescriptors.any { hasBuiltinSpecialPropertyFqName(it) } } - val Name.isBuiltinSpecialPropertyName: Boolean get() = this in SHORT_NAMES + fun getPropertyNameCandidatesBySpecialGetterName(name1: Name): List = + GETTER_JVM_NAME_TO_PROPERTIES_SHORT_NAME_MAP[name1] ?: emptyList() - fun CallableMemberDescriptor.getBuiltinSpecialPropertyAccessorName(): String? { - return propertyIfAccessor.check { - hasBuiltinSpecialPropertyFqName(it) - }?.name?.asString() + fun CallableMemberDescriptor.getBuiltinSpecialPropertyGetterName(): String? { + assert(isFromBuiltins()) { "This method is defined only for builtin members, but $this found" } + + val descriptor = propertyIfAccessor.firstOverridden { hasBuiltinSpecialPropertyFqName(it) } ?: return null + return PROPERTY_FQ_NAME_TO_JVM_GETTER_NAME_MAP[descriptor.fqNameSafe]?.asString() } } @@ -115,11 +127,7 @@ object BuiltinSpecialMethods { val ORIGINAL_SHORT_NAMES: List = FQ_NAMES_TO_JVM_MAP.keySet().map { it.shortName() } - private val JVM_SHORT_NAME_TO_BUILTIN_FQ_NAMES_MAP: Map> = - FQ_NAMES_TO_JVM_MAP.entrySet().groupBy { it.value }.mapValues { entry -> entry.value.map { it.key } } - - val JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP: Map> = - JVM_SHORT_NAME_TO_BUILTIN_FQ_NAMES_MAP.mapValues { it.value.map { it.shortName() } } + val JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP: Map> = FQ_NAMES_TO_JVM_MAP.getInversedShortNamesMap() val Name.sameAsRenamedInJvmBuiltin: Boolean get() = this in ORIGINAL_SHORT_NAMES @@ -157,7 +165,7 @@ fun getJvmMethodNameIfSpecial(callableMemberDescriptor: CallableMemberDescriptor val builtinOverridden = getBuiltinOverriddenThatAffectsJvmName(callableMemberDescriptor)?.propertyIfAccessor ?: return null return when (builtinOverridden) { - is PropertyDescriptor -> builtinOverridden.getBuiltinSpecialPropertyAccessorName() + is PropertyDescriptor -> builtinOverridden.getBuiltinSpecialPropertyGetterName() else -> BuiltinSpecialMethods.getSpecialJvmName(builtinOverridden)?.asString() } } @@ -209,3 +217,6 @@ private fun CallableMemberDescriptor.firstOverridden( } private fun CallableMemberDescriptor.isFromJavaOrBuiltins() = isFromJava || isFromBuiltins() + +private fun Map.getInversedShortNamesMap(): Map> = + entrySet().groupBy { it.value }.mapValues { entry -> entry.value.map { it.key.shortName() } } \ No newline at end of file diff --git a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt index 870f840ad2e..80e3a1e3bd0 100644 --- a/libraries/stdlib/src/kotlin/collections/MutableCollections.kt +++ b/libraries/stdlib/src/kotlin/collections/MutableCollections.kt @@ -31,6 +31,9 @@ public fun MutableList.remove(index: Int): E = removeAt(index) @Deprecated("Use explicit cast to MutableCollection instead", ReplaceWith("(this as MutableCollection).remove(o)")) public fun MutableCollection.remove(o: Any?): Boolean = remove(o as E) +@Deprecated("Use 'length' property instead", ReplaceWith("this.length")) +public fun CharSequence.length(): Int = length + /** * Adds the specified [element] to this mutable collection. */