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
|
val originElement = origin.element ?: return
|
||||||
if (functionDescriptor.name.asString() != "main" || !DescriptorUtils.isTopLevelDeclaration(functionDescriptor)) 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(
|
if (!functionDescriptor.isSuspend && !isParameterless) return
|
||||||
|
if (!state.mainFunctionDetector.isMain(unwrappedFunctionDescriptor, false, true)) return
|
||||||
|
|
||||||
|
val lambdaInternalName =
|
||||||
|
if (functionDescriptor.isSuspend)
|
||||||
|
generateLambdaForRunSuspend(
|
||||||
state,
|
state,
|
||||||
originElement,
|
originElement,
|
||||||
packagePartClassBuilder.thisName,
|
packagePartClassBuilder.thisName,
|
||||||
signatureOfRealDeclaration
|
signatureOfRealDeclaration,
|
||||||
|
isParameterless
|
||||||
)
|
)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
packagePartClassBuilder.newMethod(
|
packagePartClassBuilder.newMethod(
|
||||||
Synthetic(originElement, functionDescriptor),
|
Synthetic(originElement, functionDescriptor),
|
||||||
@@ -502,6 +511,8 @@ fun generateBridgeForMainFunctionIfNecessary(
|
|||||||
METHOD_DESCRIPTOR_FOR_MAIN, null, null
|
METHOD_DESCRIPTOR_FOR_MAIN, null, null
|
||||||
).apply {
|
).apply {
|
||||||
visitCode()
|
visitCode()
|
||||||
|
|
||||||
|
if (lambdaInternalName != null) {
|
||||||
visitTypeInsn(NEW, lambdaInternalName)
|
visitTypeInsn(NEW, lambdaInternalName)
|
||||||
visitInsn(DUP)
|
visitInsn(DUP)
|
||||||
visitVarInsn(ALOAD, 0)
|
visitVarInsn(ALOAD, 0)
|
||||||
@@ -522,6 +533,17 @@ fun generateBridgeForMainFunctionIfNecessary(
|
|||||||
),
|
),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
visitMethodInsn(
|
||||||
|
INVOKESTATIC,
|
||||||
|
packagePartClassBuilder.thisName, "main",
|
||||||
|
Type.getMethodDescriptor(
|
||||||
|
Type.VOID_TYPE
|
||||||
|
),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
visitInsn(RETURN)
|
visitInsn(RETURN)
|
||||||
visitEnd()
|
visitEnd()
|
||||||
}
|
}
|
||||||
@@ -531,7 +553,8 @@ private fun generateLambdaForRunSuspend(
|
|||||||
state: GenerationState,
|
state: GenerationState,
|
||||||
originElement: PsiElement,
|
originElement: PsiElement,
|
||||||
packagePartClassInternalName: String,
|
packagePartClassInternalName: String,
|
||||||
signatureOfRealDeclaration: JvmMethodGenericSignature
|
signatureOfRealDeclaration: JvmMethodGenericSignature,
|
||||||
|
parameterless: Boolean
|
||||||
): String {
|
): String {
|
||||||
val internalName = "$packagePartClassInternalName$$\$main"
|
val internalName = "$packagePartClassInternalName$$\$main"
|
||||||
val lambdaBuilder = state.factory.newVisitor(
|
val lambdaBuilder = state.factory.newVisitor(
|
||||||
@@ -592,6 +615,9 @@ private fun generateLambdaForRunSuspend(
|
|||||||
).apply {
|
).apply {
|
||||||
visitCode()
|
visitCode()
|
||||||
|
|
||||||
|
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)
|
visitVarInsn(ALOAD, 0)
|
||||||
visitFieldInsn(
|
visitFieldInsn(
|
||||||
GETFIELD,
|
GETFIELD,
|
||||||
@@ -599,6 +625,7 @@ private fun generateLambdaForRunSuspend(
|
|||||||
"args",
|
"args",
|
||||||
ARRAY_OF_STRINGS_TYPE.descriptor
|
ARRAY_OF_STRINGS_TYPE.descriptor
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
visitVarInsn(ALOAD, 1)
|
visitVarInsn(ALOAD, 1)
|
||||||
val continuationInternalName = state.languageVersionSettings.continuationAsmType().internalName
|
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");
|
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 {
|
public void testCompilationFailed() throws Exception {
|
||||||
String jar = tmpdir.getAbsolutePath() + File.separator + "smoke.jar";
|
String jar = tmpdir.getAbsolutePath() + File.separator + "smoke.jar";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user