NoArg: Do not invoke initializers by default. Require "invokeInitializers" option to be set explicitly (KT-18667, KT-18668)

This commit is contained in:
Yan Zhulanow
2017-07-04 17:18:41 +03:00
committed by Yan Zhulanow
parent a983137978
commit b99007961f
14 changed files with 160 additions and 9 deletions
@@ -165,6 +165,13 @@ class SimpleKotlinGradleIT : BaseGradleIT() {
}
}
@Test
fun testNoArgKt18668() {
Project("noArgKt18668", GRADLE_VERSION).build("build") {
assertSuccessful()
}
}
@Test
fun testSamWithReceiverSimple() {
Project("samWithReceiverSimple", GRADLE_VERSION).build("build") {
@@ -0,0 +1,28 @@
buildscript {
repositories {
mavenLocal()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
}
}
apply plugin: "kotlin"
apply plugin: "kotlin-noarg"
repositories {
mavenLocal()
}
noArg {
annotation("test.NoArg")
}
sourceSets {
main.kotlin.srcDir 'src'
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
@@ -0,0 +1,12 @@
package test
annotation class NoArg
@NoArg
class AuthenticationConfiguration(tokenExpiresIn: Long) {
var tokenExpiryDate: String
init {
tokenExpiryDate = tokenExpiresIn.toString()
}
}
@@ -20,6 +20,8 @@ open class NoArgExtension {
internal val myAnnotations = mutableListOf<String>()
internal val myPresets = mutableListOf<String>()
open var invokeInitializers: Boolean = false
open fun annotation(fqName: String) {
myAnnotations.add(fqName)
}
@@ -71,6 +71,7 @@ class NoArgKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
private val ANNOTATION_ARG_NAME = "annotation"
private val PRESET_ARG_NAME = "preset"
private val INVOKE_INITIALIZERS_ARG_NAME = "invokeInitializers"
}
override fun isApplicable(project: Project, task: AbstractCompile) = NoArgGradleSubplugin.isEnabled(project)
@@ -96,6 +97,10 @@ class NoArgKotlinGradleSubplugin : KotlinGradleSubplugin<AbstractCompile> {
options += SubpluginOption(PRESET_ARG_NAME, preset)
}
if (noArgExtension.invokeInitializers) {
options += SubpluginOption(INVOKE_INITIALIZERS_ARG_NAME, "true")
}
return options
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.noarg
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.context.ConstructorContext
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
@@ -32,9 +31,8 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
class NoArgExpressionCodegenExtension : ExpressionCodegenExtension {
class NoArgExpressionCodegenExtension(val invokeInitializers: Boolean = false) : ExpressionCodegenExtension {
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) = with(codegen) {
if (shouldGenerateNoArgConstructor()) {
generateNoArgConstructor()
@@ -50,7 +48,11 @@ class NoArgExpressionCodegenExtension : ExpressionCodegenExtension {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
codegen.v.load(0, AsmTypes.OBJECT_TYPE)
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()V", false)
generateInitializers(codegen)
if (invokeInitializers) {
generateInitializers(codegen)
}
codegen.v.visitInsn(Opcodes.RETURN)
}
})
+12 -3
View File
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.noarg
import com.intellij.mock.MockProject
import com.intellij.openapi.extensions.Extensions
import org.jetbrains.kotlin.noarg.diagnostic.DefaultErrorMessagesNoArg
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
@@ -33,6 +32,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.noarg.NoArgCommandLineProcessor.Companion.SUPPORTED_PRESETS
import org.jetbrains.kotlin.noarg.NoArgConfigurationKeys.ANNOTATION
import org.jetbrains.kotlin.noarg.NoArgConfigurationKeys.INVOKE_INITIALIZERS
import org.jetbrains.kotlin.noarg.NoArgConfigurationKeys.PRESET
import org.jetbrains.kotlin.noarg.diagnostic.CliNoArgDeclarationChecker
import org.jetbrains.kotlin.resolve.TargetPlatform
@@ -43,6 +43,9 @@ object NoArgConfigurationKeys {
CompilerConfigurationKey.create("annotation qualified name")
val PRESET: CompilerConfigurationKey<List<String>> = CompilerConfigurationKey.create("annotation preset")
val INVOKE_INITIALIZERS: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create(
"invoke instance initializers in a no-arg constructor")
}
class NoArgCommandLineProcessor : CommandLineProcessor {
@@ -55,15 +58,20 @@ class NoArgCommandLineProcessor : CommandLineProcessor {
val PRESET_OPTION = CliOption("preset", "<name>", "Preset name (${SUPPORTED_PRESETS.keys.joinToString()})",
required = false, allowMultipleOccurrences = true)
val INVOKE_INITIALIZERS_OPTION = CliOption("invokeInitializers", "true/false",
"Invoke instance initializers in a no-arg constructor",
required = false, allowMultipleOccurrences = false)
val PLUGIN_ID = "org.jetbrains.kotlin.noarg"
}
override val pluginId = PLUGIN_ID
override val pluginOptions = listOf(ANNOTATION_OPTION, PRESET_OPTION)
override val pluginOptions = listOf(ANNOTATION_OPTION, PRESET_OPTION, INVOKE_INITIALIZERS_OPTION)
override fun processOption(option: CliOption, value: String, configuration: CompilerConfiguration) = when (option) {
ANNOTATION_OPTION -> configuration.appendList(ANNOTATION, value)
PRESET_OPTION -> configuration.appendList(PRESET, value)
INVOKE_INITIALIZERS_OPTION -> configuration.put(INVOKE_INITIALIZERS, value == "true")
else -> throw CliOptionProcessingException("Unknown option: ${option.name}")
}
}
@@ -79,7 +87,8 @@ class NoArgComponentRegistrar : ComponentRegistrar {
Extensions.getRootArea().getExtensionPoint(DefaultErrorMessages.Extension.EP_NAME).registerExtension(DefaultErrorMessagesNoArg())
StorageComponentContainerContributor.registerExtension(project, CliNoArgComponentContainerContributor(annotations))
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension())
val invokeInitializers = configuration[INVOKE_INITIALIZERS] ?: false
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension(invokeInitializers))
}
}
+1
View File
@@ -1,4 +1,5 @@
// WITH_RUNTIME
// INVOKE_INITIALIZERS
annotation class NoArg
@@ -0,0 +1,30 @@
// WITH_RUNTIME
annotation class NoArg
class Simple(val a: String)
@NoArg
class Test(val a: String) {
val x = 5
val y: Simple? = Simple("Hello, world!")
}
fun box(): String {
try {
val test = Test::class.java.newInstance()
if (test.x != 0) {
return "Bad 5"
}
if (test.y != null) {
return "Bad Hello, world!"
}
return "OK"
} catch (e: Throwable) {
e.printStackTrace()
return "Fail"
}
}
+14
View File
@@ -0,0 +1,14 @@
// WITH_RUNTIME
annotation class NoArg
@NoArg
class Foo(val s1: String) {
val s2: String = ""
val l: List<String> = listOf()
}
fun box(): String {
val instance = Foo::class.java.newInstance()
return "OK"
}
+22
View File
@@ -0,0 +1,22 @@
// WITH_RUNTIME
annotation class NoArg
@NoArg
class AuthenticationConfiguration(tokenExpiresIn: Long) {
var tokenExpiryDate: String?
init {
tokenExpiryDate = tokenExpiresIn.toString()
}
}
fun box(): String {
val instance = AuthenticationConfiguration::class.java.newInstance()
if (instance.tokenExpiryDate != null) {
return "Initializer invoked"
}
return "OK"
}
@@ -30,7 +30,8 @@ abstract class AbstractBlackBoxCodegenTestForNoArg : AbstractBlackBoxCodegenTest
val project = myEnvironment.project
registerExtension(project, CliNoArgComponentContainerContributor(NOARG_ANNOTATIONS))
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension())
val invokeInitializers = files.any { "// INVOKE_INITIALIZERS" in it.content }
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension(invokeInitializers))
super.loadMultiFiles(files)
}
@@ -34,6 +34,6 @@ abstract class AbstractBytecodeListingTestForNoArg : AbstractBytecodeListingTest
val project = environment.project
StorageComponentContainerContributor.registerExtension(project, CliNoArgComponentContainerContributor(NOARG_ANNOTATIONS))
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension())
ExpressionCodegenExtension.registerExtension(project, NoArgExpressionCodegenExtension(false))
}
}
@@ -42,6 +42,24 @@ public class BlackBoxCodegenTestForNoArgGenerated extends AbstractBlackBoxCodege
doTest(fileName);
}
@TestMetadata("initializersWithoutInvokeInitializers.kt")
public void testInitializersWithoutInvokeInitializers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/noarg/noarg-cli/testData/box/initializersWithoutInvokeInitializers.kt");
doTest(fileName);
}
@TestMetadata("kt18667.kt")
public void testKt18667() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/noarg/noarg-cli/testData/box/kt18667.kt");
doTest(fileName);
}
@TestMetadata("kt18668.kt")
public void testKt18668() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/noarg/noarg-cli/testData/box/kt18668.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("plugins/noarg/noarg-cli/testData/box/simple.kt");