Support loading methods from Java as common properties overrides

This commit is contained in:
Denis Zharkov
2015-10-08 14:19:01 +03:00
parent 396f38e9b3
commit ad68378836
25 changed files with 628 additions and 90 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.lazy.descriptors.isJavaField
import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition
class FieldOverridabilityCondition : ExternalOverridabilityCondition {
@@ -26,8 +27,4 @@ class FieldOverridabilityCondition : ExternalOverridabilityCondition {
return subDescriptor.isJavaField == superDescriptor.isJavaField
}
// Currently getter is null iff it's loaded from Java field
private val PropertyDescriptor.isJavaField: Boolean
get() = getter == null
}
@@ -26,7 +26,6 @@ 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.utils.addToStdlib.firstNotNullResult
private val BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES = setOf(FqName("kotlin.Collection.size"), FqName("kotlin.Map.size"))
private val BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES = BUILTIN_SPECIAL_PROPERTIES_FQ_NAMES.map { it.shortName() }.toSet()
@@ -61,35 +60,26 @@ private fun CallableDescriptor.fqNameOrNull(): FqName? = fqNameUnsafe.check { it
val Name.isBuiltinSpecialPropertyName: Boolean get() = this in BUILTIN_SPECIAL_PROPERTIES_SHORT_NAMES
private val CallableDescriptor.builtinSpecialPropertyAccessorName: String?
get() = when(this) {
is PropertyAccessorDescriptor -> correspondingProperty.check { it.hasBuiltinSpecialPropertyFqName() }?.name?.asString()
else -> null
}
public val CallableMemberDescriptor.builtinSpecialPropertyAccessorName: String?
get() = propertyIfAccessor.check { it.hasBuiltinSpecialPropertyFqName() }?.name?.asString()
@Suppress("UNCHECKED_CAST")
val <T : CallableDescriptor> T.builtinSpecialOverridden: T? get() {
return when (this) {
is PropertyAccessorDescriptor -> check { correspondingProperty.hasBuiltinSpecialPropertyFqName() }
?: overriddenDescriptors.firstNotNullResult { it.builtinSpecialOverridden } as T?
is PropertyDescriptor -> check { hasBuiltinSpecialPropertyFqName() }
?: overriddenDescriptors.firstNotNullResult { it.builtinSpecialOverridden } as T?
else -> null
}
val <T : CallableMemberDescriptor> T.builtinSpecialOverridden: T? get() {
return firstOverridden { it.propertyIfAccessor.hasBuiltinSpecialPropertyFqName() } as T?
}
fun CallableDescriptor.overridesBuiltinSpecialDeclaration(): Boolean = builtinSpecialOverridden != null
fun CallableMemberDescriptor.overridesBuiltinSpecialDeclaration(): Boolean = builtinSpecialOverridden != null
public val CallableDescriptor.jvmMethodNameIfSpecial: String?
public val CallableMemberDescriptor.jvmMethodNameIfSpecial: String?
get() = builtinOverriddenThatAffectsJvmName?.builtinSpecialPropertyAccessorName
public val CallableDescriptor.builtinOverriddenThatAffectsJvmName: CallableDescriptor?
get() = if (hasBuiltinSpecialPropertyFqName() || isFromJava) builtinSpecialOverridden else null
private val CallableMemberDescriptor.builtinOverriddenThatAffectsJvmName: CallableMemberDescriptor?
get() = if (hasBuiltinSpecialPropertyFqName() || original.isFromJava) builtinSpecialOverridden else null
private val CallableDescriptor.isFromJava: Boolean
private val CallableMemberDescriptor.isFromJava: Boolean
get() = propertyIfAccessor is JavaCallableMemberDescriptor
private val CallableDescriptor.propertyIfAccessor: CallableDescriptor
private val CallableMemberDescriptor.propertyIfAccessor: CallableDescriptor
get() = if (this is PropertyAccessorDescriptor) correspondingProperty else this
val CallableMemberDescriptor.hasErasedValueParametersInJava: Boolean
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.types.JetType;
@@ -79,8 +80,16 @@ public class JavaPropertyDescriptor extends PropertyDescriptorImpl implements Ja
newGetter.initialize(enhancedReturnType);
}
assert getSetter() == null : "Field must not have a setter: " + this;
enhanced.initialize(newGetter, null);
PropertySetterDescriptorImpl newSetter = null;
PropertySetterDescriptor setter = getSetter();
if (setter != null) {
newSetter = new PropertySetterDescriptorImpl(
enhanced, setter.getAnnotations(), setter.getModality(), setter.getVisibility(),
setter.hasBody(), setter.isDefault(), getKind(), setter, SourceElement.NO_SOURCE);
newSetter.initialize(setter.getValueParameters().get(0));
}
enhanced.initialize(newGetter, newSetter);
enhanced.setSetterProjectedOut(isSetterProjectedOut());
if (compileTimeInitializer != null) {
enhanced.setCompileTimeInitializer(compileTimeInitializer);
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.load.java.lazy.descriptors
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
// Currently getter is null iff it's loaded from Java field
public val PropertyDescriptor.isJavaField: Boolean
get() = getter == null
@@ -16,10 +16,12 @@
package org.jetbrains.kotlin.load.java.lazy.descriptors
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
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
@@ -34,10 +36,7 @@ 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.structure.JavaArrayType
import org.jetbrains.kotlin.load.java.structure.JavaClass
import org.jetbrains.kotlin.load.java.structure.JavaConstructor
import org.jetbrains.kotlin.load.java.structure.JavaMethod
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
@@ -80,28 +79,63 @@ public class LazyJavaClassMemberScope(
}
override fun JavaMethodDescriptor.isVisibleAsFunction(): Boolean {
if (!canOverrideProperty) return true
return !doesOverrideAnySpecialProperty(getPropertiesFromSupertypes(name))
}
val nameAsString = name.asString()
private val JavaMethodDescriptor.canOverrideProperty: Boolean
get() = valueParameters.isEmpty() && name.isBuiltinSpecialPropertyName
private fun JavaMethodDescriptor.doesOverridesSpecialBuiltinProperty(property: PropertyDescriptor): Boolean {
if (valueParameters.isNotEmpty()) return false
// TODO: is it always false?
if (name != property.name) return false
return JetTypeChecker.DEFAULT.isSubtypeOf(returnType ?: return false, property.type)
}
private fun JavaMethodDescriptor.doesOverrideAnySpecialProperty(properties: Collection<PropertyDescriptor>): Boolean {
return properties.any overridesProperty@{
property ->
val specialOverridden = property.builtinSpecialOverridden ?: return@overridesProperty false
doesOverridesSpecialBuiltinProperty(specialOverridden)
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 (name.isBuiltinSpecialPropertyName) {
return !doesClassOverrideAnyProperty(getPropertiesFromSupertypes(name))
}
return true
}
private fun doesClassOverrideAnyProperty(properties: Collection<PropertyDescriptor>)
= properties.any { property -> doesClassOverridesProperty(property) }
private fun PropertyDescriptor.findGetterOverride(): JavaMethodDescriptor? {
val getterName = getter?.builtinSpecialOverridden?.builtinSpecialPropertyAccessorName ?: JvmAbi.getterName(name.asString())
return memberIndex().findMethodsByName(Name.identifier(getterName)).firstNotNullResult factory@{
javaMethod ->
val descriptor = resolveMethodToFunctionDescriptor(javaMethod)
if (descriptor.valueParameters.size != 0) return@factory null
descriptor.check { JetTypeChecker.DEFAULT.isSubtypeOf(descriptor.returnType ?: return@check false, type) }
}
}
private fun PropertyDescriptor.findSetterOverride(): JavaMethodDescriptor? {
return memberIndex().findMethodsByName(Name.identifier(JvmAbi.setterName(name.asString()))).firstNotNullResult factory@{
javaMethod ->
val descriptor = resolveMethodToFunctionDescriptor(javaMethod)
if (descriptor.valueParameters.size != 1) return@factory null
if (!KotlinBuiltIns.isUnit(descriptor.returnType ?: return@factory null)) return@factory null
descriptor.check { JetTypeChecker.DEFAULT.equalTypes(descriptor.valueParameters.single().type, type) }
}
}
private fun doesClassOverridesProperty(property: PropertyDescriptor): Boolean {
if (property.isJavaField) return false
val getter = property.findGetterOverride()
val setter = property.findSetterOverride()
if (getter == null) return false
if (!property.isVar) return true
return setter != null && setter.modality == getter.modality
}
override fun computeNonDeclaredFunctions(result: MutableCollection<SimpleFunctionDescriptor>, name: Name) {
@@ -122,27 +156,22 @@ public class LazyJavaClassMemberScope(
}
val propertiesFromSupertypes = getPropertiesFromSupertypes(name)
addPropertyOverrideByMethod(name, propertiesFromSupertypes, result)
addPropertyOverrideByMethod(propertiesFromSupertypes, result)
result.addAll(DescriptorResolverUtils.resolveOverrides(name, propertiesFromSupertypes, result, getContainingDeclaration(),
c.components.errorReporter))
}
private fun addPropertyOverrideByMethod(
name: Name,
propertiesFromSupertypes: Set<PropertyDescriptor>,
result: MutableCollection<PropertyDescriptor>
) {
val javaMethod = memberIndex()
.findMethodsByName(name)
.firstOrNull { it.valueParameters.isEmpty() && it.typeParameters.isEmpty() && it.visibility.isPublicAPI }
?: return
val methodDescriptor = resolveMethodToFunctionDescriptor(javaMethod)
if (methodDescriptor.doesOverrideAnySpecialProperty(propertiesFromSupertypes)) {
result.add(createPropertyDescriptorWithDefaultGetter(javaMethod, methodDescriptor.returnType, methodDescriptor.modality))
for (property in propertiesFromSupertypes) {
val newProperty = createPropertyDescriptorByMethods(property)
if (newProperty != null) {
result.add(newProperty)
break
}
}
}
@@ -172,6 +201,40 @@ public class LazyJavaClassMemberScope(
return propertyDescriptor
}
private fun createPropertyDescriptorByMethods(
overriddenProperty: PropertyDescriptor
): JavaPropertyDescriptor? {
if (!doesClassOverridesProperty(overriddenProperty)) return null
val getterMethod = overriddenProperty.findGetterOverride()!!
val setterMethod =
if (overriddenProperty.isVar)
overriddenProperty.findSetterOverride()!!
else
null
val propertyDescriptor = JavaPropertyDescriptor(
getContainingDeclaration(), Annotations.EMPTY, getterMethod.modality, getterMethod.visibility,
/* isVar = */ setterMethod != null, overriddenProperty.name, getterMethod.source,
/* original */ null,
/* isStaticFinal = */ false
)
propertyDescriptor.setType(getterMethod.returnType!!, listOf(), getDispatchReceiverParameter(), null as JetType?)
val getter = DescriptorFactory.createGetter(
propertyDescriptor, getterMethod.annotations, /* isDefault = */false, getterMethod.source
).apply {
initialize(propertyDescriptor.type)
}
val setter = setterMethod?.let { setterMethod ->
DescriptorFactory.createSetter(propertyDescriptor, setterMethod.annotations, /* isDefault = */false, setterMethod.visibility)
}
return propertyDescriptor.apply { initialize(getter, setter) }
}
private fun getPropertiesFromSupertypes(name: Name): Set<PropertyDescriptor> {
return getContainingDeclaration().typeConstructor.supertypes.flatMap {
it.memberScope.getProperties(name, NoLookupLocation.WHEN_GET_SUPER_MEMBERS).map { p -> p as PropertyDescriptor }
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.load.java
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
fun propertyNameByGetMethodName(methodName: Name): Name?
= propertyNameFromAccessorMethodName(methodName, "get") ?: propertyNameFromAccessorMethodName(methodName, "is", removePrefix = false)
fun propertyNameBySetMethodName(methodName: Name, withIsPrefix: Boolean): Name?
= propertyNameFromAccessorMethodName(methodName, "set", addPrefix = if (withIsPrefix) "is" else null)
fun propertyNamesBySetMethodName(methodName: Name)
= listOf(propertyNameBySetMethodName(methodName, false), propertyNameBySetMethodName(methodName, true)).filterNotNull()
private fun propertyNameFromAccessorMethodName(methodName: Name, prefix: String, removePrefix: Boolean = true, addPrefix: String? = null): Name? {
if (methodName.isSpecial()) return null
val identifier = methodName.getIdentifier()
if (!identifier.startsWith(prefix)) return null
if (identifier.length() == prefix.length()) return null
if (identifier[prefix.length()] in 'a'..'z') return null
if (addPrefix != null) {
assert(removePrefix)
return Name.identifier(addPrefix + identifier.removePrefix(prefix))
}
if (!removePrefix) return methodName
val name = identifier.removePrefix(prefix).decapitalizeSmart(asciiOnly = true)
if (!Name.isValidIdentifier(name)) return null
return Name.identifier(name)
}