org.jetbrains.kotlin.backend.jvm.lower.ApiVersionIsAtLeastEvaluationLowering.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.PhaseDescription
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.getIntConstArgumentOrNull
import org.jetbrains.kotlin.backend.jvm.lower.ApiVersionIsAtLeastEvaluationLowering.Data
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.config.MavenComparableVersion
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.irFalse
import org.jetbrains.kotlin.ir.builders.irTrue
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrBlock
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrInlinedFunctionBlock
import org.jetbrains.kotlin.ir.types.isInt
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.isTopLevelInPackage
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
@PhaseDescription(
name = "ApiVersionIsAtLeastEvaluationLowering",
description = "Evaluate inlined invocations of `apiVersionIsAtLeast`",
prerequisite = [JvmIrInliner::class]
)
internal class ApiVersionIsAtLeastEvaluationLowering(val context: JvmBackendContext) : FileLoweringPass, IrElementTransformer {
private val apiVersion = context.config.languageVersionSettings.apiVersion.version
data class Data(val currentFunction: IrFunction?, val isInsideInlinedBlock: Boolean)
override fun lower(irFile: IrFile) {
if (context.config.enableIrInliner) {
irFile.accept(this, Data(currentFunction = null, isInsideInlinedBlock = false))
}
}
override fun visitBlock(expression: IrBlock, data: Data): IrExpression {
return super.visitBlock(
expression,
data.copy(isInsideInlinedBlock = data.isInsideInlinedBlock || expression is IrInlinedFunctionBlock)
)
}
override fun visitFunction(declaration: IrFunction, data: Data): IrStatement {
return super.visitFunction(declaration, data.copy(currentFunction = declaration))
}
override fun visitCall(expression: IrCall, data: Data): IrExpression {
if (!data.isInsideInlinedBlock
|| !expression.symbol.owner.isApiVersionIsAtLeast
|| isInInlineFunInKotlinRuntime(data.currentFunction)
) {
return super.visitCall(expression, data) as IrExpression
}
val epic = expression.getIntConstArgumentOrNull(0) ?: return super.visitCall(expression, data) as IrExpression
val major = expression.getIntConstArgumentOrNull(1) ?: return super.visitCall(expression, data) as IrExpression
val minor = expression.getIntConstArgumentOrNull(2) ?: return super.visitCall(expression, data) as IrExpression
val currentFunction = data.currentFunction
require(currentFunction != null)
val builder = context.createJvmIrBuilder(currentFunction.symbol)
val versionArgument = MavenComparableVersion("$epic.$major.$minor")
return if (apiVersion >= versionArgument) builder.irTrue() else builder.irFalse()
}
private fun isInInlineFunInKotlinRuntime(currentFunction: IrFunction?): Boolean {
return currentFunction != null && currentFunction.isInline
&& currentFunction.getPackageFragment().packageFqName.startsWith(StandardNames.BUILT_INS_PACKAGE_NAME)
}
private val IrFunction.isApiVersionIsAtLeast: Boolean
get() {
return isTopLevelInPackage("apiVersionIsAtLeast", StandardNames.KOTLIN_INTERNAL_FQ_NAME)
&& valueParameters.size == 3
&& valueParameters[0].type.isInt()
&& valueParameters[1].type.isInt()
&& valueParameters[2].type.isInt()
&& dispatchReceiverParameter == null
&& extensionReceiverParameter == null
}
}