Support parameterless and suspend main functions in multifile classes

#KT-26574 Fixed
This commit is contained in:
Denis Zharkov
2018-09-07 15:19:08 +03:00
parent b53194ef1a
commit fc3c4ad883
9 changed files with 102 additions and 9 deletions
@@ -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
@@ -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) {
@@ -0,0 +1,6 @@
// WITH_RUNTIME
@file:JvmMultifileClass
@file:JvmName("Foo")
package Hello
suspend fun main(args: Array<String>) {}
@@ -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 <init>(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
}
@@ -0,0 +1 @@
Return code: 0
@@ -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<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
@@ -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");
@@ -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";