[NoArg] Reorganize module structure of NoArg plugin
This commit is contained in:
committed by
teamcity
parent
0f757c300a
commit
8fc4962213
@@ -0,0 +1,23 @@
|
||||
description = "Kotlin NoArg Compiler Plugin (Backend)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(intellijCore())
|
||||
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
|
||||
implementation(kotlinStdlib())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.noarg
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
|
||||
import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmOverloadsAnnotation
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
|
||||
|
||||
class CliNoArgExpressionCodegenExtension(private val annotations: List<String>, invokeInitializers: Boolean = false) :
|
||||
AbstractNoArgExpressionCodegenExtension(invokeInitializers) {
|
||||
|
||||
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> = annotations
|
||||
}
|
||||
|
||||
abstract class AbstractNoArgExpressionCodegenExtension(val invokeInitializers: Boolean) : ExpressionCodegenExtension,
|
||||
AnnotationBasedExtension {
|
||||
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) = with(codegen) {
|
||||
if (shouldGenerateNoArgConstructor()) {
|
||||
generateNoArgConstructor()
|
||||
}
|
||||
}
|
||||
|
||||
private fun ImplementationBodyCodegen.generateNoArgConstructor() {
|
||||
val superClassInternalName = typeMapper.mapClass(descriptor.getSuperClassOrAny()).internalName
|
||||
|
||||
val constructorDescriptor = createNoArgConstructorDescriptor(descriptor)
|
||||
|
||||
val superClass = descriptor.getSuperClassOrAny()
|
||||
|
||||
// If a parent sealed class has not a zero-parameter constructor, user must write @NoArg annotation for the parent class as well,
|
||||
// and then we generate <init>()V
|
||||
val isParentASealedClassWithDefaultConstructor =
|
||||
superClass.modality == Modality.SEALED && superClass.constructors.any { isZeroParameterConstructor(it) }
|
||||
|
||||
functionCodegen.generateMethod(JvmDeclarationOrigin.NO_ORIGIN, constructorDescriptor, object : CodegenBased(state) {
|
||||
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
|
||||
codegen.v.load(0, AsmTypes.OBJECT_TYPE)
|
||||
|
||||
if (isParentASealedClassWithDefaultConstructor) {
|
||||
codegen.v.aconst(null)
|
||||
codegen.v.visitMethodInsn(
|
||||
Opcodes.INVOKESPECIAL, superClassInternalName, "<init>",
|
||||
"(Lkotlin/jvm/internal/DefaultConstructorMarker;)V", false
|
||||
)
|
||||
} else {
|
||||
codegen.v.visitMethodInsn(Opcodes.INVOKESPECIAL, superClassInternalName, "<init>", "()V", false)
|
||||
}
|
||||
|
||||
if (invokeInitializers) {
|
||||
generateInitializers(codegen)
|
||||
}
|
||||
codegen.v.visitInsn(Opcodes.RETURN)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun ImplementationBodyCodegen.shouldGenerateNoArgConstructor(): Boolean {
|
||||
val origin = myClass as? KtClass ?: return false
|
||||
|
||||
if (descriptor.kind != ClassKind.CLASS || !descriptor.hasSpecialAnnotation(origin)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return descriptor.constructors.none { isZeroParameterConstructor(it) }
|
||||
}
|
||||
|
||||
override val shouldGenerateClassSyntheticPartsInLightClassesMode = true
|
||||
|
||||
companion object {
|
||||
|
||||
fun isZeroParameterConstructor(constructor: ClassConstructorDescriptor): Boolean {
|
||||
val parameters = constructor.valueParameters
|
||||
return parameters.isEmpty() ||
|
||||
(parameters.all { it.declaresDefaultValue() } && (constructor.isPrimary || constructor.findJvmOverloadsAnnotation() != null))
|
||||
}
|
||||
|
||||
fun createNoArgConstructorDescriptor(containingClass: ClassDescriptor): ConstructorDescriptor =
|
||||
ClassConstructorDescriptorImpl.createSynthesized(containingClass, Annotations.EMPTY, false, SourceElement.NO_SOURCE).apply {
|
||||
initialize(
|
||||
null,
|
||||
calculateDispatchReceiverParameter(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
containingClass.builtIns.unitType,
|
||||
Modality.OPEN,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2010-2022 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.noarg
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.extensions.AnnotationBasedExtension
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrConstructor
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrDelegatingConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.name.JvmNames.JVM_OVERLOADS_FQ_NAME
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
|
||||
class NoArgIrGenerationExtension(
|
||||
private val annotations: List<String>,
|
||||
private val invokeInitializers: Boolean,
|
||||
) : IrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
moduleFragment.accept(NoArgIrTransformer(pluginContext, annotations, invokeInitializers), null)
|
||||
}
|
||||
}
|
||||
|
||||
private class NoArgIrTransformer(
|
||||
private val context: IrPluginContext,
|
||||
private val annotations: List<String>,
|
||||
private val invokeInitializers: Boolean,
|
||||
) : AnnotationBasedExtension, IrElementVisitorVoid {
|
||||
override fun getAnnotationFqNames(modifierListOwner: KtModifierListOwner?): List<String> = annotations
|
||||
|
||||
override fun visitElement(element: IrElement) {
|
||||
element.acceptChildren(this, null)
|
||||
}
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
super.visitClass(declaration)
|
||||
|
||||
if (needsNoargConstructor(declaration)) {
|
||||
declaration.declarations.add(getOrGenerateNoArgConstructor(declaration))
|
||||
}
|
||||
}
|
||||
|
||||
private val noArgConstructors = mutableMapOf<IrClass, IrConstructor>()
|
||||
|
||||
private fun getOrGenerateNoArgConstructor(klass: IrClass): IrConstructor = noArgConstructors.getOrPut(klass) {
|
||||
val superClass =
|
||||
klass.superTypes.mapNotNull(IrType::getClass).singleOrNull { it.kind == ClassKind.CLASS }
|
||||
?: context.irBuiltIns.anyClass.owner
|
||||
|
||||
val superConstructor =
|
||||
if (needsNoargConstructor(superClass))
|
||||
getOrGenerateNoArgConstructor(superClass)
|
||||
else superClass.constructors.singleOrNull { it.isZeroParameterConstructor() }
|
||||
?: error("No noarg super constructor for ${klass.render()}:\n" + superClass.constructors.joinToString("\n") { it.render() })
|
||||
|
||||
context.irFactory.buildConstructor {
|
||||
startOffset = SYNTHETIC_OFFSET
|
||||
endOffset = SYNTHETIC_OFFSET
|
||||
returnType = klass.defaultType
|
||||
}.also { ctor ->
|
||||
ctor.parent = klass
|
||||
ctor.body = context.irFactory.createBlockBody(
|
||||
ctor.startOffset, ctor.endOffset,
|
||||
listOfNotNull(
|
||||
IrDelegatingConstructorCallImpl(
|
||||
ctor.startOffset, ctor.endOffset, context.irBuiltIns.unitType,
|
||||
superConstructor.symbol, 0, superConstructor.valueParameters.size
|
||||
),
|
||||
IrInstanceInitializerCallImpl(
|
||||
ctor.startOffset, ctor.endOffset, klass.symbol, context.irBuiltIns.unitType
|
||||
).takeIf { invokeInitializers }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun needsNoargConstructor(declaration: IrClass): Boolean =
|
||||
declaration.kind == ClassKind.CLASS &&
|
||||
declaration.isAnnotatedWithNoarg() &&
|
||||
declaration.constructors.none { it.isZeroParameterConstructor() }
|
||||
|
||||
private fun IrClass.isAnnotatedWithNoarg(): Boolean =
|
||||
toIrBasedDescriptor().hasSpecialAnnotation(null)
|
||||
|
||||
// Returns true if this constructor is callable with no arguments by JVM rules, i.e. will have descriptor `()V`.
|
||||
private fun IrConstructor.isZeroParameterConstructor(): Boolean =
|
||||
valueParameters.all { it.defaultValue != null } && (valueParameters.isEmpty() || isPrimary || hasAnnotation(JVM_OVERLOADS_FQ_NAME))
|
||||
}
|
||||
Reference in New Issue
Block a user