Support loading builtin methods from Java with different names

This commit is contained in:
Denis Zharkov
2015-10-10 18:47:18 +03:00
parent 3f5498e9f5
commit 742a538aed
11 changed files with 413 additions and 16 deletions
@@ -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?
@@ -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 }
@@ -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)
@@ -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()
}
}