Support main entry-point without arguments in JVM
#KT-26574 In Progress
This commit is contained in:
@@ -484,16 +484,25 @@ fun generateBridgeForMainFunctionIfNecessary(
|
||||
) {
|
||||
val originElement = origin.element ?: return
|
||||
if (functionDescriptor.name.asString() != "main" || !DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) return
|
||||
if (!state.mainFunctionDetector.isMain(functionDescriptor.unwrapInitialDescriptorForSuspendFunction(), false, true)) return
|
||||
|
||||
if (!functionDescriptor.isSuspend) return
|
||||
val unwrappedFunctionDescriptor = functionDescriptor.unwrapInitialDescriptorForSuspendFunction()
|
||||
val isParameterless =
|
||||
unwrappedFunctionDescriptor.extensionReceiverParameter == null && unwrappedFunctionDescriptor.valueParameters.isEmpty()
|
||||
|
||||
val lambdaInternalName = generateLambdaForRunSuspend(
|
||||
state,
|
||||
originElement,
|
||||
packagePartClassBuilder.thisName,
|
||||
signatureOfRealDeclaration
|
||||
)
|
||||
if (!functionDescriptor.isSuspend && !isParameterless) return
|
||||
if (!state.mainFunctionDetector.isMain(unwrappedFunctionDescriptor, false, true)) return
|
||||
|
||||
val lambdaInternalName =
|
||||
if (functionDescriptor.isSuspend)
|
||||
generateLambdaForRunSuspend(
|
||||
state,
|
||||
originElement,
|
||||
packagePartClassBuilder.thisName,
|
||||
signatureOfRealDeclaration,
|
||||
isParameterless
|
||||
)
|
||||
else
|
||||
null
|
||||
|
||||
packagePartClassBuilder.newMethod(
|
||||
Synthetic(originElement, functionDescriptor),
|
||||
@@ -502,26 +511,39 @@ fun generateBridgeForMainFunctionIfNecessary(
|
||||
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
|
||||
)
|
||||
if (lambdaInternalName != null) {
|
||||
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
|
||||
)
|
||||
} else {
|
||||
visitMethodInsn(
|
||||
INVOKESTATIC,
|
||||
packagePartClassBuilder.thisName, "main",
|
||||
Type.getMethodDescriptor(
|
||||
Type.VOID_TYPE
|
||||
),
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
visitInsn(RETURN)
|
||||
visitEnd()
|
||||
}
|
||||
@@ -531,7 +553,8 @@ private fun generateLambdaForRunSuspend(
|
||||
state: GenerationState,
|
||||
originElement: PsiElement,
|
||||
packagePartClassInternalName: String,
|
||||
signatureOfRealDeclaration: JvmMethodGenericSignature
|
||||
signatureOfRealDeclaration: JvmMethodGenericSignature,
|
||||
parameterless: Boolean
|
||||
): String {
|
||||
val internalName = "$packagePartClassInternalName$$\$main"
|
||||
val lambdaBuilder = state.factory.newVisitor(
|
||||
@@ -592,13 +615,17 @@ private fun generateLambdaForRunSuspend(
|
||||
).apply {
|
||||
visitCode()
|
||||
|
||||
visitVarInsn(ALOAD, 0)
|
||||
visitFieldInsn(
|
||||
GETFIELD,
|
||||
lambdaBuilder.thisName,
|
||||
"args",
|
||||
ARRAY_OF_STRINGS_TYPE.descriptor
|
||||
)
|
||||
if (!parameterless) {
|
||||
// Actually, the field for arguments may also be removed in case of parameterless main,
|
||||
// but probably it'd much easier when IR is ready
|
||||
visitVarInsn(ALOAD, 0)
|
||||
visitFieldInsn(
|
||||
GETFIELD,
|
||||
lambdaBuilder.thisName,
|
||||
"args",
|
||||
ARRAY_OF_STRINGS_TYPE.descriptor
|
||||
)
|
||||
}
|
||||
|
||||
visitVarInsn(ALOAD, 1)
|
||||
val continuationInternalName = state.languageVersionSettings.continuationAsmType().internalName
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
Return code: 0
|
||||
@@ -0,0 +1,9 @@
|
||||
package Hello
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
System.out.println("Hello!")
|
||||
}
|
||||
|
||||
fun main() {
|
||||
System.out.println("Fail")
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
OUT:
|
||||
Hello!
|
||||
|
||||
Return code: 0
|
||||
+1
@@ -0,0 +1 @@
|
||||
Return code: 0
|
||||
@@ -0,0 +1,5 @@
|
||||
package Hello
|
||||
|
||||
fun main() {
|
||||
System.out.println("Hello!")
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
OUT:
|
||||
Hello!
|
||||
|
||||
Return code: 0
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
Return code: 0
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package Hello
|
||||
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.reflect.jvm.javaMethod
|
||||
|
||||
@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() {
|
||||
thread(isDaemon = true) {
|
||||
while (true) {
|
||||
val c = callback
|
||||
c?.invoke()
|
||||
Thread.sleep(500)
|
||||
}
|
||||
}
|
||||
|
||||
appendAndSuspend("O")
|
||||
appendAndSuspend("K")
|
||||
println(result)
|
||||
callback = null
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
OUT:
|
||||
OK
|
||||
|
||||
Return code: 0
|
||||
@@ -48,6 +48,27 @@ public class CompilerSmokeTest extends CompilerSmokeTestBase {
|
||||
run("hello.run", "-cp", jar, "Hello.HelloKt", "O", "K");
|
||||
}
|
||||
|
||||
public void testHelloAppParameterlessMain() 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");
|
||||
}
|
||||
|
||||
public void testHelloAppOldAndParameterlessMain() 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");
|
||||
}
|
||||
|
||||
public void testHelloAppSuspendParameterlessMain() 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 {
|
||||
String jar = tmpdir.getAbsolutePath() + File.separator + "smoke.jar";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user