Support suspend fun main in JVM
#KT-17679 Fixed
This commit is contained in:
@@ -68,6 +68,7 @@ import java.util.*;
|
|||||||
|
|
||||||
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
|
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isNullableAny;
|
||||||
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
import static org.jetbrains.kotlin.codegen.AsmUtil.*;
|
||||||
|
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.generateBridgeForMainFunctionIfNecessary;
|
||||||
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
|
||||||
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.DECLARATION;
|
||||||
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
|
import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
|
||||||
@@ -244,6 +245,8 @@ public class FunctionCodegen {
|
|||||||
parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen));
|
parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateBridgeForMainFunctionIfNecessary(state, v, functionDescriptor, jvmSignature, origin);
|
||||||
|
|
||||||
boolean isOpenSuspendInClass =
|
boolean isOpenSuspendInClass =
|
||||||
functionDescriptor.isSuspend() &&
|
functionDescriptor.isSuspend() &&
|
||||||
functionDescriptor.getModality() != Modality.ABSTRACT && isOverridable(functionDescriptor) &&
|
functionDescriptor.getModality() != Modality.ABSTRACT && isOverridable(functionDescriptor) &&
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.builtins.UnsignedTypes
|
|||||||
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
import org.jetbrains.kotlin.codegen.context.CodegenContext
|
||||||
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
import org.jetbrains.kotlin.codegen.context.FieldOwnerContext
|
||||||
import org.jetbrains.kotlin.codegen.context.PackageContext
|
import org.jetbrains.kotlin.codegen.context.PackageContext
|
||||||
|
import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType
|
||||||
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
|
||||||
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
import org.jetbrains.kotlin.codegen.inline.ReificationArgument
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
import org.jetbrains.kotlin.codegen.optimization.common.asSequence
|
||||||
@@ -23,6 +25,7 @@ import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.idea.MainFunctionDetector
|
||||||
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
|
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
|
||||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
|
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
|
||||||
@@ -41,8 +44,11 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.Synthetic
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||||
@@ -52,6 +58,7 @@ import org.jetbrains.kotlin.types.TypeUtils
|
|||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
@@ -465,3 +472,155 @@ fun recordCallLabelForLambdaArgument(declaration: KtFunctionLiteral, bindingTrac
|
|||||||
|
|
||||||
storeLabelName(call.resultingDescriptor.name.asString())
|
storeLabelName(call.resultingDescriptor.name.asString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val ARRAY_OF_STRINGS_TYPE = Type.getType("[Ljava/lang/String;")
|
||||||
|
private val METHOD_DESCRIPTOR_FOR_MAIN = Type.getMethodDescriptor(Type.VOID_TYPE, ARRAY_OF_STRINGS_TYPE)
|
||||||
|
|
||||||
|
fun generateBridgeForMainFunctionIfNecessary(
|
||||||
|
state: GenerationState,
|
||||||
|
packagePartClassBuilder: ClassBuilder,
|
||||||
|
functionDescriptor: FunctionDescriptor,
|
||||||
|
signatureOfRealDeclaration: JvmMethodGenericSignature,
|
||||||
|
origin: JvmDeclarationOrigin
|
||||||
|
) {
|
||||||
|
val originElement = origin.element ?: return
|
||||||
|
if (functionDescriptor.name.asString() != "main" || !DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return
|
||||||
|
if (!MainFunctionDetector.isMain(functionDescriptor.unwrapInitialDescriptorForSuspendFunction(), false, true)) return
|
||||||
|
|
||||||
|
if (!functionDescriptor.isSuspend) return
|
||||||
|
|
||||||
|
val lambdaInternalName = generateLambdaForRunSuspend(
|
||||||
|
state,
|
||||||
|
originElement,
|
||||||
|
packagePartClassBuilder.thisName,
|
||||||
|
signatureOfRealDeclaration
|
||||||
|
)
|
||||||
|
|
||||||
|
packagePartClassBuilder.newMethod(
|
||||||
|
Synthetic(originElement, functionDescriptor),
|
||||||
|
ACC_PUBLIC or ACC_STATIC or ACC_SYNTHETIC,
|
||||||
|
"main",
|
||||||
|
METHOD_DESCRIPTOR_FOR_MAIN, null, null
|
||||||
|
).apply {
|
||||||
|
visitCode()
|
||||||
|
visitTypeInsn(NEW, lambdaInternalName)
|
||||||
|
visitInsn(DUP)
|
||||||
|
visitVarInsn(ALOAD, 0)
|
||||||
|
visitMethodInsn(
|
||||||
|
INVOKESPECIAL,
|
||||||
|
lambdaInternalName,
|
||||||
|
"<init>",
|
||||||
|
METHOD_DESCRIPTOR_FOR_MAIN,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
visitMethodInsn(
|
||||||
|
INVOKESTATIC,
|
||||||
|
"kotlin/coroutines/jvm/internal/RunSuspendKt", "runSuspend",
|
||||||
|
Type.getMethodDescriptor(
|
||||||
|
Type.VOID_TYPE,
|
||||||
|
Type.getObjectType(NUMBERED_FUNCTION_PREFIX + "1")
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
visitInsn(RETURN)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateLambdaForRunSuspend(
|
||||||
|
state: GenerationState,
|
||||||
|
originElement: PsiElement,
|
||||||
|
packagePartClassInternalName: String,
|
||||||
|
signatureOfRealDeclaration: JvmMethodGenericSignature
|
||||||
|
): String {
|
||||||
|
val internalName = "$packagePartClassInternalName$$\$main"
|
||||||
|
val lambdaBuilder = state.factory.newVisitor(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN,
|
||||||
|
Type.getObjectType(internalName),
|
||||||
|
originElement.containingFile
|
||||||
|
)
|
||||||
|
|
||||||
|
lambdaBuilder.defineClass(
|
||||||
|
originElement, state.classFileVersion,
|
||||||
|
ACC_FINAL or ACC_SUPER or ACC_SYNTHETIC,
|
||||||
|
internalName, null,
|
||||||
|
AsmTypes.LAMBDA.internalName,
|
||||||
|
arrayOf(NUMBERED_FUNCTION_PREFIX + "1")
|
||||||
|
)
|
||||||
|
|
||||||
|
lambdaBuilder.newField(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN,
|
||||||
|
ACC_PRIVATE or ACC_FINAL,
|
||||||
|
"args",
|
||||||
|
ARRAY_OF_STRINGS_TYPE.descriptor, null, null
|
||||||
|
)
|
||||||
|
|
||||||
|
lambdaBuilder.newMethod(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN,
|
||||||
|
AsmUtil.NO_FLAG_PACKAGE_PRIVATE or ACC_SYNTHETIC,
|
||||||
|
"<init>",
|
||||||
|
METHOD_DESCRIPTOR_FOR_MAIN, null, null
|
||||||
|
).apply {
|
||||||
|
visitCode()
|
||||||
|
visitVarInsn(ALOAD, 0)
|
||||||
|
visitVarInsn(ALOAD, 1)
|
||||||
|
visitFieldInsn(
|
||||||
|
PUTFIELD,
|
||||||
|
lambdaBuilder.thisName,
|
||||||
|
"args",
|
||||||
|
ARRAY_OF_STRINGS_TYPE.descriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
visitVarInsn(ALOAD, 0)
|
||||||
|
visitInsn(ICONST_1)
|
||||||
|
visitMethodInsn(
|
||||||
|
INVOKESPECIAL,
|
||||||
|
AsmTypes.LAMBDA.internalName,
|
||||||
|
"<init>",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
visitInsn(RETURN)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
lambdaBuilder.newMethod(
|
||||||
|
JvmDeclarationOrigin.NO_ORIGIN,
|
||||||
|
ACC_PUBLIC or ACC_FINAL or ACC_SYNTHETIC,
|
||||||
|
"invoke",
|
||||||
|
Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE), null, null
|
||||||
|
).apply {
|
||||||
|
visitCode()
|
||||||
|
|
||||||
|
visitVarInsn(ALOAD, 0)
|
||||||
|
visitFieldInsn(
|
||||||
|
GETFIELD,
|
||||||
|
lambdaBuilder.thisName,
|
||||||
|
"args",
|
||||||
|
ARRAY_OF_STRINGS_TYPE.descriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
visitVarInsn(ALOAD, 1)
|
||||||
|
val continuationInternalName = state.languageVersionSettings.continuationAsmType().internalName
|
||||||
|
|
||||||
|
visitTypeInsn(
|
||||||
|
CHECKCAST,
|
||||||
|
continuationInternalName
|
||||||
|
)
|
||||||
|
visitMethodInsn(
|
||||||
|
INVOKESTATIC,
|
||||||
|
packagePartClassInternalName,
|
||||||
|
signatureOfRealDeclaration.asmMethod.name,
|
||||||
|
signatureOfRealDeclaration.asmMethod.descriptor,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
visitInsn(ARETURN)
|
||||||
|
visitEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSyntheticClassMetadata(lambdaBuilder, state)
|
||||||
|
|
||||||
|
lambdaBuilder.done()
|
||||||
|
return lambdaBuilder.thisName
|
||||||
|
}
|
||||||
|
|||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// FILE: a.kt
|
||||||
|
suspend fun main(args: Array<String>) {}
|
||||||
|
|
||||||
|
// FILE: b.kt
|
||||||
|
suspend fun main(args: Array<String>) {}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public suspend fun main(/*0*/ args: kotlin.Array<kotlin.String>): kotlin.Unit
|
||||||
|
public suspend fun main(/*0*/ args: kotlin.Array<kotlin.String>): kotlin.Unit
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
Return code: 0
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package Hello
|
||||||
|
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
@kotlin.jvm.Volatile
|
||||||
|
private var result = ""
|
||||||
|
@kotlin.jvm.Volatile
|
||||||
|
private var callback: Function0<Unit>? = null
|
||||||
|
|
||||||
|
suspend fun appendAndSuspend(s: String) {
|
||||||
|
result += s
|
||||||
|
|
||||||
|
suspendCoroutine<Unit> { continuation ->
|
||||||
|
callback = {
|
||||||
|
continuation.resume(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) {
|
||||||
|
thread(isDaemon = true) {
|
||||||
|
while (true) {
|
||||||
|
val c = callback
|
||||||
|
c?.invoke()
|
||||||
|
Thread.sleep(500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendAndSuspend(args[0])
|
||||||
|
appendAndSuspend(args[1])
|
||||||
|
println(result)
|
||||||
|
callback = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
OUT:
|
||||||
|
OK
|
||||||
|
|
||||||
|
Return code: 0
|
||||||
@@ -15926,6 +15926,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt");
|
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedeclarationSuspendMainInMultiFile.kt")
|
||||||
|
public void testRedeclarationSuspendMainInMultiFile() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationSuspendMainInMultiFile.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("Redeclarations.kt")
|
@TestMetadata("Redeclarations.kt")
|
||||||
public void testRedeclarations() throws Exception {
|
public void testRedeclarations() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/redeclarations/Redeclarations.kt");
|
runTest("compiler/testData/diagnostics/tests/redeclarations/Redeclarations.kt");
|
||||||
|
|||||||
Generated
+5
@@ -15926,6 +15926,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
|||||||
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt");
|
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationMainInMultiFile.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("RedeclarationSuspendMainInMultiFile.kt")
|
||||||
|
public void testRedeclarationSuspendMainInMultiFile() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/redeclarations/RedeclarationSuspendMainInMultiFile.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("Redeclarations.kt")
|
@TestMetadata("Redeclarations.kt")
|
||||||
public void testRedeclarations() throws Exception {
|
public void testRedeclarations() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/redeclarations/Redeclarations.kt");
|
runTest("compiler/testData/diagnostics/tests/redeclarations/Redeclarations.kt");
|
||||||
|
|||||||
@@ -41,6 +41,13 @@ public class CompilerSmokeTest extends CompilerSmokeTestBase {
|
|||||||
run("hello.run", "-cp", jar, "Hello.HelloKt");
|
run("hello.run", "-cp", jar, "Hello.HelloKt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testHelloAppSuspendMain() throws Exception {
|
||||||
|
String jar = tmpdir.getAbsolutePath() + File.separator + "hello.jar";
|
||||||
|
|
||||||
|
assertEquals("compilation failed", 0, runCompiler("hello.compile", "-include-runtime", "hello.kt", "-d", jar));
|
||||||
|
run("hello.run", "-cp", jar, "Hello.HelloKt", "O", "K");
|
||||||
|
}
|
||||||
|
|
||||||
public void testCompilationFailed() throws Exception {
|
public void testCompilationFailed() throws Exception {
|
||||||
String jar = tmpdir.getAbsolutePath() + File.separator + "smoke.jar";
|
String jar = tmpdir.getAbsolutePath() + File.separator + "smoke.jar";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package suspendMain
|
||||||
|
|
||||||
|
suspend fun main(args: Array<String>) { // yes
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user