Get rid of typed create and invoke wherever possible
in coroutine lambdas. #KT-26243 Fixed
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker;
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
|
||||
import org.jetbrains.kotlin.resolve.jvm.RuntimeAssertionInfo;
|
||||
@@ -504,19 +505,25 @@ public class AsmUtil {
|
||||
}
|
||||
|
||||
public static int genAssignInstanceFieldFromParam(FieldInfo info, int index, InstructionAdapter iv) {
|
||||
return genAssignInstanceFieldFromParam(info, index, iv, 0);
|
||||
return genAssignInstanceFieldFromParam(info, index, iv, 0, false);
|
||||
}
|
||||
|
||||
public static int genAssignInstanceFieldFromParam(
|
||||
FieldInfo info,
|
||||
int index,
|
||||
InstructionAdapter iv,
|
||||
int ownerIndex
|
||||
int ownerIndex,
|
||||
boolean cast
|
||||
) {
|
||||
assert !info.isStatic();
|
||||
Type fieldType = info.getFieldType();
|
||||
iv.load(ownerIndex, info.getOwnerType());//this
|
||||
iv.load(index, fieldType); //param
|
||||
if (cast) {
|
||||
iv.load(index, AsmTypes.OBJECT_TYPE); //param
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, fieldType, iv);
|
||||
} else {
|
||||
iv.load(index, fieldType); //param
|
||||
}
|
||||
iv.visitFieldInsn(PUTFIELD, info.getOwnerInternalName(), info.getFieldName(), fieldType.getDescriptor());
|
||||
index += fieldType.getSize();
|
||||
return index;
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding.CAPTURES_CROSSINLINE_SUSPEND_LAMBDA
|
||||
import org.jetbrains.kotlin.codegen.context.ClosureContext
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION
|
||||
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.isReleaseCoroutines
|
||||
@@ -19,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
|
||||
@@ -165,9 +167,17 @@ class CoroutineCodegenForLambda private constructor(
|
||||
|
||||
private val constructorCallNormalizationMode = outerExpressionCodegen.state.constructorCallNormalizationMode
|
||||
|
||||
private val erasedInvokeFunction by lazy {
|
||||
getErasedInvokeFunction(funDescriptor).createCustomCopy { setModality(Modality.FINAL) }
|
||||
}
|
||||
|
||||
private lateinit var constructorToUseFromInvoke: Method
|
||||
|
||||
private val createCoroutineDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||
private val createCoroutineDescriptor by lazy {
|
||||
if (generateErasedCreate) getErasedCreateFunction() else getCreateFunction()
|
||||
}
|
||||
|
||||
private fun getCreateFunction(): SimpleFunctionDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||
funDescriptor.containingDeclaration,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier(SUSPEND_FUNCTION_CREATE_METHOD_NAME),
|
||||
@@ -188,6 +198,28 @@ class CoroutineCodegenForLambda private constructor(
|
||||
)
|
||||
}
|
||||
|
||||
private fun getErasedCreateFunction(): SimpleFunctionDescriptor {
|
||||
val typedCreate = getCreateFunction()
|
||||
assert(generateErasedCreate) { "cannot create erased create function: $typedCreate" }
|
||||
val argumentsNum = typeMapper.mapSignatureSkipGeneric(typedCreate).asmMethod.argumentTypes.size
|
||||
assert(argumentsNum == 1 || argumentsNum == 2) {
|
||||
"too many arguments of create to have an erased signature: $argumentsNum: $typedCreate"
|
||||
}
|
||||
return typedCreate.module.resolveClassByFqName(
|
||||
languageVersionSettings.coroutinesJvmInternalPackageFqName().child(
|
||||
if (languageVersionSettings.isReleaseCoroutines())
|
||||
Name.identifier("BaseContinuationImpl")
|
||||
else
|
||||
Name.identifier("CoroutineImpl")
|
||||
),
|
||||
NoLookupLocation.FROM_BACKEND
|
||||
).sure { "BaseContinuationImpl or CoroutineImpl is not found" }.defaultType.memberScope
|
||||
.getContributedFunctions(typedCreate.name, NoLookupLocation.FROM_BACKEND)
|
||||
.find { it.valueParameters.size == argumentsNum }
|
||||
.sure { "erased parent of $typedCreate is not found" }
|
||||
.createCustomCopy { setModality(Modality.FINAL) }
|
||||
}
|
||||
|
||||
override fun generateClosureBody() {
|
||||
for (parameter in allFunctionParameters()) {
|
||||
val fieldInfo = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||
@@ -202,9 +234,21 @@ class CoroutineCodegenForLambda private constructor(
|
||||
generateResumeImpl()
|
||||
}
|
||||
|
||||
private val generateErasedCreate: Boolean = allFunctionParameters().size <= 1
|
||||
|
||||
private val doNotGenerateInvokeBridge: Boolean = !originalSuspendFunctionDescriptor.isLocalSuspendFunctionNotSuspendLambda()
|
||||
|
||||
override fun generateBody() {
|
||||
super.generateBody()
|
||||
|
||||
if (doNotGenerateInvokeBridge) {
|
||||
v.serializationBindings.put<FunctionDescriptor, Method>(
|
||||
METHOD_FOR_FUNCTION,
|
||||
originalSuspendFunctionDescriptor,
|
||||
typeMapper.mapAsmMethod(erasedInvokeFunction)
|
||||
)
|
||||
}
|
||||
|
||||
// create() = ...
|
||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, createCoroutineDescriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
@@ -213,39 +257,52 @@ class CoroutineCodegenForLambda private constructor(
|
||||
}
|
||||
})
|
||||
|
||||
// invoke(..) = create(..).resume(Unit)
|
||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
codegen.v.generateInvokeMethod(signature)
|
||||
}
|
||||
})
|
||||
|
||||
if (allFunctionParameters().size <= 1) {
|
||||
val delegate = typeMapper.mapSignatureSkipGeneric(createCoroutineDescriptor).asmMethod
|
||||
|
||||
val bridgeParameterAsmTypes =
|
||||
List(delegate.argumentTypes.size - 1) { AsmTypes.OBJECT_TYPE } + delegate.argumentTypes.last()
|
||||
|
||||
val bridgeParameterKotlinTypes =
|
||||
List(delegate.argumentTypes.size - 1) { builtIns.nullableAnyType } + createCoroutineDescriptor.valueParameters.last().type
|
||||
|
||||
val bridge = Method(delegate.name, delegate.returnType, bridgeParameterAsmTypes.toTypedArray())
|
||||
|
||||
generateBridge(
|
||||
bridge, bridgeParameterKotlinTypes, createCoroutineDescriptor.returnType,
|
||||
delegate, createCoroutineDescriptor.returnType,
|
||||
false
|
||||
)
|
||||
if (doNotGenerateInvokeBridge) {
|
||||
generateUntypedInvokeMethod()
|
||||
} else {
|
||||
// invoke(..) = create(..).resume(Unit)
|
||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
|
||||
object : FunctionGenerationStrategy.CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
codegen.v.generateInvokeMethod(signature)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateBridges() {
|
||||
if (!doNotGenerateInvokeBridge) {
|
||||
super.generateBridges()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateUntypedInvokeMethod() {
|
||||
val untypedDescriptor = getErasedInvokeFunction(funDescriptor)
|
||||
val untypedAsmMethod = typeMapper.mapAsmMethod(untypedDescriptor)
|
||||
val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(untypedDescriptor)
|
||||
val mv = v.newMethod(
|
||||
OtherOrigin(element, funDescriptor), AsmUtil.getVisibilityAccessFlag(untypedDescriptor) or Opcodes.ACC_FINAL,
|
||||
untypedAsmMethod.name, untypedAsmMethod.descriptor, null, ArrayUtil.EMPTY_STRING_ARRAY
|
||||
)
|
||||
mv.visitCode()
|
||||
with(InstructionAdapter(mv)) {
|
||||
generateInvokeMethod(jvmMethodSignature)
|
||||
}
|
||||
|
||||
FunctionCodegen.endVisit(mv, "invoke", element)
|
||||
}
|
||||
|
||||
private fun InstructionAdapter.generateInvokeMethod(signature: JvmMethodSignature) {
|
||||
// this
|
||||
load(0, AsmTypes.OBJECT_TYPE)
|
||||
val parameterTypes = signature.valueParameters.map { it.asmType }
|
||||
parameterTypes.withVariableIndices().forEach { (index, type) ->
|
||||
load(index + 1, type)
|
||||
val createArgumentTypes =
|
||||
if (generateErasedCreate || doNotGenerateInvokeBridge) typeMapper.mapAsmMethod(createCoroutineDescriptor).argumentTypes.asList()
|
||||
else parameterTypes
|
||||
var index = 0
|
||||
parameterTypes.withVariableIndices().forEach { (varIndex, type) ->
|
||||
load(varIndex + 1, type)
|
||||
StackValue.coerce(type, createArgumentTypes[index++], this)
|
||||
}
|
||||
|
||||
// this.create(..)
|
||||
@@ -254,7 +311,7 @@ class CoroutineCodegenForLambda private constructor(
|
||||
createCoroutineDescriptor.name.identifier,
|
||||
Type.getMethodDescriptor(
|
||||
languageVersionSettings.continuationAsmType(),
|
||||
*parameterTypes.toTypedArray()
|
||||
*createArgumentTypes.toTypedArray()
|
||||
),
|
||||
false
|
||||
)
|
||||
@@ -293,7 +350,11 @@ class CoroutineCodegenForLambda private constructor(
|
||||
}
|
||||
|
||||
// load resultContinuation
|
||||
load(allFunctionParameters().map { typeMapper.mapType(it.type).size }.sum() + 1, AsmTypes.OBJECT_TYPE)
|
||||
if (generateErasedCreate) {
|
||||
load(allFunctionParameters().size + 1, AsmTypes.OBJECT_TYPE)
|
||||
} else {
|
||||
load(allFunctionParameters().map { typeMapper.mapType(it.type).size }.sum() + 1, AsmTypes.OBJECT_TYPE)
|
||||
}
|
||||
|
||||
invokespecial(owner.internalName, constructorToUseFromInvoke.name, constructorToUseFromInvoke.descriptor, false)
|
||||
|
||||
@@ -304,8 +365,19 @@ class CoroutineCodegenForLambda private constructor(
|
||||
var index = 1
|
||||
for (parameter in allFunctionParameters()) {
|
||||
val fieldInfoForCoroutineLambdaParameter = parameter.getFieldInfoForCoroutineLambdaParameter()
|
||||
load(index, fieldInfoForCoroutineLambdaParameter.fieldType)
|
||||
AsmUtil.genAssignInstanceFieldFromParam(fieldInfoForCoroutineLambdaParameter, index, this, cloneIndex)
|
||||
if (generateErasedCreate) {
|
||||
load(index, AsmTypes.OBJECT_TYPE)
|
||||
StackValue.coerce(AsmTypes.OBJECT_TYPE, fieldInfoForCoroutineLambdaParameter.fieldType, this)
|
||||
} else {
|
||||
load(index, fieldInfoForCoroutineLambdaParameter.fieldType)
|
||||
}
|
||||
AsmUtil.genAssignInstanceFieldFromParam(
|
||||
fieldInfoForCoroutineLambdaParameter,
|
||||
index,
|
||||
this,
|
||||
cloneIndex,
|
||||
generateErasedCreate
|
||||
)
|
||||
index += fieldInfoForCoroutineLambdaParameter.fieldType.size
|
||||
}
|
||||
|
||||
|
||||
@@ -496,3 +496,5 @@ fun FunctionDescriptor.isSuspendLambdaOrLocalFunction() = this.isSuspend && when
|
||||
is SimpleFunctionDescriptor -> this.visibility == Visibilities.LOCAL
|
||||
else -> false
|
||||
}
|
||||
|
||||
fun FunctionDescriptor.isLocalSuspendFunctionNotSuspendLambda() = isSuspendLambdaOrLocalFunction() && this !is AnonymousFunctionDescriptor
|
||||
|
||||
Reference in New Issue
Block a user