Support loading methods from Java as common properties overrides
This commit is contained in:
+1
-4
@@ -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
|
||||
}
|
||||
|
||||
+10
-20
@@ -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
|
||||
|
||||
+11
-2
@@ -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);
|
||||
|
||||
+24
@@ -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
|
||||
+99
-36
@@ -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 }
|
||||
|
||||
+47
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user