Move type enhacement parts to separate package in runtime

It's needed to enhace types when loading descriptors via reflection.
Also get rid of `enhanceSignatures` method in ExternalSignatureResolver as enhancement does not use external signature at all
This commit is contained in:
Denis Zharkov
2015-06-19 15:47:07 +03:00
parent c6b91b0f81
commit c769748cb0
7 changed files with 21 additions and 41 deletions
@@ -18,14 +18,12 @@ package org.jetbrains.kotlin.load.java.components;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.load.java.structure.JavaField;
import org.jetbrains.kotlin.load.java.structure.JavaMember;
import org.jetbrains.kotlin.load.java.structure.JavaMethod;
import org.jetbrains.kotlin.types.JetType;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -75,11 +73,6 @@ public interface ExternalSignatureResolver {
public void reportSignatureErrors(@NotNull CallableMemberDescriptor descriptor, @NotNull List<String> signatureErrors) {
throw new UnsupportedOperationException("Should not be called");
}
@Override
public <D extends CallableMemberDescriptor> Collection<D> enhanceSignatures(@NotNull @ReadOnly Collection<D> platformSignatures) {
return platformSignatures;
}
};
abstract class MemberSignature {
@@ -208,10 +201,4 @@ public interface ExternalSignatureResolver {
);
void reportSignatureErrors(@NotNull CallableMemberDescriptor descriptor, @NotNull List<String> signatureErrors);
/**
* Replaces some items in the {@code platformSignatures} collection with enhanced signatures.
* Is called after binding overrides, so the implementation is allowed to inspect contents of getOverriddenDescriptors()
*/
<D extends CallableMemberDescriptor> Collection<D> enhanceSignatures(@NotNull @ReadOnly Collection<D> platformSignatures);
}
@@ -35,16 +35,15 @@ 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
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.emptyOrSingletonList
import org.jetbrains.kotlin.utils.ifEmpty
import org.jetbrains.kotlin.utils.valuesToMap
import org.jetbrains.kotlin.utils.*
import java.util.ArrayList
import java.util.Collections
import java.util.LinkedHashSet
@@ -72,7 +71,7 @@ public class LazyJavaClassMemberScope(
result.addIfNotNull(c.samConversionResolver.resolveSamAdapter(descriptor))
}
c.externalSignatureResolver.enhanceSignatures(
enhanceSignatures(
result ifEmpty { emptyOrSingletonList(createDefaultConstructor()) }
).toReadOnlyList()
}
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.load.java.structure.JavaArrayType
import org.jetbrains.kotlin.load.java.structure.JavaField
import org.jetbrains.kotlin.load.java.structure.JavaMethod
import org.jetbrains.kotlin.load.java.structure.JavaValueParameter
import org.jetbrains.kotlin.load.java.typeEnhacement.enhanceSignatures
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.jvm.PLATFORM_TYPES
@@ -86,7 +87,7 @@ public abstract class LazyJavaMemberScope(
computeNonDeclaredFunctions(result, name)
val enhancedResult = c.externalSignatureResolver.enhanceSignatures(result)
val enhancedResult = enhanceSignatures(result)
// Make sure that lazy things are computed before we release the lock
for (f in enhancedResult) {
@@ -239,7 +240,7 @@ public abstract class LazyJavaMemberScope(
if (DescriptorUtils.isAnnotationClass(containingDeclaration))
properties.toReadOnlyList()
else
c.externalSignatureResolver.enhanceSignatures(properties).toReadOnlyList()
enhanceSignatures(properties).toReadOnlyList()
}
private fun resolveProperty(field: JavaField): PropertyDescriptor {
@@ -0,0 +1,116 @@
/*
* 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.typeEnhacement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.load.java.typeEnhacement.computeIndexedQualifiersForOverride
import org.jetbrains.kotlin.load.java.typeEnhacement.enhance
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.types.JetType
fun <D : CallableMemberDescriptor> enhanceSignatures(platformSignatures: Collection<D>): Collection<D> {
return platformSignatures.map {
it.enhance()
}
}
fun <D : CallableMemberDescriptor> D.enhance(): D {
// TODO type parameters
// TODO use new type parameters while enhancing other types
// TODO Propagation into generic type arguments
val enhancedReceiverType =
if (getExtensionReceiverParameter() != null)
parts { it.getExtensionReceiverParameter()!!.toPart() }.enhance()
else null
val enhancedValueParameters = getValueParameters().map {
p -> parts { it.getValueParameters()[p.getIndex()]!!.toPart() }.enhance()
}
val enhancedReturnType = parts { it.getReturnType()!!.toReturnTypePart() }.enhance()
if (this is JavaCallableMemberDescriptor) {
@suppress("UNCHECKED_CAST")
return this.enhance(enhancedReceiverType, enhancedValueParameters, enhancedReturnType) as D
}
return this
}
fun <T, P : SignaturePart<T>> SignatureParts<T, P>.enhance(): T {
val qualifiers = fromOverride.type.computeIndexedQualifiersForOverride(this.fromOverridden.map { it.type }, fromOverride.isCovariant)
return fromOverride.replaceType(fromOverride.type.enhance(qualifiers))
}
class SignatureParts<T, P: SignaturePart<T>>(
val fromOverride: P,
val fromOverridden: Collection<P>
)
interface SignaturePart<out T> {
val isCovariant: Boolean
get() = false
val type: JetType
fun replaceType(newType: JetType): T
}
fun ReceiverParameterDescriptor.toPart() = object : SignaturePart<JetType> {
override val type = this@toPart.getType() // workaround for KT-7557
override fun replaceType(newType: JetType) = newType
}
fun ValueParameterDescriptor.toPart() = object : SignaturePart<ValueParameterDescriptor> {
override val type = this@toPart.getType() // workaround for KT-7557
override fun replaceType(newType: JetType) = ValueParameterDescriptorImpl(
getContainingDeclaration(),
null,
getIndex(),
getAnnotations(),
getName(),
newType,
declaresDefaultValue(),
if (getVarargElementType() != null) KotlinBuiltIns.getInstance().getArrayElementType(newType) else null,
getSource()
)
}
fun JetType.toReturnTypePart() = object : SignaturePart<JetType> {
override val type = this@toReturnTypePart
override val isCovariant: Boolean = true
override fun replaceType(newType: JetType) = newType
}
fun <D : CallableMemberDescriptor, T, P : SignaturePart<T>> D.parts(collector: (D) -> P): SignatureParts<T, P> {
return SignatureParts(
collector(this),
this.getOverriddenDescriptors().map {
@suppress("UNCHECKED_CAST")
collector(it as D)
}
)
}
@@ -0,0 +1,134 @@
/*
* 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.typeEnhacement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.load.java.typeEnhacement.JavaTypeQualifiers
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NOT_NULL
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NULLABLE
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.types.*
// The index in the lambda is the position of the type component:
// Example: for `A<B, C<D, E>>`, indices go as follows: `0 - A<...>, 1 - B, 2 - C<D, E>, 3 - D, 4 - E`,
// which corresponds to the left-to-right breadth-first walk of the tree representation of the type.
// For flexible types, both bounds are indexed in the same way: `(A<B>..C<D>)` gives `0 - (A<B>..C<D>), 1 - B and D`.
fun JetType.enhance(qualifiers: (Int) -> JavaTypeQualifiers) = this.enhancePossiblyFlexible(qualifiers, 0).type
private enum class TypeComponentPosition {
FLEXIBLE_LOWER,
FLEXIBLE_UPPER,
INFLEXIBLE
}
data class Result(val type: JetType, val subtreeSize: Int)
private fun JetType.enhancePossiblyFlexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int): Result {
if (this.isError()) return Result(this, 1)
return if (this.isFlexible()) {
with(this.flexibility()) {
val lowerResult = lowerBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_LOWER)
val upperResult = upperBound.enhanceInflexible(qualifiers, index, TypeComponentPosition.FLEXIBLE_UPPER)
assert(lowerResult.subtreeSize == upperResult.subtreeSize) {
"Different tree sizes of bounds: " +
"lower = ($lowerBound, ${lowerResult.subtreeSize}), " +
"upper = ($upperBound, ${upperResult.subtreeSize})"
}
Result(
DelegatingFlexibleType.create(lowerResult.type, upperResult.type, extraCapabilities), lowerResult.subtreeSize
)
}
}
else this.enhanceInflexible(qualifiers, index, TypeComponentPosition.INFLEXIBLE)
}
private fun JetType.enhanceInflexible(qualifiers: (Int) -> JavaTypeQualifiers, index: Int, position: TypeComponentPosition): Result {
val shouldEnhance = position.shouldEnhance()
if (!shouldEnhance && getArguments().isEmpty()) return Result(this, 1)
val originalClass = getConstructor().getDeclarationDescriptor()
?: return Result(this, 1)
val effectiveQualifiers = qualifiers(index)
val enhancedClassifier = originalClass.enhanceMutability(effectiveQualifiers, position)
var globalArgIndex = index + 1
val enhancedArguments = getArguments().mapIndexed {
localArgIndex, arg ->
if (arg.isStarProjection()) {
globalArgIndex++
TypeUtils.makeStarProjection(enhancedClassifier.getTypeConstructor().getParameters()[localArgIndex])
}
else {
val (enhancedType, subtreeSize) = arg.getType().enhancePossiblyFlexible(qualifiers, globalArgIndex)
globalArgIndex += subtreeSize
TypeProjectionImpl(
arg.getProjectionKind(),
enhancedType
)
}
}
val enhancedType = JetTypeImpl(
getAnnotations(),
enhancedClassifier.getTypeConstructor(),
this.getEnhancedNullability(effectiveQualifiers, position),
enhancedArguments,
if (enhancedClassifier is ClassDescriptor)
enhancedClassifier.getMemberScope(enhancedArguments)
else enhancedClassifier.getDefaultType().getMemberScope()
)
return Result(enhancedType, globalArgIndex - index)
}
private fun TypeComponentPosition.shouldEnhance() = this != TypeComponentPosition.INFLEXIBLE
private fun ClassifierDescriptor.enhanceMutability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): ClassifierDescriptor {
if (!position.shouldEnhance()) return this
if (this !is ClassDescriptor) return this // mutability is not applicable for type parameters
val mapping = JavaToKotlinClassMap.INSTANCE
when (qualifiers.mutability) {
READ_ONLY -> {
if (position == TypeComponentPosition.FLEXIBLE_LOWER && mapping.isMutable(this)) {
return mapping.convertMutableToReadOnly(this)
}
}
MUTABLE -> {
if (position == TypeComponentPosition.FLEXIBLE_UPPER && mapping.isReadOnly(this) ) {
return mapping.convertReadOnlyToMutable(this)
}
}
}
return this
}
private fun JetType.getEnhancedNullability(qualifiers: JavaTypeQualifiers, position: TypeComponentPosition): Boolean {
if (!position.shouldEnhance()) return this.isMarkedNullable()
return when (qualifiers.nullability) {
NULLABLE -> true
NOT_NULL -> false
else -> this.isMarkedNullable()
}
}
@@ -0,0 +1,162 @@
/*
* 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.typeEnhacement
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.typeEnhacement.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NOT_NULL
import org.jetbrains.kotlin.load.java.typeEnhacement.NullabilityQualifier.NULLABLE
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.types.flexibility
import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.utils.getOrDefault
import java.util.ArrayList
enum class NullabilityQualifier {
NULLABLE,
NOT_NULL
}
val NULLABLE_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_NULLABLE_ANNOTATION
)
val NOT_NULL_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_NOT_NULL_ANNOTATION
)
enum class MutabilityQualifier {
READ_ONLY,
MUTABLE
}
val READ_ONLY_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_READONLY_ANNOTATION
)
val MUTABLE_ANNOTATIONS = listOf(
JvmAnnotationNames.JETBRAINS_MUTABLE_ANNOTATION
)
class JavaTypeQualifiers(
val nullability: NullabilityQualifier?,
val mutability: MutabilityQualifier?
) {
companion object {
val NONE = JavaTypeQualifiers(null, null)
}
}
private fun JetType.extractQualifiers(): JavaTypeQualifiers {
val (lower, upper) =
if (this.isFlexible())
flexibility().let { Pair(it.lowerBound, it.upperBound) }
else Pair(this, this)
val mapping = JavaToKotlinClassMap.INSTANCE
return JavaTypeQualifiers(
if (lower.isMarkedNullable()) NULLABLE else if (!upper.isMarkedNullable()) NOT_NULL else null,
if (mapping.isReadOnly(lower)) READ_ONLY else if (mapping.isMutable(upper)) MUTABLE else null
)
}
private fun Annotations.extractQualifiers(): JavaTypeQualifiers {
fun <T: Any> List<FqName>.ifPresent(qualifier: T) = if (any { findAnnotation(it) != null}) qualifier else null
fun <T: Any> singleNotNull(x: T?, y: T?) = if (x == null || y == null) x ?: y else null
return JavaTypeQualifiers(
singleNotNull(NULLABLE_ANNOTATIONS.ifPresent(NULLABLE), NOT_NULL_ANNOTATIONS.ifPresent(NOT_NULL)),
singleNotNull(READ_ONLY_ANNOTATIONS.ifPresent(READ_ONLY), MUTABLE_ANNOTATIONS.ifPresent(MUTABLE))
)
}
fun JetType.computeIndexedQualifiersForOverride(fromSupertypes: Collection<JetType>, isCovariant: Boolean): (Int) -> JavaTypeQualifiers {
fun JetType.toIndexed(): List<JetType> {
val list = ArrayList<JetType>(1)
fun add(type: JetType) {
list.add(type)
for (arg in type.getArguments()) {
if (arg.isStarProjection()) {
list.add(arg.getType())
}
else {
add(arg.getType())
}
}
}
add(this)
return list
}
val indexedFromSupertypes = fromSupertypes.map { it.toIndexed() }
val indexedThisType = this.toIndexed()
// The covariant case may be hard, e.g. in the superclass the return may be Super<T>, but in the subclass it may be Derived, which
// is declared to extend Super<T>, and propagating data here is highly non-trivial, so we only look at the head type constructor
// (outermost type), unless the type in the subclass is interchangeable with the all the types in superclasses:
// e.g. we have (Mutable)List<String!>! in the subclass and { List<String!>, (Mutable)List<String>! } from superclasses
// Note that `this` is flexible here, so it's equal to it's bounds
val onlyHeadTypeConstructor = isCovariant && fromSupertypes.any { !JetTypeChecker.DEFAULT.equalTypes(it, this) }
return fun(index: Int): JavaTypeQualifiers {
val isHeadTypeConstructor = index == 0
if (!isHeadTypeConstructor && onlyHeadTypeConstructor) return JavaTypeQualifiers.NONE
val qualifiers = indexedThisType.getOrDefault(index, { return JavaTypeQualifiers.NONE })
val verticalSlice = indexedFromSupertypes.map { it.getOrDefault(index, { null }) }.filterNotNull()
// Only the head type constructor is safely co-variant
return qualifiers.computeQualifiersForOverride(verticalSlice, isCovariant && isHeadTypeConstructor)
}
}
private fun JetType.computeQualifiersForOverride(fromSupertypes: Collection<JetType>, isCovariant: Boolean): JavaTypeQualifiers {
val nullabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().nullability }.filterNotNull().toSet()
val mutabilityFromSupertypes = fromSupertypes.map { it.extractQualifiers().mutability }.filterNotNull().toSet()
val own = getAnnotations().extractQualifiers()
if (isCovariant) {
fun <T : Any> Set<T>.selectCovariantly(low: T, high: T, own: T?): T? {
val supertypeQualifier = if (low in this) low else if (high in this) high else null
return if (supertypeQualifier == low && own == high) null else own ?: supertypeQualifier
}
return JavaTypeQualifiers(
nullabilityFromSupertypes.selectCovariantly(NOT_NULL, NULLABLE, own.nullability),
mutabilityFromSupertypes.selectCovariantly(MUTABLE, READ_ONLY, own.mutability)
)
}
else {
fun <T : Any> Set<T>.selectInvariantly(own: T?): T? {
val effectiveSet = own?.let { (this + own).toSet() } ?: this
// if this set contains exactly one element, it is the qualifier everybody agrees upon,
// otherwise (no qualifiers, or multiple qualifiers), there's no single such qualifier
// and all qualifiers are discarded
return effectiveSet.singleOrNull()
}
return JavaTypeQualifiers(
nullabilityFromSupertypes.selectInvariantly(own.nullability),
mutabilityFromSupertypes.selectInvariantly(own.mutability)
)
}
}