Rewrite generator for OperationsMapGenerated

Do not generate operations as lambdas; instead use `when` over
strings/enums, which is generated to tableswitch in the bytecode.

This reduces the proguarded compiler jar size by ~0.57%.

 #KT-23565 Fixed
This commit is contained in:
Alexander Udalov
2021-01-08 18:00:04 +01:00
parent df75cddcb8
commit 742fef9042
3 changed files with 740 additions and 509 deletions
+97 -88
View File
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.Printer
import java.io.File
@@ -27,29 +27,30 @@ fun generate(): String {
val sb = StringBuilder()
val p = Printer(sb)
p.println(File("license/COPYRIGHT.txt").readText())
p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\")")
p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\", \"NON_EXHAUSTIVE_WHEN\")")
p.println()
p.println("package org.jetbrains.kotlin.resolve.constants.evaluate")
p.println()
p.println("import org.jetbrains.kotlin.resolve.constants.evaluate.CompileTimeType.*")
p.println("import java.math.BigInteger")
p.println("import java.util.HashMap")
p.println()
p.println("/** This file is generated by org.jetbrains.kotlin.generators.evaluate:generate(). DO NOT MODIFY MANUALLY */")
p.println("/** This file is generated by `./gradlew generateOperationsMap`. DO NOT MODIFY MANUALLY */")
p.println()
val unaryOperationsMap = arrayListOf<Triple<String, List<KotlinType>, Boolean>>()
val binaryOperationsMap = arrayListOf<Pair<String, List<KotlinType>>>()
val builtIns = DefaultBuiltIns.Instance
@Suppress("UNCHECKED_CAST")
val allPrimitiveTypes = builtIns.builtInsPackageScope.getContributedDescriptors()
.filter { it is ClassDescriptor && KotlinBuiltIns.isPrimitiveType(it.defaultType) } as List<ClassDescriptor>
.filter { it is ClassDescriptor && KotlinBuiltIns.isPrimitiveType(it.defaultType) } as List<ClassDescriptor>
for (descriptor in allPrimitiveTypes + builtIns.string) {
@Suppress("UNCHECKED_CAST")
val functions = descriptor.getMemberScope(listOf()).getContributedDescriptors()
.filter { it is CallableDescriptor && !EXCLUDED_FUNCTIONS.contains(it.getName().asString()) } as List<CallableDescriptor>
.filter { it is CallableDescriptor && !EXCLUDED_FUNCTIONS.contains(it.getName().asString()) } as List<CallableDescriptor>
for (function in functions) {
val parametersTypes = function.getParametersTypes()
@@ -57,108 +58,116 @@ fun generate(): String {
when (parametersTypes.size) {
1 -> unaryOperationsMap.add(Triple(function.name.asString(), parametersTypes, function is FunctionDescriptor))
2 -> binaryOperationsMap.add(function.name.asString() to parametersTypes)
else -> throw IllegalStateException("Couldn't add following method from builtins to operations map: ${function.name} in class ${descriptor.name}")
else -> throw IllegalStateException(
"Couldn't add following method from builtins to operations map: ${function.name} in class ${descriptor.name}"
)
}
}
}
p.println("internal val emptyBinaryFun: Function2<BigInteger, BigInteger, BigInteger> = { _, _ -> BigInteger(\"0\") }")
p.println("internal val emptyUnaryFun: Function1<Long, Long> = { _ -> 1.toLong() }")
p.println()
p.println("internal val unaryOperations: HashMap<UnaryOperationKey<*>, Pair<Function1<Any?, Any>, Function1<Long, Long>>>")
p.println(" = hashMapOf<UnaryOperationKey<*>, Pair<Function1<Any?, Any>, Function1<Long, Long>>>(")
p.println("internal fun evalUnaryOp(name: String, type: CompileTimeType, value: Any): Any? {")
p.pushIndent()
val unaryOperationsMapIterator = unaryOperationsMap.iterator()
while (unaryOperationsMapIterator.hasNext()) {
val (funcName, parameters, isFunction) = unaryOperationsMapIterator.next()
val parenthesesOrBlank = if (isFunction) "()" else ""
p.println(
"unaryOperation(",
parameters.map { it.asString() }.joinToString(", "),
", ",
"\"$funcName\"",
", { a -> a.$funcName$parenthesesOrBlank }, ",
renderCheckUnaryOperation(funcName, parameters),
")",
if (unaryOperationsMapIterator.hasNext()) "," else ""
)
p.println("when (type) {")
p.pushIndent()
for ((type, operations) in unaryOperationsMap.groupBy { (_, parameters, _) -> parameters.single() }) {
p.println("${type.asString()} -> when (name) {")
p.pushIndent()
for ((name, _, isFunction) in operations) {
val parenthesesOrBlank = if (isFunction) "()" else ""
p.println("\"$name\" -> return (value as ${type.typeName}).$name$parenthesesOrBlank")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println(")")
p.println("}")
p.println("return null")
p.popIndent()
p.println("}")
p.println()
p.println()
p.println("internal val binaryOperations: HashMap<BinaryOperationKey<*, *>, Pair<Function2<Any?, Any?, Any>, Function2<BigInteger, BigInteger, BigInteger>>>")
p.println(" = hashMapOf<BinaryOperationKey<*, *>, Pair<Function2<Any?, Any?, Any>, Function2<BigInteger, BigInteger, BigInteger>>>(")
p.println("internal fun evalBinaryOp(name: String, leftType: CompileTimeType, left: Any, rightType: CompileTimeType, right: Any): Any? {")
p.pushIndent()
val binaryOperationsMapIterator = binaryOperationsMap.iterator()
while (binaryOperationsMapIterator.hasNext()) {
val (funcName, parameters) = binaryOperationsMapIterator.next()
p.println(
"binaryOperation(",
parameters.map { it.asString() }.joinToString(", "),
", ",
"\"$funcName\"",
", { a, b -> a.$funcName(b) }, ",
renderCheckBinaryOperation(funcName, parameters),
")",
if (binaryOperationsMapIterator.hasNext()) "," else ""
)
p.println("when (leftType) {")
p.pushIndent()
for ((leftType, operationsOnThisLeftType) in binaryOperationsMap.groupBy { (_, parameters) -> parameters.first() }) {
p.println("${leftType.asString()} -> when (rightType) {")
p.pushIndent()
for ((rightType, operations) in operationsOnThisLeftType.groupBy { (_, parameters) -> parameters[1] }) {
p.println("${rightType.asString()} -> when (name) {")
p.pushIndent()
for ((name, _) in operations) {
val castToRightType = if (rightType.typeName == "Any") "" else " as ${rightType.typeName}"
p.println("\"$name\" -> return (left as ${leftType.typeName}).$name(right$castToRightType)")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println(")")
p.println("}")
p.println("return null")
p.popIndent()
p.println("}")
p.println()
p.println("internal fun checkBinaryOp(")
p.println(" name: String, leftType: CompileTimeType, left: BigInteger, rightType: CompileTimeType, right: BigInteger")
p.println("): BigInteger? {")
p.pushIndent()
p.println("when (leftType) {")
p.pushIndent()
val checkedBinaryOperations =
binaryOperationsMap.filter { (name, parameters) -> getBinaryCheckerName(name, parameters[0], parameters[1]) != null }
for ((leftType, operationsOnThisLeftType) in checkedBinaryOperations.groupBy { (_, parameters) -> parameters.first() }) {
p.println("${leftType.asString()} -> when (rightType) {")
p.pushIndent()
for ((rightType, operations) in operationsOnThisLeftType.groupBy { (_, parameters) -> parameters[1] }) {
p.println("${rightType.asString()} -> when (name) {")
p.pushIndent()
for ((name, _) in operations) {
val checkerName = getBinaryCheckerName(name, leftType, rightType)!!
p.println("\"$name\" -> return left.$checkerName(right)")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
p.println("return null")
p.popIndent()
p.println("}")
return sb.toString()
}
fun renderCheckUnaryOperation(name: String, params: List<KotlinType>): String {
val isAllParamsIntegers = params.fold(true) { a, b -> a && b.isIntegerType() }
if (!isAllParamsIntegers) {
return "emptyUnaryFun"
}
private fun getBinaryCheckerName(name: String, leftType: KotlinType, rightType: KotlinType): String? {
if (!leftType.isIntegerType() || !rightType.isIntegerType()) return null
return when(name) {
"unaryMinus", "minus" -> "{ a -> a.$name() }"
else -> "emptyUnaryFun"
return when (name) {
"plus" -> "add"
"minus" -> "subtract"
"div" -> "divide"
"times" -> "multiply"
"mod", "rem", "xor", "or", "and" -> name
else -> null
}
}
fun renderCheckBinaryOperation(name: String, params: List<KotlinType>): String {
val isAllParamsIntegers = params.fold(true) { a, b -> a && b.isIntegerType() }
if (!isAllParamsIntegers) {
return "emptyBinaryFun"
}
private fun KotlinType.isIntegerType(): Boolean =
KotlinBuiltIns.isInt(this) || KotlinBuiltIns.isShort(this) || KotlinBuiltIns.isByte(this) || KotlinBuiltIns.isLong(this)
return when(name) {
"plus" -> "{ a, b -> a.add(b) }"
"minus" -> "{ a, b -> a.subtract(b) }"
"div" -> "{ a, b -> a.divide(b) }"
"times" -> "{ a, b -> a.multiply(b) }"
"mod",
"rem",
"xor",
"or",
"and" -> "{ a, b -> a.$name(b) }"
else -> "emptyBinaryFun"
}
}
private fun CallableDescriptor.getParametersTypes(): List<KotlinType> =
listOf((containingDeclaration as ClassDescriptor).defaultType) +
valueParameters.map { it.type.makeNotNullable() }
private fun KotlinType.isIntegerType(): Boolean {
return KotlinBuiltIns.isInt(this) ||
KotlinBuiltIns.isShort(this) ||
KotlinBuiltIns.isByte(this) ||
KotlinBuiltIns.isLong(this)
}
private fun KotlinType.asString(): String = typeName.toUpperCase()
private fun CallableDescriptor.getParametersTypes(): List<KotlinType> {
val list = arrayListOf<KotlinType>((containingDeclaration as ClassDescriptor).defaultType)
valueParameters.map { it.type }.forEach {
list.add(TypeUtils.makeNotNullable(it))
}
return list
}
private fun KotlinType.asString(): String = constructor.declarationDescriptor!!.name.asString().toUpperCase()
private val KotlinType.typeName: String
get(): String = constructor.declarationDescriptor!!.name.asString()