Support loading builtin methods from Java with different names
This commit is contained in:
+67
-13
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.java
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
@@ -32,7 +33,9 @@ private val BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES = BUILTIN_SPECIAL_PROPERTIES_
|
||||
|
||||
|
||||
private val BUILTIN_METHODS_ERASED_COLLECTION_PARAMETER_FQ_NAMES = setOf(FqName("kotlin.Collection.containsAll"))
|
||||
private val BUILTIN_METHODS_GENERIC_PARAMETERS_FQ_NAMES = setOf(FqName("kotlin.Collection.contains"))
|
||||
private val BUILTIN_METHODS_GENERIC_PARAMETERS_FQ_NAMES = setOf(
|
||||
FqName("kotlin.Collection.contains"), FqName("kotlin.MutableCollection.remove")
|
||||
)
|
||||
|
||||
private val BUILTIN_METHODS_ERASED_VALUE_PARAMETERS_FQ_NAMES =
|
||||
BUILTIN_METHODS_GENERIC_PARAMETERS_FQ_NAMES + BUILTIN_METHODS_ERASED_COLLECTION_PARAMETER_FQ_NAMES
|
||||
@@ -40,20 +43,40 @@ private val BUILTIN_METHODS_ERASED_VALUE_PARAMETERS_FQ_NAMES =
|
||||
private val BUILTIN_METHODS_ERASED_VALUE_PARAMETERS_SHORT_NAMES =
|
||||
BUILTIN_METHODS_ERASED_VALUE_PARAMETERS_FQ_NAMES.map { it.shortName() }.toSet()
|
||||
|
||||
public fun CallableDescriptor.hasBuiltinSpecialPropertyFqName(): Boolean {
|
||||
if (this is PropertyAccessorDescriptor) return correspondingProperty.hasBuiltinSpecialPropertyFqName()
|
||||
private object BuiltinSpecialMethods {
|
||||
val REMOVE_AT_FQ_NAME = FqName("kotlin.MutableList.removeAt")
|
||||
|
||||
val FQ_NAMES_TO_JVM_MAP: Map<FqName, Name> = mapOf(
|
||||
REMOVE_AT_FQ_NAME to Name.identifier("remove"),
|
||||
FqName("kotlin.CharSequence.get") to Name.identifier("charAt")
|
||||
)
|
||||
|
||||
val ORIGINAL_SHORT_NAMES: List<Name> = FQ_NAMES_TO_JVM_MAP.keySet().map { it.shortName() }
|
||||
|
||||
private val JVM_SHORT_NAME_TO_BUILTIN_FQ_NAMES_MAP: Map<Name, List<FqName>> =
|
||||
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<Name, List<Name>> =
|
||||
JVM_SHORT_NAME_TO_BUILTIN_FQ_NAMES_MAP.mapValues { it.value.map { it.shortName() } }
|
||||
}
|
||||
|
||||
public fun CallableMemberDescriptor.hasBuiltinSpecialPropertyFqName(): Boolean {
|
||||
if (name !in BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES) return false
|
||||
|
||||
return hasBuiltinSpecialPropertyFqNameImpl()
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean {
|
||||
private fun CallableMemberDescriptor.hasBuiltinSpecialPropertyFqNameImpl(): Boolean {
|
||||
if (fqNameOrNull() in BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES) return true
|
||||
|
||||
if (!fqNameUnsafe.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return false
|
||||
if (builtIns.builtInsModule != module) return false
|
||||
if (!isFromBuiltins()) return false
|
||||
|
||||
return overriddenDescriptors.any(CallableDescriptor::hasBuiltinSpecialPropertyFqName)
|
||||
return overriddenDescriptors.any(CallableMemberDescriptor::hasBuiltinSpecialPropertyFqName)
|
||||
}
|
||||
|
||||
public fun CallableMemberDescriptor.isFromBuiltins(): Boolean {
|
||||
if (!(propertyIfAccessor.fqNameOrNull()?.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME) ?: false)) return false
|
||||
return builtIns.builtInsModule == module
|
||||
}
|
||||
|
||||
private fun CallableDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.check { it.isSafe }?.toSafe()
|
||||
@@ -66,28 +89,59 @@ public fun CallableMemberDescriptor.getBuiltinSpecialPropertyAccessorName(): Str
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : CallableMemberDescriptor> T.getBuiltinSpecialOverridden(): T? {
|
||||
return firstOverridden { it.propertyIfAccessor.hasBuiltinSpecialPropertyFqName() } as T?
|
||||
return when (this) {
|
||||
is PropertyDescriptor, is PropertyAccessorDescriptor ->
|
||||
firstOverridden { it.propertyIfAccessor.hasBuiltinSpecialPropertyFqName() } as T?
|
||||
else -> firstOverridden { it.isBuiltinFunctionWithDifferentNameInJvm() } as T?
|
||||
}
|
||||
}
|
||||
|
||||
private fun CallableMemberDescriptor.isBuiltinFunctionWithDifferentNameInJvm(): Boolean {
|
||||
if (!isFromBuiltins()) return false
|
||||
val fqName = fqNameOrNull() ?: return false
|
||||
return firstOverridden { BuiltinSpecialMethods.FQ_NAMES_TO_JVM_MAP.containsKey(fqName) } != null
|
||||
}
|
||||
|
||||
fun CallableMemberDescriptor.overridesBuiltinSpecialDeclaration(): Boolean = getBuiltinSpecialOverridden() != null
|
||||
|
||||
public fun CallableMemberDescriptor.getJvmMethodNameIfSpecial(): String? {
|
||||
return getBuiltinOverriddenThatAffectsJvmName()?.getBuiltinSpecialPropertyAccessorName()
|
||||
val builtinOverridden = getBuiltinOverriddenThatAffectsJvmName()?.propertyIfAccessor ?: return null
|
||||
return when (builtinOverridden) {
|
||||
is PropertyDescriptor -> builtinOverridden.getBuiltinSpecialPropertyAccessorName()
|
||||
else -> builtinOverridden.specialJvmName()?.asString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CallableMemberDescriptor.getBuiltinOverriddenThatAffectsJvmName(): CallableMemberDescriptor? {
|
||||
return if (hasBuiltinSpecialPropertyFqName() || original.isFromJava) getBuiltinSpecialOverridden() else null
|
||||
val overriddenBuiltin = getBuiltinSpecialOverridden() ?: return null
|
||||
|
||||
if (isFromJava || isFromBuiltins()) return overriddenBuiltin
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private val CallableMemberDescriptor.isFromJava: Boolean
|
||||
get() = propertyIfAccessor is JavaCallableMemberDescriptor
|
||||
private fun CallableMemberDescriptor.specialJvmName(): Name? {
|
||||
return BuiltinSpecialMethods.FQ_NAMES_TO_JVM_MAP[fqNameOrNull() ?: return null]
|
||||
}
|
||||
|
||||
private val CallableMemberDescriptor.propertyIfAccessor: CallableDescriptor
|
||||
public fun getSpecialBuiltinFunctionsByJvmName(name: Name): List<Name> =
|
||||
BuiltinSpecialMethods.JVM_SHORT_NAME_TO_BUILTIN_SHORT_NAMES_MAP[name] ?: emptyList()
|
||||
|
||||
public val CallableMemberDescriptor.isRemoveAtByIndex: Boolean
|
||||
get() = name.asString() == "removeAt" && fqNameOrNull() == BuiltinSpecialMethods.REMOVE_AT_FQ_NAME
|
||||
|
||||
private val CallableMemberDescriptor.isFromJava: Boolean
|
||||
get() = propertyIfAccessor is JavaCallableMemberDescriptor && propertyIfAccessor.containingDeclaration is JavaClassDescriptor
|
||||
|
||||
private val CallableMemberDescriptor.propertyIfAccessor: CallableMemberDescriptor
|
||||
get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this
|
||||
|
||||
val CallableMemberDescriptor.hasErasedValueParametersInJava: Boolean
|
||||
get() = fqNameOrNull() in BUILTIN_METHODS_ERASED_VALUE_PARAMETERS_FQ_NAMES
|
||||
|
||||
val Name.sameAsRenamedInJvmBuiltin: Boolean
|
||||
get() = this in BuiltinSpecialMethods.ORIGINAL_SHORT_NAMES
|
||||
|
||||
fun FunctionDescriptor.getOverriddenBuiltinFunctionWithErasedValueParametersInJava(): FunctionDescriptor? {
|
||||
if (!name.sameAsBuiltinMethodWithErasedValueParameters) return null
|
||||
return firstOverridden { it.hasErasedValueParametersInJava } as FunctionDescriptor?
|
||||
|
||||
+56
-1
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ConstructorDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.LookupLocation
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -36,12 +35,15 @@ import org.jetbrains.kotlin.load.java.lazy.child
|
||||
import org.jetbrains.kotlin.load.java.lazy.resolveAnnotations
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.RawSubstitution
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.toAttributes
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.typeEnhacement.enhanceSignatures
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.serialization.deserialization.ErrorReporter
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker
|
||||
@@ -99,9 +101,36 @@ public class LazyJavaClassMemberScope(
|
||||
return !doesClassOverrideAnyProperty(getPropertiesFromSupertypes(name))
|
||||
}
|
||||
|
||||
val javaMethod = (source as? JavaSourceElement)?.javaElement as? JavaMethod
|
||||
if (javaMethod?.doesOverrideRenamedBuiltins() ?: false) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun JavaMethod.doesOverrideRenamedBuiltins(): Boolean {
|
||||
return getSpecialBuiltinFunctionsByJvmName(name).any {
|
||||
builtinName ->
|
||||
val builtinSpecialFromSuperTypes =
|
||||
getFunctionsFromSupertypes(builtinName).filter { it.overridesBuiltinSpecialDeclaration() }
|
||||
if (builtinSpecialFromSuperTypes.isEmpty()) return@any false
|
||||
|
||||
val methodDescriptor = resolveMethodToFunctionDescriptorWithName(this, builtinName)
|
||||
|
||||
builtinSpecialFromSuperTypes.any { isOverridableRenamedDescriptor(it, methodDescriptor) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOverridableRenamedDescriptor(superDescriptor: FunctionDescriptor, subDescriptor: FunctionDescriptor): Boolean {
|
||||
// if we check 'removeAt', get original sub-descriptor to distinct `remove(int)` and `remove(E)` in Java
|
||||
val subDescriptorToCheck = if (superDescriptor.isRemoveAtByIndex) subDescriptor.original else subDescriptor
|
||||
|
||||
return OverridingUtil.DEFAULT.isOverridableByIncludingReturnType(
|
||||
superDescriptor, subDescriptorToCheck
|
||||
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
}
|
||||
|
||||
private fun doesClassOverrideAnyProperty(properties: Collection<PropertyDescriptor>)
|
||||
= properties.any { property -> doesClassOverridesProperty(property) }
|
||||
|
||||
@@ -141,9 +170,35 @@ public class LazyJavaClassMemberScope(
|
||||
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
|
||||
val functionsFromSupertypes = getFunctionsFromSupertypes(name)
|
||||
|
||||
if (name.sameAsRenamedInJvmBuiltin) {
|
||||
addOverriddenBuiltinMethods(result, name, functionsFromSupertypes)
|
||||
}
|
||||
|
||||
result.addAll(DescriptorResolverUtils.resolveOverrides(name, functionsFromSupertypes, result, getContainingDeclaration(), c.components.errorReporter))
|
||||
}
|
||||
|
||||
private fun addOverriddenBuiltinMethods(
|
||||
result: MutableCollection<SimpleFunctionDescriptor>,
|
||||
name: Name,
|
||||
functionsFromSupertypes: Set<SimpleFunctionDescriptor>
|
||||
) {
|
||||
// Merge functions with same signatures
|
||||
val mergedFunctionFromSuperTypes = DescriptorResolverUtils.resolveOverrides(
|
||||
name, functionsFromSupertypes, emptyList(), getContainingDeclaration(), ErrorReporter.DO_NOTHING)
|
||||
|
||||
for (descriptor in mergedFunctionFromSuperTypes) {
|
||||
val overriddenBuiltin = descriptor.getBuiltinSpecialOverridden() ?: continue
|
||||
val nameInJava = overriddenBuiltin.getJvmMethodNameIfSpecial()!!
|
||||
for (method in memberIndex().findMethodsByName(Name.identifier(nameInJava))) {
|
||||
val renamedCopy = resolveMethodToFunctionDescriptorWithName(method, name)
|
||||
|
||||
if (isOverridableRenamedDescriptor(overriddenBuiltin, renamedCopy)) {
|
||||
result.add(renamedCopy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFunctionsFromSupertypes(name: Name): Set<SimpleFunctionDescriptor> {
|
||||
return getContainingDeclaration().typeConstructor.supertypes.flatMap {
|
||||
it.memberScope.getFunctions(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS).map { f -> f as SimpleFunctionDescriptor }
|
||||
|
||||
+5
-1
@@ -110,9 +110,13 @@ public abstract class LazyJavaScope(
|
||||
valueParameters: ResolvedValueParameters): MethodSignatureData
|
||||
|
||||
open fun resolveMethodToFunctionDescriptor(method: JavaMethod): JavaMethodDescriptor {
|
||||
return resolveMethodToFunctionDescriptorWithName(method, method.name)
|
||||
}
|
||||
|
||||
protected fun resolveMethodToFunctionDescriptorWithName(method: JavaMethod, name: Name): JavaMethodDescriptor {
|
||||
val annotations = c.resolveAnnotations(method)
|
||||
val functionDescriptorImpl = JavaMethodDescriptor.createJavaMethod(
|
||||
containingDeclaration, annotations, method.getName(), c.components.sourceElementFactory.source(method)
|
||||
containingDeclaration, annotations, name, c.components.sourceElementFactory.source(method)
|
||||
)
|
||||
|
||||
val c = c.child(functionDescriptorImpl, method)
|
||||
|
||||
+13
-1
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.java.lazy.descriptors
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.load.java.structure.*
|
||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.utils.valuesToMap
|
||||
import java.util.HashSet
|
||||
|
||||
@@ -38,6 +39,11 @@ object EMPTY_MEMBER_INDEX : MemberIndex {
|
||||
override fun getAllFieldNames() = listOf<Name>()
|
||||
}
|
||||
|
||||
private val ADDITIONAL_MEMBER_NAMES_MAP = mapOf(
|
||||
FqName("java.util.List") to listOf(Name.identifier("removeAt")),
|
||||
FqName("java.lang.CharSequence") to listOf(Name.identifier("get"))
|
||||
)
|
||||
|
||||
open class ClassMemberIndex(val jClass: JavaClass, val memberFilter: (JavaMember) -> Boolean) : MemberIndex {
|
||||
private val methodFilter = {
|
||||
m: JavaMethod ->
|
||||
@@ -48,12 +54,17 @@ open class ClassMemberIndex(val jClass: JavaClass, val memberFilter: (JavaMember
|
||||
private val fields = jClass.getFields().asSequence().filter(memberFilter).valuesToMap { m -> m.getName() }
|
||||
|
||||
override fun findMethodsByName(name: Name): Collection<JavaMethod> = methods[name] ?: listOf()
|
||||
override fun getMethodNames(nameFilter: (Name) -> Boolean): Collection<Name> = jClass.getAllMemberNames(methodFilter) { getMethods() }
|
||||
override fun getMethodNames(nameFilter: (Name) -> Boolean): Collection<Name> =
|
||||
jClass.getAllMemberNames(methodFilter) { getMethods() }
|
||||
|
||||
override fun findFieldByName(name: Name): JavaField? = fields[name]
|
||||
override fun getAllFieldNames(): Collection<Name> = jClass.getAllMemberNames(memberFilter) { getFields() }
|
||||
}
|
||||
|
||||
private fun JavaClass.getNonDeclaredMethodNames(): List<Name> {
|
||||
return ADDITIONAL_MEMBER_NAMES_MAP[this.fqName].orEmpty()
|
||||
}
|
||||
|
||||
private fun <M : JavaMember> JavaClass.getAllMemberNames(filter: (M) -> Boolean, getMembers: JavaClass.() -> Collection<M>): Set<Name> {
|
||||
val result = HashSet<Name>()
|
||||
val visitedSuperClasses = HashSet<JavaClass>()
|
||||
@@ -70,6 +81,7 @@ private fun <M : JavaMember> JavaClass.getAllMemberNames(filter: (M) -> Boolean,
|
||||
for (supertype in getSupertypes()) {
|
||||
val classifier = supertype.getClassifier()
|
||||
if (classifier is JavaClass) {
|
||||
result.addAll(classifier.getNonDeclaredMethodNames())
|
||||
classifier.visit()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user