JVM: fix inlining of default lambdas of signature (Result) -> Any

They have no `invoke` bridge, and the overridden invoke expectes a boxed
`Result` as an argument.
This commit is contained in:
pyos
2021-06-07 11:55:51 +02:00
committed by max-kammerer
parent a0a14d9e25
commit 392e4fba42
15 changed files with 104 additions and 55 deletions
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.Label
@@ -128,13 +127,14 @@ abstract class DefaultLambda(
loadClassBytesByInternalName(sourceCompiler.state, lambdaClassType.internalName)
}
protected fun loadInvoke(sourceCompiler: SourceCompilerForInline, invokeMethod: Method) {
// Returns whether the loaded invoke is erased, i.e. the name equals the fallback and all types are `Object`.
protected fun loadInvoke(sourceCompiler: SourceCompilerForInline, erasedName: String, actualMethod: Method): Boolean {
val classBytes = loadClass(sourceCompiler)
val invokeNameFallback = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
// TODO: `signatureAmbiguity = true` ignores the argument types from `invokeMethod` and only looks at the count.
node = getMethodNode(classBytes, invokeMethod.name, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
?: getMethodNode(classBytes, invokeNameFallback, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
?: error("Can't find method '$invokeMethod' in '${lambdaClassType.internalName}'")
node = getMethodNode(classBytes, actualMethod.name, actualMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
?: getMethodNode(classBytes, erasedName, actualMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
?: error("Can't find method '$actualMethod' in '${lambdaClassType.internalName}'")
return invokeMethod.run { name == erasedName && returnType == OBJECT_TYPE && argumentTypes.all { it == OBJECT_TYPE } }
}
private companion object {
@@ -13,9 +13,6 @@ import org.jetbrains.kotlin.codegen.binding.CalculatedClosure
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.config.isReleaseCoroutines
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.*
@@ -170,9 +167,7 @@ class PsiInlineCodegen(
JvmCodegenUtil.getBoundCallableReferenceReceiver(resolvedCall)
} else null
val lambda = PsiExpressionLambda(
ktLambda!!, state.typeMapper, state.languageVersionSettings, parameter.isCrossinline, boundReceiver != null
)
val lambda = PsiExpressionLambda(ktLambda!!, state, parameter.isCrossinline, boundReceiver != null)
rememberClosure(type, parameter.index, lambda)
if (boundReceiver != null) {
// Has to be done immediately to preserve evaluation order.
@@ -225,8 +220,7 @@ private val FunctionDescriptor.explicitParameters
class PsiExpressionLambda(
expression: KtExpression,
private val typeMapper: KotlinTypeMapper,
private val languageVersionSettings: LanguageVersionSettings,
private val state: GenerationState,
isCrossInline: Boolean,
override val isBoundCallableReference: Boolean
) : ExpressionLambda(isCrossInline) {
@@ -239,9 +233,7 @@ class PsiExpressionLambda(
override val invokeMethodParameters: List<KotlinType?>
get() {
val actualInvokeDescriptor = if (isSuspend)
getOrCreateJvmSuspendFunctionView(
invokeMethodDescriptor, languageVersionSettings.isReleaseCoroutines(), typeMapper.bindingContext
)
getOrCreateJvmSuspendFunctionView(invokeMethodDescriptor, state)
else
invokeMethodDescriptor
return actualInvokeDescriptor.explicitParameters.map { it.returnType }
@@ -263,7 +255,7 @@ class PsiExpressionLambda(
val closure: CalculatedClosure
init {
val bindingContext = typeMapper.bindingContext
val bindingContext = state.bindingContext
val function = bindingContext.get(BindingContext.FUNCTION, functionWithBodyOrCallableReference)
if (function == null && expression is KtCallableReferenceExpression) {
val variableDescriptor =
@@ -271,7 +263,7 @@ class PsiExpressionLambda(
?: throw AssertionError("Reference expression not resolved to variable descriptor with accessors: ${expression.getText()}")
classDescriptor = bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, variableDescriptor)
?: throw IllegalStateException("Class for callable not found: $variableDescriptor\n${expression.text}")
lambdaClassType = typeMapper.mapClass(classDescriptor)
lambdaClassType = state.typeMapper.mapClass(classDescriptor)
val getFunction = PropertyReferenceCodegen.findGetFunction(variableDescriptor)
invokeMethodDescriptor = PropertyReferenceCodegen.createFakeOpenDescriptor(getFunction, classDescriptor)
val resolvedCall = expression.callableReference.getResolvedCallWithAssert(bindingContext)
@@ -287,7 +279,7 @@ class PsiExpressionLambda(
closure = bindingContext.get(CodegenBinding.CLOSURE, classDescriptor)
?: throw AssertionError("null closure for lambda ${expression.text}")
returnLabels = getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null }
invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor)
invokeMethod = state.typeMapper.mapAsmMethod(invokeMethodDescriptor)
isSuspend = invokeMethodDescriptor.isSuspend
}
@@ -296,13 +288,13 @@ class PsiExpressionLambda(
arrayListOf<CapturedParamDesc>().apply {
val captureThis = closure.capturedOuterClassDescriptor
if (captureThis != null) {
add(capturedParamDesc(AsmUtil.CAPTURED_THIS_FIELD, typeMapper.mapType(captureThis.defaultType), isSuspend = false))
add(capturedParamDesc(AsmUtil.CAPTURED_THIS_FIELD, state.typeMapper.mapType(captureThis.defaultType), isSuspend = false))
}
val capturedReceiver = closure.capturedReceiverFromOuterContext
if (capturedReceiver != null) {
val fieldName = closure.getCapturedReceiverFieldName(typeMapper.bindingContext, languageVersionSettings)
val type = typeMapper.mapType(capturedReceiver).let {
val fieldName = closure.getCapturedReceiverFieldName(state.typeMapper.bindingContext, state.languageVersionSettings)
val type = state.typeMapper.mapType(capturedReceiver).let {
if (isBoundCallableReference) AsmUtil.boxType(it) else it
}
add(capturedParamDesc(fieldName, type, isSuspend = false))
@@ -336,23 +328,14 @@ class PsiDefaultLambda(
get() = invokeMethodDescriptor.returnType
init {
val substitutedDescriptor = parameterDescriptor.type.memberScope
val name = if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE
val descriptor = parameterDescriptor.type.memberScope
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
.single()
invokeMethodDescriptor = when {
// Property references: `(A) -> B` => `get(Any?): Any?`
isPropertyReference -> substitutedDescriptor.original
// Suspend function references: `suspend (A) -> B` => `invoke(A, Continuation<B>): Any?`
// TODO: default suspend lambdas are currently uninlinable
parameterDescriptor.type.isSuspendFunctionType ->
getOrCreateJvmSuspendFunctionView(
substitutedDescriptor,
sourceCompiler.state.languageVersionSettings.isReleaseCoroutines(),
sourceCompiler.state.bindingContext
)
// Non-suspend function references and lambdas: `(A) -> B` => `invoke(A): B`
else -> substitutedDescriptor
}
loadInvoke(sourceCompiler, sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod)
.let { if (parameterDescriptor.type.isSuspendFunctionType) getOrCreateJvmSuspendFunctionView(it, sourceCompiler.state) else it }
// This is technically wrong as it always uses `invoke`, but `loadInvoke` will fall back to `get` which is never mangled...
val asmMethod = sourceCompiler.state.typeMapper.mapAsmMethod(descriptor)
val invokeIsErased = loadInvoke(sourceCompiler, name.asString(), asmMethod)
invokeMethodDescriptor = if (invokeIsErased) descriptor.original else descriptor
}
}
@@ -90,7 +90,7 @@ class PsiSourceCompilerForInline(
get() = codegen.parentCodegen.orCreateSourceMapper
override fun generateLambdaBody(lambdaInfo: ExpressionLambda, reifiedTypeParameters: ReifiedTypeParametersUsages): SMAPAndMethodNode {
lambdaInfo as? PsiExpressionLambda ?: error("TODO")
require(lambdaInfo is PsiExpressionLambda)
val invokeMethodDescriptor = lambdaInfo.invokeMethodDescriptor
val jvmMethodSignature = state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
val asmMethod = jvmMethodSignature.asmMethod