Type enhancement and qualifier extraction
This commit is contained in:
committed by
Denis Zharkov
parent
e095162c19
commit
991f0fcf2e
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.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.*
|
||||
|
||||
|
||||
fun JetType.enhance(qualifiers: JavaTypeQualifiers): JetType {
|
||||
val mutabilityEnhanced =
|
||||
if (this.isFlexible())
|
||||
this.flexibility().enhanceMutability(qualifiers.mutability)
|
||||
else this
|
||||
return mutabilityEnhanced.enhanceNullability(qualifiers.nullability)
|
||||
}
|
||||
|
||||
private fun Flexibility.enhanceMutability(qualifier: MutabilityQualifier?): JetType {
|
||||
val mapping = JavaToKotlinClassMap.INSTANCE
|
||||
|
||||
val (newLower, newUpper) = run {
|
||||
when (qualifier) {
|
||||
READ_ONLY -> {
|
||||
val lowerClass = TypeUtils.getClassDescriptor(lowerBound)
|
||||
if (lowerClass != null && mapping.isMutable(lowerClass)) {
|
||||
return@run Pair(lowerBound.replaceClass(mapping.convertMutableToReadOnly(lowerClass)), upperBound)
|
||||
}
|
||||
}
|
||||
MUTABLE -> {
|
||||
val upperClass = TypeUtils.getClassDescriptor(upperBound)
|
||||
if (upperClass != null && mapping.isReadOnly(upperClass) ) {
|
||||
return@run Pair(lowerBound, upperBound.replaceClass(mapping.convertReadOnlyToMutable(upperClass)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return@run Pair(lowerBound, upperBound)
|
||||
}
|
||||
|
||||
return DelegatingFlexibleType.create(newLower, newUpper, extraCapabilities)
|
||||
}
|
||||
|
||||
private fun JetType.enhanceNullability(qualifier: NullabilityQualifier?): JetType {
|
||||
return when (qualifier) {
|
||||
NULLABLE -> TypeUtils.makeNullable(this)
|
||||
NOT_NULL -> TypeUtils.makeNotNullable(this)
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
private fun JetType.replaceClass(newClass: ClassDescriptor): JetType {
|
||||
assert(newClass.getTypeConstructor().getParameters().size() == getArguments().size(),
|
||||
{"Can't replace type constructor ${getConstructor()} by ${newClass}: type parameter count does not match"})
|
||||
return JetTypeImpl(
|
||||
getAnnotations(),
|
||||
newClass.getTypeConstructor(),
|
||||
isMarkedNullable(),
|
||||
getArguments(),
|
||||
newClass.getMemberScope(getArguments())
|
||||
)
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.flexibility
|
||||
import org.jetbrains.kotlin.types.isFlexible
|
||||
|
||||
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?
|
||||
)
|
||||
|
||||
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.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)
|
||||
)
|
||||
}
|
||||
}
|
||||
+12
@@ -29,6 +29,8 @@ import org.jetbrains.kotlin.name.FqNameUnsafe;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.*;
|
||||
@@ -186,10 +188,20 @@ public class JavaToKotlinClassMap implements PlatformToKotlinClassMap {
|
||||
return mutableToReadOnly.containsKey(mutable);
|
||||
}
|
||||
|
||||
public boolean isMutable(@NotNull JetType type) {
|
||||
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
|
||||
return classDescriptor != null && isMutable(classDescriptor);
|
||||
}
|
||||
|
||||
public boolean isReadOnly(@NotNull ClassDescriptor readOnly) {
|
||||
return readOnlyToMutable.containsKey(readOnly);
|
||||
}
|
||||
|
||||
public boolean isReadOnly(@NotNull JetType type) {
|
||||
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
|
||||
return classDescriptor != null && isReadOnly(classDescriptor);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ClassDescriptor convertMutableToReadOnly(@NotNull ClassDescriptor mutable) {
|
||||
ClassDescriptor readOnly = mutableToReadOnly.get(mutable);
|
||||
|
||||
Reference in New Issue
Block a user