diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java index a3c81484d10..a37f844592d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureCodegen.java @@ -304,7 +304,7 @@ public class ClosureCodegen extends MemberCodegen { ); } - 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 = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index b5c3f6d8b83..cd39139ae74 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -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 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().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) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 3f0ff1513dc..dc197e4d5f7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -300,6 +300,14 @@ fun KtExpression?.asmType(typeMapper: KotlinTypeMapper, bindingContext: BindingC fun KtExpression?.kotlinType(bindingContext: BindingContext) = this?.let(bindingContext::getType) +fun Collection.withVariableIndices(): List> = mutableListOf>().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() -} \ No newline at end of file +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index 5547f572cd5..77f6de693e0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -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) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 2e3e25997e7..c0f94101936 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -137,7 +137,7 @@ typealias FunctionDescriptorCopyBuilderToFunctionDescriptorCopyBuilder = FunctionDescriptor.CopyBuilder.(FunctionDescriptor) -> FunctionDescriptor.CopyBuilder -private fun D.createCustomCopy( +fun D.createCustomCopy( copySettings: FunctionDescriptorCopyBuilderToFunctionDescriptorCopyBuilder ): D { diff --git a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt index 5c74bb06804..427d9b499bd 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutineFields.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutineFields.txt @@ -13,6 +13,8 @@ final class CoroutineFieldsKt$box$1 { private field p$: Controller inner class CoroutineFieldsKt$box$1 method (p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation + public synthetic method create(p0: java.lang.Object, p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation protected final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object public final @org.jetbrains.annotations.Nullable method invoke(@org.jetbrains.annotations.NotNull p0: Controller, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object public synthetic method invoke(p0: java.lang.Object, p1: java.lang.Object): java.lang.Object diff --git a/compiler/testData/codegen/bytecodeText/constCoroutine.kt b/compiler/testData/codegen/bytecodeText/constCoroutine.kt index 82b20708881..0564cfa7240 100644 --- a/compiler/testData/codegen/bytecodeText/constCoroutine.kt +++ b/compiler/testData/codegen/bytecodeText/constCoroutine.kt @@ -19,7 +19,7 @@ fun box(): String { return "OK" } -// 1 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit; +// 2 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit; // 1 GETSTATIC EmptyContinuation.INSTANCE -// 1 GETSTATIC kotlin/coroutines/CoroutineIntrinsics.INSTANCE -// 3 GETSTATIC +// 2 GETSTATIC kotlin/coroutines/CoroutineIntrinsics.INSTANCE +// 5 GETSTATIC diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/SuspendFunctions.kt b/core/runtime.jvm/src/kotlin/jvm/internal/SuspendFunctions.kt new file mode 100644 index 00000000000..04f1343a584 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/SuspendFunctions.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal + +import kotlin.coroutines.Continuation + +interface SuspendFunction0 { + fun create(resultingContinuation: Continuation): Continuation +} + +interface SuspendFunction1 { + fun create(value: T, resultingContinuation: Continuation): Continuation +} diff --git a/libraries/stdlib/src/kotlin/coroutines/CoroutinesLibrary.kt b/libraries/stdlib/src/kotlin/coroutines/CoroutinesLibrary.kt index ee92f0b1ad0..741ee7559a7 100644 --- a/libraries/stdlib/src/kotlin/coroutines/CoroutinesLibrary.kt +++ b/libraries/stdlib/src/kotlin/coroutines/CoroutinesLibrary.kt @@ -19,7 +19,7 @@ public fun (suspend R.() -> T).createCoroutine( receiver: R, completion: Continuation, dispatcher: ContinuationDispatcher? = null -): Continuation = (this as (R, Continuation) -> Continuation).invoke(receiver, withDispatcher(completion, dispatcher)) +): Continuation = (this as kotlin.jvm.internal.SuspendFunction1).create(receiver, withDispatcher(completion, dispatcher)) /** * Starts coroutine with receiver type [R] and result type [T]. @@ -34,7 +34,7 @@ public fun (suspend R.() -> T).startCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null ) { - createCoroutine(receiver, completion, dispatcher).resume(Unit) + (this as Function2, Any?>).invoke(receiver, withDispatcher(completion, dispatcher)) } /** @@ -49,7 +49,7 @@ public fun (suspend R.() -> T).startCoroutine( public fun (suspend () -> T).createCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null -): Continuation = (this as (Continuation) -> Continuation).invoke(withDispatcher(completion, dispatcher)) +): Continuation = (this as kotlin.jvm.internal.SuspendFunction0).create(withDispatcher(completion, dispatcher)) /** * Starts coroutine without receiver and with result type [T]. @@ -63,7 +63,7 @@ public fun (suspend () -> T).startCoroutine( completion: Continuation, dispatcher: ContinuationDispatcher? = null ) { - createCoroutine(completion, dispatcher).resume(Unit) + (this as Function1, Any?>).invoke(withDispatcher(completion, dispatcher)) } /** diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt index dc82fe9e390..cc5978627d2 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-runtime.txt @@ -900,6 +900,14 @@ public final class kotlin/jvm/internal/StringCompanionObject { public static final field INSTANCE Lkotlin/jvm/internal/StringCompanionObject; } +public abstract interface class kotlin/jvm/internal/SuspendFunction0 { + public abstract fun create (Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; +} + +public abstract interface class kotlin/jvm/internal/SuspendFunction1 { + public abstract fun create (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Lkotlin/coroutines/Continuation; +} + public class kotlin/jvm/internal/TypeIntrinsics { public fun ()V public static fun asMutableCollection (Ljava/lang/Object;)Ljava/util/Collection;