Support parameterless and suspend main functions in multifile classes
#KT-26574 Fixed
This commit is contained in:
@@ -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
|
||||
}
|
||||
+1
@@ -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
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
OUT:
|
||||
OK
|
||||
|
||||
Return code: 0
|
||||
+5
@@ -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";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user