JVM IR: Support parameterless main methods
This commit: - introduces tests explicating what is and isn't considered a proper main method on the JVM backends. - implements support for parameterless main methods on the JVM IR backend - See KT-34338 for more tests.
This commit is contained in:
committed by
Alexander Udalov
parent
4b77db8979
commit
28b6913a25
@@ -43,4 +43,5 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
|
||||
object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true)
|
||||
object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD")
|
||||
object GENERATED_ASSERTION_ENABLED_FIELD : IrDeclarationOriginImpl("GENERATED_ASSERTION_ENABLED_FIELD", isSynthetic = true)
|
||||
object GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD : IrDeclarationOriginImpl("GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD", isSynthetic = true)
|
||||
}
|
||||
|
||||
@@ -172,8 +172,15 @@ private val syntheticAccessorPhase = makeIrFilePhase(
|
||||
prerequisite = setOf(objectClassPhase, staticDefaultFunctionPhase, interfacePhase)
|
||||
)
|
||||
|
||||
private val mainMethodGenerationPhase = makeIrFilePhase(
|
||||
::MainMethodGenerationLowering,
|
||||
name = "MainMethodGeneration",
|
||||
description = "Identify parameterless main methods and generate bridge main-methods"
|
||||
)
|
||||
|
||||
@Suppress("Reformat")
|
||||
private val jvmFilePhases =
|
||||
mainMethodGenerationPhase then
|
||||
typeAliasAnnotationMethodsPhase then
|
||||
stripTypeAliasDeclarationsPhase then
|
||||
provisionalFunctionExpressionPhase then
|
||||
|
||||
+1
-5
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
@@ -122,11 +123,6 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
||||
getJvmModuleNameForDeserializedDescriptor(function.descriptor)
|
||||
else null) ?: context.state.moduleName
|
||||
|
||||
private fun IrDeclaration.getJvmNameFromAnnotation(): String? {
|
||||
val const = getAnnotation(DescriptorUtils.JVM_NAME)?.getValueArgument(0) as? IrConst<*>
|
||||
return const?.value as? String
|
||||
}
|
||||
|
||||
private fun IrFunction.isPublishedApi(): Boolean =
|
||||
propertyIfAccessor.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.publishedApi)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.builders.Scope
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
|
||||
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_DEFAULT_FQ_NAME
|
||||
|
||||
/**
|
||||
@@ -82,6 +84,12 @@ val IrType.erasedUpperBound: IrClass
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
|
||||
fun IrDeclaration.getJvmNameFromAnnotation(): String? {
|
||||
val const = getAnnotation(DescriptorUtils.JVM_NAME)?.getValueArgument(0) as? IrConst<*>
|
||||
return const?.value as? String
|
||||
}
|
||||
|
||||
val IrFunction.propertyIfAccessor: IrDeclaration
|
||||
get() = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: this
|
||||
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
|
||||
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.builders.irBlockBody
|
||||
import org.jetbrains.kotlin.ir.builders.irCall
|
||||
import org.jetbrains.kotlin.ir.builders.irReturn
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
internal class MainMethodGenerationLowering(val context: JvmBackendContext) : ClassLoweringPass {
|
||||
|
||||
override fun lower(irClass: IrClass) {
|
||||
if (!context.configuration.languageVersionSettings.supportsFeature(LanguageFeature.ExtendedMainConvention)) return
|
||||
if (irClass.origin != IrDeclarationOrigin.FILE_CLASS) return
|
||||
|
||||
val parameterlessMain = irClass.functions.find { it.isParameterlessMainMethod() } ?: return
|
||||
|
||||
if (irClass.functions.any { it.isMainMethod() }) return
|
||||
|
||||
generateMainMethod(irClass, parameterlessMain)
|
||||
}
|
||||
|
||||
private fun generateMainMethod(irClass: IrClass, parameterlessMain: IrSimpleFunction) {
|
||||
irClass.addFunction {
|
||||
name = Name.identifier("main")
|
||||
visibility = Visibilities.PUBLIC
|
||||
returnType = context.irBuiltIns.unitType
|
||||
modality = Modality.FINAL
|
||||
origin = JvmLoweredDeclarationOrigin.GENERATED_MAIN_FOR_PARAMETERLESS_MAIN_METHOD
|
||||
}.apply {
|
||||
addValueParameter {
|
||||
name = Name.identifier("args")
|
||||
type = context.irBuiltIns.arrayClass.typeWith(context.irBuiltIns.stringType)
|
||||
}
|
||||
body = context.createIrBuilder(this.symbol).irBlockBody {
|
||||
+irReturn(this.irCall(parameterlessMain))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrSimpleFunction.isParameterlessMainMethod(): Boolean =
|
||||
typeParameters.isEmpty() && valueParameters.isEmpty() && returnType.isUnit() && name.asString() == "main"
|
||||
|
||||
|
||||
private fun IrSimpleFunction.isMainMethod(): Boolean {
|
||||
if (getJvmNameFromAnnotation() ?: name.asString() != "main") return false
|
||||
if (!returnType.isUnit()) return false
|
||||
|
||||
val parameter = allParameters.singleOrNull() ?: return false
|
||||
if (!parameter.type.isArray() && !parameter.type.isNullableArray()) return false
|
||||
|
||||
val argType = (parameter.type as IrSimpleType).arguments.first()
|
||||
return when (argType) {
|
||||
is IrTypeProjection -> {
|
||||
(argType.variance != Variance.IN_VARIANCE) && argType.type.isStringClassType()
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
@JvmName("main")
|
||||
fun foo(args: Array<String>) {
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
fun Array<String>.main() {
|
||||
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>?) {
|
||||
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<in String>) {
|
||||
|
||||
}
|
||||
|
||||
// method: ArrayWithContravariantStringIsNotMainMethodKt::main
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
@@ -0,0 +1,24 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<Int>) {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<in String>) {
|
||||
|
||||
}
|
||||
|
||||
fun main(args: Array<String>): Unit? {
|
||||
|
||||
return Unit
|
||||
}
|
||||
|
||||
fun Any.main(args: Array<String>) {
|
||||
|
||||
}
|
||||
|
||||
// method: ImpostorMainsKt::main
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
@@ -0,0 +1,7 @@
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
}
|
||||
|
||||
// method: MainKt::main
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
@@ -0,0 +1,21 @@
|
||||
// WITH_RUNTIME
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
@JvmName("notMain")
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
}
|
||||
|
||||
// method: NotJvmMainNameKt::main
|
||||
// jvm signature: ()V
|
||||
// generic signature: null
|
||||
|
||||
// method: NotJvmMainNameKt::main
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
|
||||
// method: NotJvmMainNameKt::notMain
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
@@ -0,0 +1,11 @@
|
||||
fun main() {
|
||||
|
||||
}
|
||||
|
||||
// method: SimpleKt::main
|
||||
// jvm signature: ()V
|
||||
// generic signature: null
|
||||
|
||||
// method: SimpleKt::main
|
||||
// jvm signature: ([Ljava/lang/String;)V
|
||||
// generic signature: null
|
||||
@@ -3438,6 +3438,39 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/parameterlessMain")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ParameterlessMain extends AbstractBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInParameterlessMain() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/parameterlessMain"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnJvmNameMain.kt")
|
||||
public void testDontGenerateOnJvmNameMain() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnJvmNameMain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnMain.kt")
|
||||
public void testDontGenerateOnMain() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnMain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnMainExtension.kt")
|
||||
public void testDontGenerateOnMainExtension() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnMainExtension.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnNullableArray.kt")
|
||||
public void testDontGenerateOnNullableArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnNullableArray.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/properties")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+33
@@ -3408,6 +3408,39 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/parameterlessMain")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ParameterlessMain extends AbstractIrBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInParameterlessMain() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/parameterlessMain"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnJvmNameMain.kt")
|
||||
public void testDontGenerateOnJvmNameMain() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnJvmNameMain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnMain.kt")
|
||||
public void testDontGenerateOnMain() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnMain.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnMainExtension.kt")
|
||||
public void testDontGenerateOnMainExtension() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnMainExtension.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dontGenerateOnNullableArray.kt")
|
||||
public void testDontGenerateOnNullableArray() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/parameterlessMain/dontGenerateOnNullableArray.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/properties")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+38
@@ -613,4 +613,42 @@ public class IrWriteSignatureTestGenerated extends AbstractIrWriteSignatureTest
|
||||
runTest("compiler/testData/writeSignature/nothing/nullableNothing.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeSignature/parameterlessMain")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ParameterlessMain extends AbstractIrWriteSignatureTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInParameterlessMain() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/writeSignature/parameterlessMain"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayWithContravariantStringIsNotMainMethod.kt")
|
||||
public void testArrayWithContravariantStringIsNotMainMethod() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/arrayWithContravariantStringIsNotMainMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("impostorMains.kt")
|
||||
public void testImpostorMains() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/impostorMains.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("main.kt")
|
||||
public void testMain() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notJvmMainName.kt")
|
||||
public void testNotJvmMainName() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/notJvmMainName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/simple.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
@@ -613,4 +613,42 @@ public class WriteSignatureTestGenerated extends AbstractWriteSignatureTest {
|
||||
runTest("compiler/testData/writeSignature/nothing/nullableNothing.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/writeSignature/parameterlessMain")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ParameterlessMain extends AbstractWriteSignatureTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInParameterlessMain() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/writeSignature/parameterlessMain"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("arrayWithContravariantStringIsNotMainMethod.kt")
|
||||
public void testArrayWithContravariantStringIsNotMainMethod() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/arrayWithContravariantStringIsNotMainMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("impostorMains.kt")
|
||||
public void testImpostorMains() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/impostorMains.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("main.kt")
|
||||
public void testMain() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("notJvmMainName.kt")
|
||||
public void testNotJvmMainName() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/notJvmMainName.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simple.kt")
|
||||
public void testSimple() throws Exception {
|
||||
runTest("compiler/testData/writeSignature/parameterlessMain/simple.kt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user