From fc3c4ad883af61f458edb4c4910a0c6d51b96204 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 7 Sep 2018 15:19:08 +0300 Subject: [PATCH] Support parameterless and suspend main functions in multifile classes #KT-26574 Fixed --- .../kotlin/codegen/FunctionCodegen.java | 4 +- .../jetbrains/kotlin/codegen/codegenUtil.kt | 29 +++++++++++---- .../bytecodeListing/main/multifileSuspend.kt | 6 +++ .../bytecodeListing/main/multifileSuspend.txt | 18 +++++++++ .../hello.compile.expected | 1 + .../helloAppSuspendMainInMultifile/hello.kt | 37 +++++++++++++++++++ .../hello.run.expected | 4 ++ .../codegen/BytecodeListingTestGenerated.java | 5 +++ .../kotlin/integration/CompilerSmokeTest.java | 7 ++++ 9 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/main/multifileSuspend.kt create mode 100644 compiler/testData/codegen/bytecodeListing/main/multifileSuspend.txt create mode 100644 compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.compile.expected create mode 100644 compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.kt create mode 100644 compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.run.expected diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index bbb6571b3a7..beb8b8acc87 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -245,7 +245,7 @@ public class FunctionCodegen { parentBodyCodegen.addAdditionalTask(new JvmStaticInCompanionObjectGenerator(functionDescriptor, origin, state, parentBodyCodegen)); } - generateBridgeForMainFunctionIfNecessary(state, v, functionDescriptor, jvmSignature, origin); + generateBridgeForMainFunctionIfNecessary(state, v, functionDescriptor, jvmSignature, origin, methodContext.getParentContext()); boolean isOpenSuspendInClass = functionDescriptor.isSuspend() && @@ -848,7 +848,7 @@ public class FunctionCodegen { return parameter.getName().asString(); } - private static void generateFacadeDelegateMethodBody( + public static void generateFacadeDelegateMethodBody( @NotNull MethodVisitor mv, @NotNull Method asmMethod, @NotNull MultifileClassFacadeContext context diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index 29e189138ac..c7cc6e04381 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.builtins.UnsignedTypes import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.FieldOwnerContext +import org.jetbrains.kotlin.codegen.context.MultifileClassFacadeContext import org.jetbrains.kotlin.codegen.context.PackageContext import org.jetbrains.kotlin.codegen.coroutines.continuationAsmType import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction @@ -480,7 +481,8 @@ fun generateBridgeForMainFunctionIfNecessary( packagePartClassBuilder: ClassBuilder, functionDescriptor: FunctionDescriptor, signatureOfRealDeclaration: JvmMethodGenericSignature, - origin: JvmDeclarationOrigin + origin: JvmDeclarationOrigin, + parentContext: CodegenContext<*> ) { val originElement = origin.element ?: return if (functionDescriptor.name.asString() != "main" || !DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return @@ -492,6 +494,24 @@ fun generateBridgeForMainFunctionIfNecessary( if (!functionDescriptor.isSuspend && !isParameterless) return if (!state.mainFunctionDetector.isMain(unwrappedFunctionDescriptor, false, true)) return + val bridgeMethodVisitor = packagePartClassBuilder.newMethod( + Synthetic(originElement, functionDescriptor), + ACC_PUBLIC or ACC_STATIC or ACC_SYNTHETIC, + "main", + METHOD_DESCRIPTOR_FOR_MAIN, null, null + ) + + if (parentContext is MultifileClassFacadeContext) { + bridgeMethodVisitor.visitCode() + FunctionCodegen.generateFacadeDelegateMethodBody( + bridgeMethodVisitor, + Method("main", METHOD_DESCRIPTOR_FOR_MAIN), + parentContext + ) + bridgeMethodVisitor.visitEnd() + return + } + val lambdaInternalName = if (functionDescriptor.isSuspend) generateLambdaForRunSuspend( @@ -504,12 +524,7 @@ fun generateBridgeForMainFunctionIfNecessary( else null - packagePartClassBuilder.newMethod( - Synthetic(originElement, functionDescriptor), - ACC_PUBLIC or ACC_STATIC or ACC_SYNTHETIC, - "main", - METHOD_DESCRIPTOR_FOR_MAIN, null, null - ).apply { + bridgeMethodVisitor.apply { visitCode() if (lambdaInternalName != null) { diff --git a/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.kt b/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.kt new file mode 100644 index 00000000000..2c5c1a1bd42 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.kt @@ -0,0 +1,6 @@ +// WITH_RUNTIME +@file:JvmMultifileClass +@file:JvmName("Foo") +package Hello + +suspend fun main(args: Array) {} diff --git a/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.txt b/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.txt new file mode 100644 index 00000000000..c25b4bf91e9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/main/multifileSuspend.txt @@ -0,0 +1,18 @@ +@kotlin.Metadata +public final class Hello/Foo { + public final static @org.jetbrains.annotations.Nullable method main(@org.jetbrains.annotations.NotNull p0: java.lang.String[], @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public synthetic static method main(p0: java.lang.String[]): void +} + +@kotlin.Metadata +synthetic final class Hello/Foo__MultifileSuspendKt$$$main { + private final field args: java.lang.String[] + synthetic method (p0: java.lang.String[]): void + public synthetic final method invoke(p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +synthetic final class Hello/Foo__MultifileSuspendKt { + public final static @org.jetbrains.annotations.Nullable method main(@org.jetbrains.annotations.NotNull p0: java.lang.String[], @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public synthetic static method main(p0: java.lang.String[]): void +} diff --git a/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.compile.expected b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.compile.expected new file mode 100644 index 00000000000..a14ac74940f --- /dev/null +++ b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.compile.expected @@ -0,0 +1 @@ +Return code: 0 diff --git a/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.kt b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.kt new file mode 100644 index 00000000000..f71af621f9a --- /dev/null +++ b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.kt @@ -0,0 +1,37 @@ +@file:JvmMultifileClass +@file:JvmName("Foo") +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? = null + +suspend fun appendAndSuspend(s: String) { + result += s + + suspendCoroutine { continuation -> + callback = { + continuation.resume(Unit) + } + } +} + +suspend fun main(args: Array) { + thread(isDaemon = true) { + while (true) { + val c = callback + c?.invoke() + Thread.sleep(500) + } + } + + appendAndSuspend(args[0]) + appendAndSuspend(args[1]) + println(result) + callback = null +} diff --git a/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.run.expected b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.run.expected new file mode 100644 index 00000000000..4000785a4d1 --- /dev/null +++ b/compiler/testData/integration/smoke/helloAppSuspendMainInMultifile/hello.run.expected @@ -0,0 +1,4 @@ +OUT: +OK + +Return code: 0 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 18d8d2d7ed5..74a746c775a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -332,6 +332,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeListing/main"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("multifileSuspend.kt") + public void testMultifileSuspend() throws Exception { + runTest("compiler/testData/codegen/bytecodeListing/main/multifileSuspend.kt"); + } + @TestMetadata("parameterlessMain.kt") public void testParameterlessMain() throws Exception { runTest("compiler/testData/codegen/bytecodeListing/main/parameterlessMain.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java b/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java index 7f8b45e5260..7dc9cad47cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java +++ b/compiler/tests/org/jetbrains/kotlin/integration/CompilerSmokeTest.java @@ -48,6 +48,13 @@ public class CompilerSmokeTest extends CompilerSmokeTestBase { run("hello.run", "-cp", jar, "Hello.HelloKt", "O", "K"); } + public void testHelloAppSuspendMainInMultifile() 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.Foo", "O", "K"); + } + public void testHelloAppParameterlessMain() throws Exception { String jar = tmpdir.getAbsolutePath() + File.separator + "hello.jar";