JVM IR: specialize ExpressionCodegen.visitFunctionAccess for constructors
This makes the code a bit clearer, and gets rid of some extra type mapper invocations when generating constructor calls.
This commit is contained in:
+68
-44
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.utils.keysToMap
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
@@ -386,7 +387,7 @@ class ExpressionCodegen(
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: BlockInfo) =
|
||||
visitStatementContainer(expression, data)
|
||||
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue {
|
||||
override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it }
|
||||
|
||||
@@ -394,47 +395,14 @@ class ExpressionCodegen(
|
||||
require(callee.parent is IrClass) { "Unhandled intrinsic in ExpressionCodegen: ${callee.render()}" }
|
||||
val callable = methodSignatureMapper.mapToCallableMethod(irFunction, expression)
|
||||
val callGenerator = getOrCreateCallGenerator(expression, data, callable.signature)
|
||||
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
|
||||
val isSuspensionPoint = expression.isSuspensionPoint()
|
||||
|
||||
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||
addInlineMarker(mv, isStartNotEnd = true)
|
||||
}
|
||||
|
||||
when {
|
||||
expression is IrConstructorCall -> {
|
||||
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
|
||||
if (it.wereUsedReifiedParameters()) {
|
||||
putNeedClassReificationMarker(v)
|
||||
propagateChildReifiedTypeParametersUsages(it)
|
||||
}
|
||||
}
|
||||
|
||||
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
|
||||
// instance methods named <init>.
|
||||
markLineNumber(expression)
|
||||
mv.anew(asmType)
|
||||
mv.dup()
|
||||
}
|
||||
expression is IrDelegatingConstructorCall -> {
|
||||
expression.markLineNumber(startOffset = true)
|
||||
|
||||
// In this case the receiver is `this` (not specified in IR) and the return value is discarded anyway.
|
||||
mv.load(0, OBJECT_TYPE)
|
||||
|
||||
for (argumentIndex in 0 until expression.typeArgumentsCount) {
|
||||
val classifier = expression.getTypeArgument(argumentIndex)?.classifierOrNull
|
||||
if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) {
|
||||
consumeReifiedOperationMarker(classifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
expression.symbol.owner is IrConstructor ->
|
||||
throw AssertionError("IrCall to IrConstructor: ${expression.javaClass.simpleName}")
|
||||
}
|
||||
|
||||
expression.dispatchReceiver?.let { receiver ->
|
||||
val type = if ((expression as? IrCall)?.superQualifierSymbol != null) receiver.asmType else callable.owner
|
||||
val type = if (expression.superQualifierSymbol != null) receiver.asmType else callable.owner
|
||||
callGenerator.genValueAndPut(callee.dispatchReceiverParameter!!, receiver, type, this, data)
|
||||
}
|
||||
|
||||
@@ -458,14 +426,10 @@ class ExpressionCodegen(
|
||||
addSuspendMarker(mv, isStartNotEnd = true, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
|
||||
}
|
||||
|
||||
if (irFunction.isInvokeSuspendOfContinuation()) {
|
||||
val generatorForActualCall =
|
||||
// Do not inline callee to continuation, instead, call it
|
||||
with(callable) {
|
||||
mv.visitMethodInsn(invokeOpcode, owner.internalName, asmMethod.name, asmMethod.descriptor, isInterfaceMethod)
|
||||
}
|
||||
} else {
|
||||
callGenerator.genCall(callable, this, expression)
|
||||
}
|
||||
if (irFunction.isInvokeSuspendOfContinuation()) IrCallGenerator.DefaultCallGenerator else callGenerator
|
||||
generatorForActualCall.genCall(callable, this, expression)
|
||||
|
||||
if (isSuspensionPoint != SuspensionPointKind.NEVER) {
|
||||
addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE)
|
||||
@@ -473,8 +437,6 @@ class ExpressionCodegen(
|
||||
}
|
||||
|
||||
return when {
|
||||
expression is IrConstructorCall ->
|
||||
MaterialValue(this, asmType, expression.type)
|
||||
(expression.type.isNothing() || expression.type.isUnit()) && irFunction.shouldContainSuspendMarkers() -> {
|
||||
// NewInference allows casting `() -> T` to `() -> Unit`. A CHECKCAST here will fail.
|
||||
// Also, if the callee is a suspend function with a suspending tail call, the next `resumeWith`
|
||||
@@ -511,6 +473,68 @@ class ExpressionCodegen(
|
||||
else -> SuspensionPointKind.ALWAYS
|
||||
}
|
||||
|
||||
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: BlockInfo): PromisedValue {
|
||||
val callee = expression.symbol.owner
|
||||
val owner = typeMapper.mapClass(callee.constructedClass)
|
||||
val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee)
|
||||
|
||||
markLineNumber(expression)
|
||||
|
||||
// In this case the receiver is `this` (not specified in IR) and the return value is discarded anyway.
|
||||
mv.load(0, OBJECT_TYPE)
|
||||
|
||||
for (argumentIndex in 0 until expression.typeArgumentsCount) {
|
||||
val classifier = expression.getTypeArgument(argumentIndex)?.classifierOrNull
|
||||
if (classifier is IrTypeParameterSymbol && classifier.owner.isReified) {
|
||||
consumeReifiedOperationMarker(classifier)
|
||||
}
|
||||
}
|
||||
|
||||
generateConstructorArguments(expression, signature, data)
|
||||
markLineNumber(expression)
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false)
|
||||
|
||||
return unitValue
|
||||
}
|
||||
|
||||
override fun visitConstructorCall(expression: IrConstructorCall, data: BlockInfo): PromisedValue {
|
||||
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
|
||||
?.invoke(expression, this, data)?.let { return it }
|
||||
|
||||
val callee = expression.symbol.owner
|
||||
val owner = typeMapper.mapClass(callee.constructedClass)
|
||||
val signature = methodSignatureMapper.mapSignatureSkipGeneric(callee)
|
||||
|
||||
closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let {
|
||||
if (it.wereUsedReifiedParameters()) {
|
||||
putNeedClassReificationMarker(v)
|
||||
propagateChildReifiedTypeParametersUsages(it)
|
||||
}
|
||||
}
|
||||
|
||||
// IR constructors have no receiver and return the new instance, but on JVM they are void-returning
|
||||
// instance methods named <init>.
|
||||
markLineNumber(expression)
|
||||
mv.anew(owner)
|
||||
mv.dup()
|
||||
|
||||
generateConstructorArguments(expression, signature, data)
|
||||
markLineNumber(expression)
|
||||
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner.internalName, signature.asmMethod.name, signature.asmMethod.descriptor, false)
|
||||
|
||||
return MaterialValue(this, owner, expression.type)
|
||||
}
|
||||
|
||||
private fun generateConstructorArguments(expression: IrFunctionAccessExpression, signature: JvmMethodSignature, data: BlockInfo) {
|
||||
expression.symbol.owner.valueParameters.forEachIndexed { i, irParameter ->
|
||||
val arg = expression.getValueArgument(i)
|
||||
?: error("Null argument in ExpressionCodegen for parameter ${irParameter.render()}")
|
||||
gen(arg, signature.valueParameters[i].asmType, irParameter.type, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: BlockInfo): PromisedValue {
|
||||
val varType = typeMapper.mapType(declaration)
|
||||
val index = frameMap.enter(declaration.symbol, varType)
|
||||
|
||||
+6
-19
@@ -33,9 +33,7 @@ import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunctionBase
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBasedSimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedKotlinType
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.*
|
||||
@@ -300,26 +298,15 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
if (valueArgumentsCount > 0) (getValueArgument(0) as? IrConst<*>)?.value as? Boolean ?: true else null
|
||||
}
|
||||
|
||||
private fun IrFunctionAccessExpression.computeCalleeParent(): IrClass {
|
||||
if (this is IrCall) {
|
||||
superQualifierSymbol?.let { return it.owner }
|
||||
}
|
||||
return dispatchReceiver?.type?.classOrNull?.owner
|
||||
?: symbol.owner.parentAsClass // Static call or type parameter
|
||||
}
|
||||
|
||||
fun mapToCallableMethod(caller: IrFunction, expression: IrFunctionAccessExpression): IrCallableMethod {
|
||||
fun mapToCallableMethod(caller: IrFunction, expression: IrCall): IrCallableMethod {
|
||||
val callee = expression.symbol.owner
|
||||
val calleeParent = expression.computeCalleeParent()
|
||||
val calleeParent = expression.superQualifierSymbol?.owner
|
||||
?: expression.dispatchReceiver?.type?.classOrNull?.owner
|
||||
?: callee.parentAsClass // Static call or type parameter
|
||||
val owner = typeMapper.mapOwner(calleeParent)
|
||||
|
||||
if (callee !is IrSimpleFunction) {
|
||||
check(callee is IrConstructor) { "Function must be a simple function or a constructor: ${callee.render()}" }
|
||||
return IrCallableMethod(owner, Opcodes.INVOKESPECIAL, mapSignatureSkipGeneric(callee), false)
|
||||
}
|
||||
|
||||
val isInterface = calleeParent.isJvmInterface
|
||||
val isSuperCall = (expression as? IrCall)?.superQualifierSymbol != null
|
||||
val isSuperCall = expression.superQualifierSymbol != null
|
||||
|
||||
val invokeOpcode = when {
|
||||
callee.dispatchReceiverParameter == null -> Opcodes.INVOKESTATIC
|
||||
|
||||
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
@@ -66,9 +65,6 @@ abstract class PromisedValue(val codegen: ExpressionCodegen, val type: Type, val
|
||||
|
||||
val typeMapper: IrTypeMapper
|
||||
get() = codegen.typeMapper
|
||||
|
||||
val kotlinType: KotlinType
|
||||
get() = irType.toKotlinType()
|
||||
}
|
||||
|
||||
// A value that *has* been fully constructed.
|
||||
|
||||
Reference in New Issue
Block a user