Refine coroutine/suspend functions ABI

- `invoke` method must always start a suspend functions
- For creation of coroutine that has not been started yet, there are
two special internal interfaces `SuspendFunction0`/`SuspendFunction1`
This commit is contained in:
Denis Zharkov
2016-12-20 10:50:16 +03:00
parent 75174b4fba
commit d0ba048342
10 changed files with 145 additions and 15 deletions
@@ -304,7 +304,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
);
}
private void generateBridge(@NotNull Method bridge, @NotNull Method delegate) {
protected void generateBridge(@NotNull Method bridge, @NotNull Method delegate) {
if (bridge.equals(delegate)) return;
MethodVisitor mv =
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.builtins.createFunctionType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.codegen.coroutines.createJvmSuspendFunctionView
import org.jetbrains.kotlin.coroutines.isSuspendLambda
import org.jetbrains.kotlin.descriptors.*
@@ -28,6 +29,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.utils.addIfNotNull
class JvmRuntimeTypes(module: ModuleDescriptor) {
private val kotlinJvmInternalPackage = MutablePackageFragmentDescriptor(module, FqName("kotlin.jvm.internal"))
@@ -48,8 +50,16 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
(0..2).map { i -> createClass(kotlinJvmInternalPackage, "MutablePropertyReference$i") }
}
private fun createClass(packageFragment: PackageFragmentDescriptor, name: String): ClassDescriptor =
MutableClassDescriptor(packageFragment, ClassKind.CLASS, /* isInner = */ false, /* isExternal = */ false,
private val suspendFunctions: List<ClassDescriptor> by lazy {
(0..1).map { i -> createClass(kotlinJvmInternalPackage, "SuspendFunction$i", ClassKind.INTERFACE) }
}
private fun createClass(
packageFragment: PackageFragmentDescriptor,
name: String,
classKind: ClassKind = ClassKind.CLASS
): ClassDescriptor =
MutableClassDescriptor(packageFragment, classKind, /* isInner = */ false, /* isExternal = */ false,
Name.identifier(name), SourceElement.NO_SOURCE).apply {
modality = Modality.FINAL
visibility = Visibilities.PUBLIC
@@ -75,7 +85,15 @@ class JvmRuntimeTypes(module: ModuleDescriptor) {
)
if (descriptor.isSuspendLambda) {
return listOf(coroutineImplClass.defaultType, functionType)
return mutableListOf<KotlinType>().apply {
add(coroutineImplClass.defaultType)
add(functionType)
val parametersNumber =
descriptor.valueParameters.size + (if (functionType.isExtensionFunctionType) 1 else 0)
addIfNotNull(suspendFunctions.getOrNull(parametersNumber)?.defaultType)
}
}
return listOf(lambda.defaultType, functionType)
@@ -300,6 +300,14 @@ fun KtExpression?.asmType(typeMapper: KotlinTypeMapper, bindingContext: BindingC
fun KtExpression?.kotlinType(bindingContext: BindingContext) = this?.let(bindingContext::getType)
fun Collection<Type>.withVariableIndices(): List<Pair<Int, Type>> = mutableListOf<Pair<Int, Type>>().apply {
var index = 0
for (type in this@withVariableIndices) {
add(index to type)
index += type.size
}
}
fun FunctionDescriptor.isGenericToArray(): Boolean {
if (valueParameters.size != 1 || typeParameters.size != 1) return false
@@ -330,4 +338,4 @@ fun MemberDescriptor.isToArrayFromCollection(): Boolean {
if (!isSubclass(containingClassDescriptor, collectionClass)) return false
return isGenericToArray() || isNonGenericToArray()
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.coroutines
import com.intellij.util.ArrayUtil
import org.jetbrains.kotlin.backend.common.CONTINUATION_RESUME_METHOD_NAME
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.ClosureContext
import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -36,6 +37,8 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.KotlinTypeFactory
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.singletonOrEmptyList
@@ -91,6 +94,19 @@ class CoroutineCodegen(
)
}
private val createCoroutineDescriptor =
funDescriptor.createCustomCopy {
setName(Name.identifier("create"))
setReturnType(
KotlinTypeFactory.simpleNotNullType(
Annotations.EMPTY,
funDescriptor.builtIns.continuationClassDescriptor,
listOf(funDescriptor.builtIns.unitType.asTypeProjection())
)
)
setVisibility(Visibilities.PUBLIC)
}
override fun generateClosureBody() {
for (parameter in allLambdaParameters()) {
val fieldInfo = parameter.getFieldInfoForCoroutineLambdaParameter()
@@ -108,14 +124,65 @@ class CoroutineCodegen(
override fun generateBody() {
super.generateBody()
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, createCoroutineDescriptor,
object : FunctionGenerationStrategy.CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
generateCreateCoroutineMethod(codegen)
}
})
// invoke(..) = create(..).resume(Unit)
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, funDescriptor,
object : FunctionGenerationStrategy.CodegenBased(state) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
generateInvokeMethod(codegen)
codegen.v.generateInvokeMethod(signature)
}
})
if (allLambdaParameters().size <= 1) {
val delegate = typeMapper.mapSignatureSkipGeneric(createCoroutineDescriptor).asmMethod
val bridgeParameters = (1..delegate.argumentTypes.size - 1).map { AsmTypes.OBJECT_TYPE } + delegate.argumentTypes.last()
val bridge = Method(delegate.name, delegate.returnType, bridgeParameters.toTypedArray())
generateBridge(bridge, delegate)
}
}
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)
}
// this.create(..)
invokevirtual(
v.thisName,
createCoroutineDescriptor.name.identifier,
Type.getMethodDescriptor(
AsmTypes.CONTINUATION,
*parameterTypes.toTypedArray()
),
false
)
checkcast(Type.getObjectType(v.thisName))
// .resume(Unit)
StackValue.putUnitInstance(this)
invokevirtual(
AsmTypes.RESTRICTED_COROUTINE_IMPL.internalName,
CONTINUATION_RESUME_METHOD_NAME.identifier,
Type.getMethodDescriptor(Type.VOID_TYPE, AsmTypes.OBJECT_TYPE),
false
)
loadSuspendMarker()
areturn(AsmTypes.OBJECT_TYPE)
}
override fun generateConstructor(): Method {
val args = calculateConstructorParameters(typeMapper, closure, asmType)
val argTypes = args.map { it.fieldType }.plus(AsmTypes.CONTINUATION).toTypedArray()
@@ -149,7 +216,7 @@ class CoroutineCodegen(
return constructor
}
private fun generateInvokeMethod(codegen: ExpressionCodegen) {
private fun generateCreateCoroutineMethod(codegen: ExpressionCodegen) {
val classDescriptor = closureContext.contextDescriptor
val owner = typeMapper.mapClass(classDescriptor)
@@ -137,7 +137,7 @@ typealias FunctionDescriptorCopyBuilderToFunctionDescriptorCopyBuilder =
FunctionDescriptor.CopyBuilder<out FunctionDescriptor>.(FunctionDescriptor)
-> FunctionDescriptor.CopyBuilder<out FunctionDescriptor>
private fun <D : FunctionDescriptor> D.createCustomCopy(
fun <D : FunctionDescriptor> D.createCustomCopy(
copySettings: FunctionDescriptorCopyBuilderToFunctionDescriptorCopyBuilder
): D {