From 74e8c7c1c5c5328ebb63aeb895578a898ea94d48 Mon Sep 17 00:00:00 2001 From: Ting-Yuan Huang Date: Fri, 24 May 2019 23:54:57 -0700 Subject: [PATCH] JVM_IR: generate default constructor Quoted from https://kotlinlang.org/docs/reference/classes.html "On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors." --- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 1 + .../lower/JvmDefaultConstructorLowering.kt | 53 +++++++++++++++++++ .../checkIfConstructorIsSynthetic.kt | 1 - .../defaultArguments/constructor/manyArgs.kt | 1 - .../codegen/box/jvmOverloads/privateClass.kt | 1 - .../multiplatform/noArgActualConstructor.kt | 1 - .../primitives/equalsNull_withExplicitFlag.kt | 1 - .../box/secondaryConstructors/withPrimary.kt | 1 - .../class/DefaultConstructor.kt | 1 - .../class/DefaultConstructorWithTwoArgs.kt | 1 - 10 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 2adb559c666..dadafffc4d1 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -114,6 +114,7 @@ val jvmPhases = namedIrFilePhase( annotationPhase then tailrecPhase then + jvmDefaultConstructorPhase then defaultArgumentStubPhase then interfacePhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt new file mode 100644 index 00000000000..985d78b784a --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmDefaultConstructorLowering.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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.passTypeArgumentsFrom +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.ir.builders.declarations.addConstructor +import org.jetbrains.kotlin.ir.builders.irBlockBody +import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.hasDefaultValue + +internal val jvmDefaultConstructorPhase = makeIrFilePhase( + ::JvmDefaultConstructorLowering, + name = "JvmDefaultConstructor", + description = "Generate default constructors for Java" +) + +// Quoted from https://kotlinlang.org/docs/reference/classes.html +// +// "On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional +// parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson +// or JPA that create class instances through parameterless constructors." +private class JvmDefaultConstructorLowering(val context: JvmBackendContext) : ClassLoweringPass { + + override fun lower(irClass: IrClass) { + val primaryConstructor = irClass.constructors.firstOrNull { it.isPrimary } ?: return + + if (!primaryConstructor.valueParameters.all { it.hasDefaultValue() }) + return + + // Skip if the default constructor is already defined by user. + if (irClass.constructors.any { it.valueParameters.isEmpty() }) + return + + irClass.addConstructor().apply { + val irBuilder = context.createIrBuilder(this.symbol, startOffset, endOffset) + body = irBuilder.irBlockBody { + +irDelegatingConstructorCall(primaryConstructor).apply { + passTypeArgumentsFrom(irClass) + passTypeArgumentsFrom(primaryConstructor, irClass.typeParameters.size) + } + } + } + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt b/compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt index 806646735fc..562c46fb3fe 100644 --- a/compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt +++ b/compiler/testData/codegen/box/defaultArguments/constructor/checkIfConstructorIsSynthetic.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/defaultArguments/constructor/manyArgs.kt b/compiler/testData/codegen/box/defaultArguments/constructor/manyArgs.kt index 6f861a15f21..9591a15c803 100644 --- a/compiler/testData/codegen/box/defaultArguments/constructor/manyArgs.kt +++ b/compiler/testData/codegen/box/defaultArguments/constructor/manyArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/jvmOverloads/privateClass.kt b/compiler/testData/codegen/box/jvmOverloads/privateClass.kt index a7fb95f05c2..772a5ea10e6 100644 --- a/compiler/testData/codegen/box/jvmOverloads/privateClass.kt +++ b/compiler/testData/codegen/box/jvmOverloads/privateClass.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt b/compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt index 4689a10d638..959210610ab 100644 --- a/compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt +++ b/compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/platformTypes/primitives/equalsNull_withExplicitFlag.kt b/compiler/testData/codegen/box/platformTypes/primitives/equalsNull_withExplicitFlag.kt index 5fefcdab981..f88168f008e 100644 --- a/compiler/testData/codegen/box/platformTypes/primitives/equalsNull_withExplicitFlag.kt +++ b/compiler/testData/codegen/box/platformTypes/primitives/equalsNull_withExplicitFlag.kt @@ -1,5 +1,4 @@ // KOTLIN_CONFIGURATION_FLAGS: +JVM.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL -// TARGET_BACKEND: JVM // WITH_RUNTIME // FILE: test.kt import kotlin.test.* diff --git a/compiler/testData/codegen/box/secondaryConstructors/withPrimary.kt b/compiler/testData/codegen/box/secondaryConstructors/withPrimary.kt index c3b419f5b56..28080ec08d1 100644 --- a/compiler/testData/codegen/box/secondaryConstructors/withPrimary.kt +++ b/compiler/testData/codegen/box/secondaryConstructors/withPrimary.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: WithPrimary.java diff --git a/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructor.kt b/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructor.kt index 1a80403528c..84f7685ab88 100644 --- a/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructor.kt +++ b/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructor.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR package test class A(val a: Int = 1) diff --git a/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructorWithTwoArgs.kt b/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructorWithTwoArgs.kt index 10dd7c7c617..e0cb4b06bf3 100644 --- a/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructorWithTwoArgs.kt +++ b/compiler/testData/compileJavaAgainstKotlin/class/DefaultConstructorWithTwoArgs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR package test class A(val a: Int = 1, val b: String = "default")