Get rid of typed create and invoke wherever possible

in coroutine lambdas.
 #KT-26243 Fixed
This commit is contained in:
Ilmir Usmanov
2018-08-09 00:04:53 +03:00
parent 3bdf38e6b2
commit d3dbcae7a4
28 changed files with 365 additions and 73 deletions
@@ -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