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
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.load.java.components;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
@@ -37,7 +36,6 @@ import org.jetbrains.kotlin.resolve.jvm.kotlinSignature.SignaturesPropagationDat
import org.jetbrains.kotlin.types.JetType;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -136,9 +134,4 @@ public class TraceBasedExternalSignatureResolver implements ExternalSignatureRes
public void reportSignatureErrors(@NotNull CallableMemberDescriptor descriptor, @NotNull List<String> signatureErrors) {
trace.record(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, descriptor, signatureErrors);
}
@Override
public <D extends CallableMemberDescriptor> Collection<D> enhanceSignatures(@NotNull @ReadOnly Collection<D> platformSignatures) {
return ComponentsPackage.enhanceSignatures(platformSignatures);
}
}
@@ -1,116 +0,0 @@
/*
* 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.components
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.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaConstructorDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
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)
}
)
}
@@ -1,134 +0,0 @@
/*
* 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.components
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.load.java.components.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.components.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.components.NullabilityQualifier.NOT_NULL
import org.jetbrains.kotlin.load.java.components.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()
}
}
@@ -1,162 +0,0 @@
/*
* 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.components
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.java.components.MutabilityQualifier.MUTABLE
import org.jetbrains.kotlin.load.java.components.MutabilityQualifier.READ_ONLY
import org.jetbrains.kotlin.load.java.components.NullabilityQualifier.NOT_NULL
import org.jetbrains.kotlin.load.java.components.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)
)
}
}