From 253a901bd3ddaf7ed87fcdd84cbf745db3dc3945 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Mon, 28 Nov 2016 07:13:47 +0300 Subject: [PATCH] Evolve DSL for defining enhanced nullability info for known JDK functions. Enhance nullability for java.util.Optional. --- .../predefinedEnhancementInfo.kt | 143 ++++++++++++++++-- .../load/kotlin/methodSignatureBuilding.kt | 8 + 2 files changed, 137 insertions(+), 14 deletions(-) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/predefinedEnhancementInfo.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/predefinedEnhancementInfo.kt index a51c60ec9a6..d7a617f6cda 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/predefinedEnhancementInfo.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhancement/predefinedEnhancementInfo.kt @@ -16,7 +16,10 @@ package org.jetbrains.kotlin.load.java.typeEnhancement +import org.jetbrains.kotlin.load.kotlin.SignatureBuildingComponents import org.jetbrains.kotlin.load.kotlin.signatures +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType.BOOLEAN class TypeEnhancementInfo(val map: Map) { constructor(vararg pairs: Pair) : this(mapOf(*pairs)) @@ -27,20 +30,132 @@ class PredefinedFunctionEnhancementInfo( val parametersInfo: List = emptyList() ) -private val NOT_NULLABLE = JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, isNotNullTypeParameter = false) +/** Type is always nullable: `T?` */ +private val NULLABLE = JavaTypeQualifiers(NullabilityQualifier.NULLABLE, null, isNotNullTypeParameter = false) +/** Nullability depends on substitution, but the type is not platform: `T` */ +private val NOT_PLATFORM = JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, isNotNullTypeParameter = false) +/** Type is always non-nullable: `T & Any` */ +private val NOT_NULLABLE = JavaTypeQualifiers(NullabilityQualifier.NOT_NULL, null, isNotNullTypeParameter = true) val PREDEFINED_FUNCTION_ENHANCEMENT_INFO_BY_SIGNATURE = signatures { - mapOf( - signature(javaUtil("Iterator"), "forEachRemaining(Ljava/util/function/Consumer;)V") to - PredefinedFunctionEnhancementInfo( - parametersInfo = listOf( - TypeEnhancementInfo( - 0 to NOT_NULLABLE, - 1 to NOT_NULLABLE - ) - ) - ), - signature("java/util/function/Consumer", "accept(Ljava/lang/Object;)V") to - PredefinedFunctionEnhancementInfo(parametersInfo = listOf(TypeEnhancementInfo(0 to NOT_NULLABLE))) - ) + val JLObject = javaLang("Object") + val JFPredicate = javaFunction("Predicate") + val JFConsumer = javaFunction("Consumer") + val JFBiFunction = javaFunction("BiFunction") + val JFFunction = javaFunction("Function") + val JUOptional = javaUtil("Optional") + + enhancement { + forClass(javaUtil("Iterator")) { + function("forEachRemaining") { + parameter(JFConsumer, NOT_PLATFORM, NOT_PLATFORM) + } + } + forClass(javaUtil("Collection")) { + function("removeIf") { + parameter(JFPredicate, NOT_PLATFORM, NOT_PLATFORM) + returns(BOOLEAN) + } + } + forClass(javaUtil("Map")) { + function("merge") { + parameter(JLObject, NOT_PLATFORM) + parameter(JLObject, NOT_NULLABLE) + parameter(JFBiFunction, NOT_PLATFORM, NOT_NULLABLE, NOT_NULLABLE, NULLABLE) + returns(JLObject, NULLABLE) + } + } + forClass(JFConsumer) { + function("accept") { + parameter(JLObject, NOT_PLATFORM) + } + } + forClass(JFPredicate) { + function("test") { + parameter(JLObject, NOT_PLATFORM) + returns(BOOLEAN) + } + } + forClass(JFFunction) { + function("apply") { + parameter(JLObject, NOT_PLATFORM) + returns(JLObject, NOT_PLATFORM) + } + } + forClass(JFBiFunction) { + function("apply") { + parameter(JLObject, NOT_PLATFORM) + parameter(JLObject, NOT_PLATFORM) + returns(JLObject, NOT_PLATFORM) + } + } + forClass(JUOptional) { + function("empty") { + returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE) + } + function("of") { + parameter(JLObject, NOT_NULLABLE) + returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE) + } + function("ofNullable") { + parameter(JLObject, NULLABLE) + returns(JUOptional, NOT_PLATFORM, NOT_NULLABLE) + } + function("get") { + returns(JLObject, NOT_NULLABLE) + } + function("ifPresent") { + parameter(JFConsumer, NOT_PLATFORM, NOT_NULLABLE) + } + } + } } + + +private inline fun enhancement(block: SignatureEnhancementBuilder.() -> Unit): Map + = SignatureEnhancementBuilder().apply(block).build() + +private class SignatureEnhancementBuilder { + private val signatures = mutableMapOf() + + inline fun forClass(internalName: String, block: ClassEnhancementBuilder.() -> Unit) = + ClassEnhancementBuilder(internalName).block() + + inner class ClassEnhancementBuilder(val className: String) { + fun function(name: String, block: FunctionEnhancementBuilder.() -> Unit) { + signatures += FunctionEnhancementBuilder(name).apply(block).build() + } + + inner class FunctionEnhancementBuilder(val functionName: String) { + private val parameters = mutableListOf>() + private var returnType: Pair = "V" to null + + fun parameter(type: String, vararg pairs: Pair) { + parameters += type to + if (pairs.isEmpty()) null else TypeEnhancementInfo(*pairs) + } + fun parameter(type: String, vararg qualifiers: JavaTypeQualifiers) { + parameters += type to + if (qualifiers.isEmpty()) null else TypeEnhancementInfo(qualifiers.withIndex().associateBy({it.index}, {it.value})) + } + fun returns(type: String, vararg pairs: Pair) { + returnType = type to TypeEnhancementInfo(*pairs) + } + fun returns(type: String, vararg qualifiers: JavaTypeQualifiers) { + returnType = type to TypeEnhancementInfo(qualifiers.withIndex().associateBy({it.index}, {it.value})) + } + fun returns(type: JvmPrimitiveType) { + returnType = type.desc to null + } + fun build() = with (SignatureBuildingComponents) { + signature(className, jvmDescriptor(functionName, parameters.map { it.first }, returnType.first)) to + PredefinedFunctionEnhancementInfo(returnType.second, parameters.map { it.second }) + } + } + + } + + fun build(): Map = signatures +} + + diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureBuilding.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureBuilding.kt index 0830cfc8282..99008bc5c71 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureBuilding.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/methodSignatureBuilding.kt @@ -25,6 +25,7 @@ inline fun signatures(block: SignatureBuildingComponents.() -> T) = with(Sig object SignatureBuildingComponents { fun javaLang(name: String) = "java/lang/$name" fun javaUtil(name: String) = "java/util/$name" + fun javaFunction(name: String) = "java/util/function/$name" fun constructors(vararg signatures: String) = signatures.map { "($it)V" }.toTypedArray() @@ -36,4 +37,11 @@ object SignatureBuildingComponents { fun signature(classDescriptor: ClassDescriptor, jvmDescriptor: String) = signature(classDescriptor.internalName, jvmDescriptor) fun signature(classId: ClassId, jvmDescriptor: String) = signature(classId.internalName, jvmDescriptor) fun signature(internalName: String, jvmDescriptor: String) = internalName + "." + jvmDescriptor + + fun jvmDescriptor(name: String, vararg parameters: String, ret: String = "V") = + jvmDescriptor(name, parameters.asList(), ret) + fun jvmDescriptor(name: String, parameters: List, ret: String = "V") = + "$name(${parameters.joinToString("") { escapeClassName(it) }})${escapeClassName(internalName = ret)}" + + private fun escapeClassName(internalName: String) = if (internalName.length > 1) "L$internalName;" else internalName }