Do not generate variables for non-generated fields
Use attributes instead of classes. #KT-43048 Fixed
This commit is contained in:
@@ -128,7 +128,7 @@ class JvmBackendContext(
|
|||||||
|
|
||||||
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
val inlineClassReplacements = MemoizedInlineClassReplacements(state.functionsWithInlineClassReturnTypesMangled, irFactory, this)
|
||||||
|
|
||||||
internal val continuationClassesVarsCountByType: MutableMap<IrClass, Map<Type, Int>> = hashMapOf()
|
internal val continuationClassesVarsCountByType: MutableMap<IrAttributeContainer, Map<Type, Int>> = hashMapOf()
|
||||||
|
|
||||||
internal fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol =
|
internal fun referenceClass(descriptor: ClassDescriptor): IrClassSymbol =
|
||||||
symbolTable.lazyWrapper.referenceClass(descriptor)
|
symbolTable.lazyWrapper.referenceClass(descriptor)
|
||||||
|
|||||||
+4
-1
@@ -359,11 +359,14 @@ class ClassCodegen private constructor(
|
|||||||
val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
|
val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
|
||||||
val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
|
val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
|
||||||
|
|
||||||
|
// For suspend lambdas continuation class is null, and we need to use containing class to put L$ fields
|
||||||
|
val attributeContainer = continuationClass?.attributeOwnerId ?: irClass.attributeOwnerId
|
||||||
|
|
||||||
node.acceptWithStateMachine(
|
node.acceptWithStateMachine(
|
||||||
method,
|
method,
|
||||||
this,
|
this,
|
||||||
smapCopyingVisitor,
|
smapCopyingVisitor,
|
||||||
context.continuationClassesVarsCountByType[continuationClass] ?: emptyMap()
|
context.continuationClassesVarsCountByType[attributeContainer] ?: emptyMap()
|
||||||
) {
|
) {
|
||||||
continuationClassCodegen.value.visitor
|
continuationClassCodegen.value.visitor
|
||||||
}
|
}
|
||||||
|
|||||||
+507
@@ -0,0 +1,507 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 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.codegen
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
||||||
|
import org.jetbrains.kotlin.codegen.DescriptorAsmUtil
|
||||||
|
import org.jetbrains.kotlin.codegen.inline.*
|
||||||
|
import org.jetbrains.kotlin.codegen.writeKotlinMetadata
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
|
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrBlockBody
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||||
|
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||||
|
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.checkers.JvmSimpleNameBacktickChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.*
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
interface MetadataSerializer {
|
||||||
|
fun serialize(metadata: MetadataSource): Pair<MessageLite, JvmStringTable>?
|
||||||
|
fun bindMethodMetadata(metadata: MetadataSource.Property, signature: Method)
|
||||||
|
fun bindMethodMetadata(metadata: MetadataSource.Function, signature: Method)
|
||||||
|
fun bindFieldMetadata(metadata: MetadataSource.Property, signature: Pair<Type, String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassCodegen private constructor(
|
||||||
|
val irClass: IrClass,
|
||||||
|
val context: JvmBackendContext,
|
||||||
|
private val parentFunction: IrFunction?,
|
||||||
|
) : InnerClassConsumer {
|
||||||
|
private val parentClassCodegen = (parentFunction?.parentAsClass ?: irClass.parent as? IrClass)?.let { getOrCreate(it, context) }
|
||||||
|
private val withinInline: Boolean = parentClassCodegen?.withinInline == true || parentFunction?.isInline == true
|
||||||
|
|
||||||
|
private val state get() = context.state
|
||||||
|
private val typeMapper get() = context.typeMapper
|
||||||
|
|
||||||
|
val type: Type = typeMapper.mapClass(irClass)
|
||||||
|
|
||||||
|
val reifiedTypeParametersUsages = ReifiedTypeParametersUsages()
|
||||||
|
|
||||||
|
private val jvmSignatureClashDetector = JvmSignatureClashDetector(irClass, type, context)
|
||||||
|
|
||||||
|
private val classOrigin = irClass.descriptorOrigin
|
||||||
|
|
||||||
|
private val visitor = state.factory.newVisitor(classOrigin, type, irClass.fileParent.loadSourceFilesInfo()).apply {
|
||||||
|
val signature = typeMapper.mapClassSignature(irClass, type)
|
||||||
|
// Ensure that the backend only produces class names that would be valid in the frontend for JVM.
|
||||||
|
if (context.state.classBuilderMode.generateBodies && signature.hasInvalidName()) {
|
||||||
|
throw IllegalStateException("Generating class with invalid name '${type.className}': ${irClass.dump()}")
|
||||||
|
}
|
||||||
|
defineClass(
|
||||||
|
irClass.psiElement,
|
||||||
|
state.classFileVersion,
|
||||||
|
irClass.flags,
|
||||||
|
signature.name,
|
||||||
|
signature.javaGenericSignature,
|
||||||
|
signature.superclassName,
|
||||||
|
signature.interfaces.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: the order of entries in this set depends on the order in which methods are generated; this means it is unstable
|
||||||
|
// under incremental compilation, as calls to `inline fun`s declared in this class cause them to be generated out of order.
|
||||||
|
private val innerClasses = linkedSetOf<IrClass>()
|
||||||
|
|
||||||
|
// TODO: the names produced by generators in this map depend on the order in which methods are generated; see above.
|
||||||
|
private val regeneratedObjectNameGenerators = mutableMapOf<String, NameGenerator>()
|
||||||
|
|
||||||
|
fun getRegeneratedObjectNameGenerator(function: IrFunction): NameGenerator {
|
||||||
|
val name = if (function.name.isSpecial) "special" else function.name.asString()
|
||||||
|
return regeneratedObjectNameGenerators.getOrPut(name) {
|
||||||
|
NameGenerator("${type.internalName}\$$name\$\$inlined")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var generated = false
|
||||||
|
|
||||||
|
fun generate() {
|
||||||
|
// TODO: reject repeated generate() calls; currently, these can happen for objects in finally
|
||||||
|
// blocks since they are `accept`ed once per each CFG edge out of the try-finally.
|
||||||
|
if (generated) return
|
||||||
|
generated = true
|
||||||
|
|
||||||
|
// We remove reads of `$$delegatedProperties` (and the field itself) if they are not in fact used for anything.
|
||||||
|
val delegatedProperties = irClass.fields.singleOrNull { it.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE }
|
||||||
|
val delegatedPropertyOptimizer = if (delegatedProperties != null) DelegatedPropertyOptimizer() else null
|
||||||
|
// Generating a method node may cause the addition of a field with an initializer if an inline function
|
||||||
|
// call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors,
|
||||||
|
// there is a very specific generation order.
|
||||||
|
val smap = context.getSourceMapper(irClass)
|
||||||
|
// 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement:
|
||||||
|
for (method in irClass.declarations.filterIsInstance<IrFunction>()) {
|
||||||
|
if (method.name.asString() != "<clinit>") {
|
||||||
|
generateMethod(method, smap, delegatedPropertyOptimizer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. `<clinit>` itself can add a field, but the statement is generated via the `return init` hack:
|
||||||
|
irClass.functions.find { it.name.asString() == "<clinit>" }?.let { generateMethod(it, smap, delegatedPropertyOptimizer) }
|
||||||
|
// 3. Now we have all the fields (`$$delegatedProperties` might be redundant if all reads were optimized out):
|
||||||
|
for (field in irClass.fields) {
|
||||||
|
if (field !== delegatedProperties || delegatedPropertyOptimizer?.needsDelegatedProperties == true) {
|
||||||
|
generateField(field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4. Generate nested classes at the end, to ensure that when the companion's metadata is serialized
|
||||||
|
// everything moved to the outer class has already been recorded in `globalSerializationBindings`.
|
||||||
|
for (declaration in irClass.declarations) {
|
||||||
|
if (declaration is IrClass) {
|
||||||
|
getOrCreate(declaration, context).generate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object : AnnotationCodegen(this@ClassCodegen, context) {
|
||||||
|
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
||||||
|
return visitor.visitor.visitAnnotation(descr, visible)
|
||||||
|
}
|
||||||
|
}.genAnnotations(irClass, null, null)
|
||||||
|
generateKotlinMetadataAnnotation()
|
||||||
|
|
||||||
|
generateInnerAndOuterClasses()
|
||||||
|
|
||||||
|
if (withinInline || !smap.isTrivial) {
|
||||||
|
visitor.visitSMAP(smap, !context.state.languageVersionSettings.supportsFeature(LanguageFeature.CorrectSourceMappingSyntax))
|
||||||
|
} else {
|
||||||
|
smap.sourceInfo!!.sourceFileName?.let {
|
||||||
|
visitor.visitSource(it, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visitor.done()
|
||||||
|
jvmSignatureClashDetector.reportErrors(classOrigin)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateAssertFieldIfNeeded(generatingClInit: Boolean): IrExpression? {
|
||||||
|
if (irClass.hasAssertionsDisabledField(context))
|
||||||
|
return null
|
||||||
|
val topLevelClass = generateSequence(this) { it.parentClassCodegen }.last().irClass
|
||||||
|
val field = irClass.buildAssertionsDisabledField(context, topLevelClass)
|
||||||
|
generateField(field)
|
||||||
|
// Normally, `InitializersLowering` would move the initializer to <clinit>, but
|
||||||
|
// it's obviously too late for that.
|
||||||
|
val init = IrSetFieldImpl(
|
||||||
|
field.startOffset, field.endOffset, field.symbol, null,
|
||||||
|
field.initializer!!.expression, context.irBuiltIns.unitType
|
||||||
|
)
|
||||||
|
if (generatingClInit) {
|
||||||
|
// Too late to modify the IR; have to ask the currently active `ExpressionCodegen`
|
||||||
|
// to generate this statement directly.
|
||||||
|
return init
|
||||||
|
}
|
||||||
|
val classInitializer = irClass.functions.singleOrNull { it.name.asString() == "<clinit>" } ?: irClass.addFunction {
|
||||||
|
name = Name.special("<clinit>")
|
||||||
|
returnType = context.irBuiltIns.unitType
|
||||||
|
}.apply {
|
||||||
|
body = IrBlockBodyImpl(startOffset, endOffset)
|
||||||
|
}
|
||||||
|
(classInitializer.body as IrBlockBody).statements.add(0, init)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private val metadataSerializer: MetadataSerializer =
|
||||||
|
context.backendExtension.createSerializer(
|
||||||
|
context, irClass, type, visitor.serializationBindings, parentClassCodegen?.metadataSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun generateKotlinMetadataAnnotation() {
|
||||||
|
// TODO: if `-Xmultifile-parts-inherit` is enabled, write the corresponding flag for parts and facades to [Metadata.extraInt].
|
||||||
|
val extraFlags = context.backendExtension.generateMetadataExtraFlags(state.abiStability)
|
||||||
|
|
||||||
|
val facadeClassName = context.multifileFacadeForPart[irClass.attributeOwnerId]
|
||||||
|
val metadata = irClass.metadata
|
||||||
|
val entry = irClass.fileParent.fileEntry
|
||||||
|
val kind = when {
|
||||||
|
facadeClassName != null -> KotlinClassHeader.Kind.MULTIFILE_CLASS_PART
|
||||||
|
metadata is MetadataSource.Class -> KotlinClassHeader.Kind.CLASS
|
||||||
|
metadata is MetadataSource.File -> KotlinClassHeader.Kind.FILE_FACADE
|
||||||
|
metadata is MetadataSource.Function -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||||
|
entry is MultifileFacadeFileEntry -> KotlinClassHeader.Kind.MULTIFILE_CLASS
|
||||||
|
else -> KotlinClassHeader.Kind.SYNTHETIC_CLASS
|
||||||
|
}
|
||||||
|
writeKotlinMetadata(visitor, state, kind, extraFlags) {
|
||||||
|
if (metadata != null) {
|
||||||
|
metadataSerializer.serialize(metadata)?.let { (proto, stringTable) ->
|
||||||
|
DescriptorAsmUtil.writeAnnotationData(it, proto, stringTable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry is MultifileFacadeFileEntry) {
|
||||||
|
val arv = it.visitArray(JvmAnnotationNames.METADATA_DATA_FIELD_NAME)
|
||||||
|
for (partFile in entry.partFiles) {
|
||||||
|
val fileClass = partFile.declarations.singleOrNull { it.isFileClass } as IrClass?
|
||||||
|
if (fileClass != null) arv.visit(null, typeMapper.mapClass(fileClass).internalName)
|
||||||
|
}
|
||||||
|
arv.visitEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (facadeClassName != null) {
|
||||||
|
it.visit(JvmAnnotationNames.METADATA_MULTIFILE_CLASS_NAME_FIELD_NAME, facadeClassName.internalName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irClass in context.classNameOverride) {
|
||||||
|
val isFileClass = kind == KotlinClassHeader.Kind.MULTIFILE_CLASS ||
|
||||||
|
kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART ||
|
||||||
|
kind == KotlinClassHeader.Kind.FILE_FACADE
|
||||||
|
assert(isFileClass) { "JvmPackageName is not supported for classes: ${irClass.render()}" }
|
||||||
|
it.visit(JvmAnnotationNames.METADATA_PACKAGE_NAME_FIELD_NAME, irClass.fqNameWhenAvailable!!.parent().asString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrFile.loadSourceFilesInfo(): List<File> {
|
||||||
|
val entry = fileEntry
|
||||||
|
if (entry is MultifileFacadeFileEntry) {
|
||||||
|
return entry.partFiles.flatMap { it.loadSourceFilesInfo() }
|
||||||
|
}
|
||||||
|
return listOfNotNull(context.psiSourceManager.getFileEntry(this)?.let { File(it.name) })
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getOrCreate(
|
||||||
|
irClass: IrClass,
|
||||||
|
context: JvmBackendContext,
|
||||||
|
// The `parentFunction` is only set for classes nested inside of functions. This is usually safe, since there is no
|
||||||
|
// way to refer to (inline) members of such a class from outside of the function unless the function in question is
|
||||||
|
// itself declared as inline. In that case, the function will be compiled before we can refer to the nested class.
|
||||||
|
//
|
||||||
|
// The one exception to this rule are anonymous objects defined as members of a class. These are nested inside of the
|
||||||
|
// class initializer, but can be referred to from anywhere within the scope of the class. That's why we have to ensure
|
||||||
|
// that all references to classes inside of <clinit> have a non-null `parentFunction`.
|
||||||
|
parentFunction: IrFunction? = irClass.parent.safeAs<IrFunction>()?.takeIf {
|
||||||
|
it.origin == JvmLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER
|
||||||
|
},
|
||||||
|
): ClassCodegen =
|
||||||
|
context.classCodegens.getOrPut(irClass) { ClassCodegen(irClass, context, parentFunction) }.also {
|
||||||
|
assert(parentFunction == null || it.parentFunction == parentFunction) {
|
||||||
|
"inconsistent parent function for ${irClass.render()}:\n" +
|
||||||
|
"New: ${parentFunction!!.render()}\n" +
|
||||||
|
"Old: ${it.parentFunction?.render()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JvmClassSignature.hasInvalidName() =
|
||||||
|
name.splitToSequence('/').any { identifier -> identifier.any { it in JvmSimpleNameBacktickChecker.INVALID_CHARS } }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateField(field: IrField) {
|
||||||
|
val fieldType = typeMapper.mapType(field)
|
||||||
|
val fieldSignature =
|
||||||
|
if (field.origin == IrDeclarationOrigin.PROPERTY_DELEGATE) null
|
||||||
|
else context.methodSignatureMapper.mapFieldSignature(field)
|
||||||
|
val fieldName = field.name.asString()
|
||||||
|
val flags = field.computeFieldFlags(context, state.languageVersionSettings)
|
||||||
|
val fv = visitor.newField(
|
||||||
|
field.descriptorOrigin, flags, fieldName, fieldType.descriptor,
|
||||||
|
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
|
||||||
|
)
|
||||||
|
|
||||||
|
jvmSignatureClashDetector.trackField(field, RawSignature(fieldName, fieldType.descriptor, MemberKind.FIELD))
|
||||||
|
|
||||||
|
if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) {
|
||||||
|
val skipNullabilityAnnotations =
|
||||||
|
flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 ||
|
||||||
|
field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE
|
||||||
|
object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) {
|
||||||
|
override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor {
|
||||||
|
return fv.visitAnnotation(descr, visible)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitTypeAnnotation(descr: String?, path: TypePath?, visible: Boolean): AnnotationVisitor {
|
||||||
|
return fv.visitTypeAnnotation(TypeReference.newTypeReference(TypeReference.FIELD).value, path, descr, visible)
|
||||||
|
}
|
||||||
|
}.genAnnotations(field, fieldType, field.type)
|
||||||
|
}
|
||||||
|
|
||||||
|
(field.metadata as? MetadataSource.Property)?.let {
|
||||||
|
metadataSerializer.bindFieldMetadata(it, fieldType to fieldName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
|
||||||
|
// TODO: Write annotations to the component
|
||||||
|
// visitor.newRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
|
||||||
|
|
||||||
|
fun generateMethodNode(method: IrFunction): SMAPAndMethodNode {
|
||||||
|
if (!method.isInline && !method.isSuspendCapturingCrossinline()) {
|
||||||
|
// Inline methods can be used multiple times by `IrSourceCompilerForInline`, suspend methods
|
||||||
|
// are used twice (`f` and `f$$forInline`) if they capture crossinline lambdas, and everything
|
||||||
|
// else is only generated by `generateMethod` below so does not need caching.
|
||||||
|
// TODO: inline lambdas are not marked `isInline`, and are generally used once, but may be needed
|
||||||
|
// multiple times if declared in a `finally` block - should they be cached?
|
||||||
|
return FunctionCodegen(method, this).generate()
|
||||||
|
}
|
||||||
|
val (node, smap) = generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
|
||||||
|
val copy = with(node) { MethodNode(Opcodes.API_VERSION, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||||
|
node.instructions.resetLabels()
|
||||||
|
node.accept(copy)
|
||||||
|
return SMAPAndMethodNode(copy, smap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateMethod(method: IrFunction, classSMAP: SourceMapper, delegatedPropertyOptimizer: DelegatedPropertyOptimizer?) {
|
||||||
|
if (method.isFakeOverride) {
|
||||||
|
jvmSignatureClashDetector.trackFakeOverrideMethod(method)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val (node, smap) = generateMethodNode(method)
|
||||||
|
if (delegatedPropertyOptimizer != null) {
|
||||||
|
delegatedPropertyOptimizer.transform(node)
|
||||||
|
if (method.name.asString() == "<clinit>") {
|
||||||
|
delegatedPropertyOptimizer.transformClassInitializer(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.preprocessSuspendMarkers(
|
||||||
|
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE || method.isEffectivelyInlineOnly(),
|
||||||
|
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||||
|
)
|
||||||
|
val mv = with(node) { visitor.newMethod(method.descriptorOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||||
|
val smapCopier = SourceMapCopier(classSMAP, smap)
|
||||||
|
val smapCopyingVisitor = object : MethodVisitor(Opcodes.API_VERSION, mv) {
|
||||||
|
override fun visitLineNumber(line: Int, start: Label) =
|
||||||
|
super.visitLineNumber(smapCopier.mapLineNumber(line), start)
|
||||||
|
}
|
||||||
|
if (method.hasContinuation()) {
|
||||||
|
// Generate a state machine within this method. The continuation class for it should be generated
|
||||||
|
// lazily so that if tail call optimization kicks in, the unused class will not be written to the output.
|
||||||
|
val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
|
||||||
|
val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
|
||||||
|
|
||||||
|
// For suspend lambdas continuation class is null, and we need to use containing class to put L$ fields
|
||||||
|
val attributeContainer = continuationClass?.attributeOwnerId ?: irClass.attributeOwnerId
|
||||||
|
|
||||||
|
node.acceptWithStateMachine(
|
||||||
|
method,
|
||||||
|
this,
|
||||||
|
smapCopyingVisitor,
|
||||||
|
context.continuationClassesVarsCountByType[attributeContainer] ?: emptyMap()
|
||||||
|
) {
|
||||||
|
continuationClassCodegen.value.visitor
|
||||||
|
}
|
||||||
|
|
||||||
|
if (continuationClass != null && (continuationClassCodegen.isInitialized() || method.isSuspendCapturingCrossinline())) {
|
||||||
|
continuationClassCodegen.value.generate()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.accept(smapCopyingVisitor)
|
||||||
|
}
|
||||||
|
jvmSignatureClashDetector.trackMethod(method, RawSignature(node.name, node.desc, MemberKind.METHOD))
|
||||||
|
|
||||||
|
when (val metadata = method.metadata) {
|
||||||
|
is MetadataSource.Property -> {
|
||||||
|
assert(method.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_OR_TYPEALIAS_ANNOTATIONS) {
|
||||||
|
"MetadataSource.Property on IrFunction should only be used for synthetic \$annotations methods: ${method.render()}"
|
||||||
|
}
|
||||||
|
metadataSerializer.bindMethodMetadata(metadata, Method(node.name, node.desc))
|
||||||
|
}
|
||||||
|
is MetadataSource.Function -> metadataSerializer.bindMethodMetadata(metadata, Method(node.name, node.desc))
|
||||||
|
null -> Unit
|
||||||
|
else -> error("Incorrect metadata source $metadata for:\n${method.dump()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateInnerAndOuterClasses() {
|
||||||
|
// JVMS7 (4.7.6): a nested class or interface member will have InnerClasses information
|
||||||
|
// for each enclosing class and for each immediate member
|
||||||
|
parentClassCodegen?.innerClasses?.add(irClass)
|
||||||
|
for (codegen in generateSequence(this) { it.parentClassCodegen }.takeWhile { it.parentClassCodegen != null }) {
|
||||||
|
innerClasses.add(codegen.irClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JVMS7 (4.7.7): A class must have an EnclosingMethod attribute if and only if
|
||||||
|
// it is a local class or an anonymous class.
|
||||||
|
//
|
||||||
|
// The attribute contains the innermost class that encloses the declaration of
|
||||||
|
// the current class. If the current class is immediately enclosed by a method
|
||||||
|
// or constructor, the name and type of the function is recorded as well.
|
||||||
|
if (parentClassCodegen != null) {
|
||||||
|
// In case there's no primary constructor, it's unclear which constructor should be the enclosing one, so we select the first.
|
||||||
|
val enclosingFunction = if (irClass.attributeOwnerId in context.isEnclosedInConstructor) {
|
||||||
|
val containerClass = parentClassCodegen.irClass
|
||||||
|
containerClass.primaryConstructor
|
||||||
|
?: containerClass.declarations.firstIsInstanceOrNull<IrConstructor>()
|
||||||
|
?: error("Class in a non-static initializer found, but container has no constructors: ${containerClass.render()}")
|
||||||
|
} else parentFunction
|
||||||
|
if (enclosingFunction != null || irClass.isAnonymousObject) {
|
||||||
|
val method = enclosingFunction?.let(context.methodSignatureMapper::mapAsmMethod)
|
||||||
|
visitor.visitOuterClass(parentClassCodegen.type.internalName, method?.name, method?.descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (klass in innerClasses) {
|
||||||
|
val innerClass = typeMapper.classInternalName(klass)
|
||||||
|
val outerClass =
|
||||||
|
if (klass.attributeOwnerId in context.isEnclosedInConstructor) null
|
||||||
|
else klass.parent.safeAs<IrClass>()?.let(typeMapper::classInternalName)
|
||||||
|
val innerName = klass.name.takeUnless { it.isSpecial }?.asString()
|
||||||
|
visitor.visitInnerClass(innerClass, outerClass, innerName, klass.calculateInnerClassAccessFlags(context))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addInnerClassInfoFromAnnotation(innerClass: IrClass) {
|
||||||
|
// It's necessary for proper recovering of classId by plain string JVM descriptor when loading annotations
|
||||||
|
// See FileBasedKotlinClass.convertAnnotationVisitor
|
||||||
|
generateSequence<IrDeclaration>(innerClass) { it.parent as? IrDeclaration }.takeWhile { !it.isTopLevelDeclaration }.forEach {
|
||||||
|
if (it is IrClass) {
|
||||||
|
innerClasses.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IrDeclaration.descriptorOrigin: JvmDeclarationOrigin
|
||||||
|
get() {
|
||||||
|
val psiElement = context.psiSourceManager.findPsiElement(this)
|
||||||
|
// For declarations inside lambdas, produce a descriptor which refers back to the original function.
|
||||||
|
// This is needed for plugins which check for lambdas inside of inline functions using the descriptor
|
||||||
|
// contained in JvmDeclarationOrigin. This matches the behavior of the JVM backend.
|
||||||
|
// TODO: this is really not very useful, as this does nothing for other anonymous objects.
|
||||||
|
val isLambda = irClass.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL ||
|
||||||
|
irClass.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA
|
||||||
|
val descriptor = if (isLambda)
|
||||||
|
irClass.attributeOwnerId.safeAs<IrFunctionReference>()?.symbol?.owner?.toIrBasedDescriptor() ?: toIrBasedDescriptor()
|
||||||
|
else
|
||||||
|
toIrBasedDescriptor()
|
||||||
|
return if (origin == IrDeclarationOrigin.FILE_CLASS)
|
||||||
|
JvmDeclarationOrigin(JvmDeclarationOriginKind.PACKAGE_PART, psiElement, descriptor)
|
||||||
|
else
|
||||||
|
OtherOrigin(psiElement, descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IrClass.flags: Int
|
||||||
|
get() = origin.flags or getVisibilityAccessFlagForClass() or
|
||||||
|
(if (isAnnotatedWithDeprecated) Opcodes.ACC_DEPRECATED else 0) or
|
||||||
|
(if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME)) Opcodes.ACC_SYNTHETIC else 0) or
|
||||||
|
when {
|
||||||
|
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||||
|
isInterface -> Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||||
|
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
||||||
|
else -> Opcodes.ACC_SUPER or modality.flags
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
|
||||||
|
origin.flags or visibility.flags or
|
||||||
|
(if (isDeprecatedCallable(context) ||
|
||||||
|
correspondingPropertySymbol?.owner?.isDeprecatedCallable(context) == true
|
||||||
|
) Opcodes.ACC_DEPRECATED else 0) or
|
||||||
|
(if (isFinal) Opcodes.ACC_FINAL else 0) or
|
||||||
|
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||||
|
(if (hasAnnotation(VOLATILE_ANNOTATION_FQ_NAME)) Opcodes.ACC_VOLATILE else 0) or
|
||||||
|
(if (hasAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)) Opcodes.ACC_TRANSIENT else 0) or
|
||||||
|
(if (hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||||
|
isPrivateCompanionFieldInInterface(languageVersionSettings)
|
||||||
|
) Opcodes.ACC_SYNTHETIC else 0)
|
||||||
|
|
||||||
|
private fun IrField.isPrivateCompanionFieldInInterface(languageVersionSettings: LanguageVersionSettings): Boolean =
|
||||||
|
origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE &&
|
||||||
|
languageVersionSettings.supportsFeature(LanguageFeature.ProperVisibilityForCompanionObjectInstanceField) &&
|
||||||
|
parentAsClass.isJvmInterface &&
|
||||||
|
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
|
||||||
|
|
||||||
|
private val IrDeclarationOrigin.flags: Int
|
||||||
|
get() = (if (isSynthetic) Opcodes.ACC_SYNTHETIC else 0) or
|
||||||
|
(if (this == IrDeclarationOrigin.FIELD_FOR_ENUM_ENTRY) Opcodes.ACC_ENUM else 0)
|
||||||
|
|
||||||
|
private val Modality.flags: Int
|
||||||
|
get() = when (this) {
|
||||||
|
Modality.ABSTRACT, Modality.SEALED -> Opcodes.ACC_ABSTRACT
|
||||||
|
Modality.FINAL -> Opcodes.ACC_FINAL
|
||||||
|
Modality.OPEN -> 0
|
||||||
|
else -> throw AssertionError("Unsupported modality $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
private val DescriptorVisibility.flags: Int
|
||||||
|
get() = DescriptorAsmUtil.getVisibilityAccessFlag(this) ?: throw AssertionError("Unsupported visibility $this")
|
||||||
+51
-32
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.ir.visitors.*
|
|||||||
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
|
|
||||||
@@ -166,37 +167,40 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
+ context.irBuiltIns.anyNType
|
+ context.irBuiltIns.anyNType
|
||||||
)
|
)
|
||||||
superTypes = listOf(suspendLambda.defaultType, functionNType)
|
superTypes = listOf(suspendLambda.defaultType, functionNType)
|
||||||
val usedParams = ArrayList<IrSymbolOwner>(function.explicitParameters.size)
|
val usedParams = mutableSetOf<IrSymbolOwner>()
|
||||||
|
|
||||||
// marking the parameters referenced in the function
|
// marking the parameters referenced in the function
|
||||||
function.acceptChildrenVoid(
|
function.acceptChildrenVoid(
|
||||||
object : IrElementVisitorVoid {
|
object : IrElementVisitorVoid {
|
||||||
override fun visitElement(element: IrElement) =
|
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
|
||||||
if (element is IrDeclarationReference && element.symbol is IrValueParameterSymbol && element.symbol.owner in function.explicitParameters)
|
|
||||||
usedParams += element.symbol.owner
|
override fun visitGetValue(expression: IrGetValue) {
|
||||||
else
|
if (expression.symbol is IrValueParameterSymbol && expression.symbol.owner in function.explicitParameters) {
|
||||||
element.acceptChildrenVoid(this)
|
usedParams += expression.symbol.owner
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY)
|
addField(COROUTINE_LABEL_FIELD_NAME, context.irBuiltIns.intType, JavaDescriptorVisibilities.PACKAGE_VISIBILITY)
|
||||||
val varsCountByType = HashMap<Type, Int>()
|
val varsCountByType = HashMap<Type, Int>()
|
||||||
|
|
||||||
val parametersFields = function.explicitParameters.filter { it in usedParams }.map {
|
val parametersFields = function.explicitParameters.map {
|
||||||
addField {
|
val field = if (it in usedParams) addField {
|
||||||
val normalizedType = context.typeMapper.mapType(it.type).normalize()
|
val normalizedType = context.typeMapper.mapType(it.type).normalize()
|
||||||
val index = varsCountByType[normalizedType]?.plus(1) ?: 0
|
val index = varsCountByType[normalizedType]?.plus(1) ?: 0
|
||||||
varsCountByType[normalizedType] = index
|
varsCountByType[normalizedType] = index
|
||||||
// Rename `$this` to avoid being caught by inlineCodegenUtils.isCapturedFieldName()
|
// Rename `$this` to avoid being caught by inlineCodegenUtils.isCapturedFieldName()
|
||||||
name = Name.identifier("${normalizedType.descriptor[0]}$$index")
|
name = Name.identifier("${normalizedType.descriptor[0]}$$index")
|
||||||
type = it.type
|
type = if (normalizedType == AsmTypes.OBJECT_TYPE) context.irBuiltIns.anyNType else it.type
|
||||||
origin = LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE
|
origin = LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CAPTURED_VALUE
|
||||||
isFinal = false
|
isFinal = false
|
||||||
visibility = if (it.index < 0) DescriptorVisibilities.PRIVATE else JavaDescriptorVisibilities.PACKAGE_VISIBILITY
|
visibility = if (it.index < 0) DescriptorVisibilities.PRIVATE else JavaDescriptorVisibilities.PACKAGE_VISIBILITY
|
||||||
}
|
} else null
|
||||||
|
ParameterInfo(field, it.type, it.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
context.continuationClassesVarsCountByType[this] = varsCountByType
|
context.continuationClassesVarsCountByType[attributeOwnerId] = varsCountByType
|
||||||
val constructor = addPrimaryConstructorForLambda(suspendLambda, arity)
|
val constructor = addPrimaryConstructorForLambda(suspendLambda, arity)
|
||||||
val invokeToOverride = functionNClass.functions.single {
|
val invokeToOverride = functionNClass.functions.single {
|
||||||
it.owner.valueParameters.size == arity + 1 && it.owner.name.asString() == "invoke"
|
it.owner.valueParameters.size == arity + 1 && it.owner.name.asString() == "invoke"
|
||||||
@@ -218,26 +222,34 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = function
|
context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = function
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrClass.addInvokeSuspendForLambda(irFunction: IrFunction, suspendLambda: IrClass, fields: List<IrField>): IrSimpleFunction {
|
private fun IrClass.addInvokeSuspendForLambda(
|
||||||
|
irFunction: IrFunction,
|
||||||
|
suspendLambda: IrClass,
|
||||||
|
fields: List<ParameterInfo>
|
||||||
|
): IrSimpleFunction {
|
||||||
val superMethod = suspendLambda.functions.single {
|
val superMethod = suspendLambda.functions.single {
|
||||||
it.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.valueParameters.size == 1 &&
|
it.name.asString() == INVOKE_SUSPEND_METHOD_NAME && it.valueParameters.size == 1 &&
|
||||||
it.valueParameters[0].type.isKotlinResult()
|
it.valueParameters[0].type.isKotlinResult()
|
||||||
}
|
}
|
||||||
return addFunctionOverride(superMethod, irFunction.startOffset, irFunction.endOffset).apply {
|
return addFunctionOverride(superMethod, irFunction.startOffset, irFunction.endOffset).apply {
|
||||||
val localVals: List<IrVariable> = fields.mapIndexed { index, field ->
|
val localVals: List<IrVariable?> = fields.mapIndexed { index, param ->
|
||||||
buildVariable(
|
if (param.isUsed) {
|
||||||
parent = this,
|
buildVariable(
|
||||||
startOffset = UNDEFINED_OFFSET,
|
parent = this,
|
||||||
endOffset = UNDEFINED_OFFSET,
|
startOffset = UNDEFINED_OFFSET,
|
||||||
origin = IrDeclarationOrigin.DEFINED,
|
endOffset = UNDEFINED_OFFSET,
|
||||||
name = field.name,
|
origin = IrDeclarationOrigin.DEFINED,
|
||||||
type = field.type
|
name = param.name,
|
||||||
).apply {
|
type = param.type
|
||||||
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
|
).apply {
|
||||||
val initializerBlock = IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.type)
|
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
|
||||||
initializerBlock.statements += IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol, field.type, receiver)
|
val initializerBlock = IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
||||||
initializer = initializerBlock
|
initializerBlock.statements += IrGetFieldImpl(
|
||||||
}
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, param.field!!.symbol, type, receiver
|
||||||
|
)
|
||||||
|
initializer = initializerBlock
|
||||||
|
}
|
||||||
|
} else null
|
||||||
}
|
}
|
||||||
|
|
||||||
body = irFunction.moveBodyTo(this, mapOf())?.let { body ->
|
body = irFunction.moveBodyTo(this, mapOf())?.let { body ->
|
||||||
@@ -246,10 +258,11 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
|
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
|
||||||
?: return expression
|
?: return expression
|
||||||
val lvar = localVals[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
|
val lvar = localVals[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
|
||||||
|
?: return expression
|
||||||
return IrGetValueImpl(expression.startOffset, expression.endOffset, lvar.symbol)
|
return IrGetValueImpl(expression.startOffset, expression.endOffset, lvar.symbol)
|
||||||
}
|
}
|
||||||
}, null)
|
}, null)
|
||||||
context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, localVals + body.statements)
|
context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, localVals.filterNotNull() + body.statements)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +305,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
constructor: IrFunction,
|
constructor: IrFunction,
|
||||||
invokeSuspend: IrSimpleFunction,
|
invokeSuspend: IrSimpleFunction,
|
||||||
invokeToOverride: IrSimpleFunctionSymbol,
|
invokeToOverride: IrSimpleFunctionSymbol,
|
||||||
fieldsForUnbound: List<IrField>
|
fieldsForUnbound: List<ParameterInfo>
|
||||||
) = addFunctionOverride(invokeToOverride.owner) { function ->
|
) = addFunctionOverride(invokeToOverride.owner) { function ->
|
||||||
+irReturn(callInvokeSuspend(invokeSuspend, cloneLambda(function, constructor, fieldsForUnbound)))
|
+irReturn(callInvokeSuspend(invokeSuspend, cloneLambda(function, constructor, fieldsForUnbound)))
|
||||||
}
|
}
|
||||||
@@ -300,7 +313,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
private fun IrClass.addCreate(
|
private fun IrClass.addCreate(
|
||||||
constructor: IrFunction,
|
constructor: IrFunction,
|
||||||
createToOverride: IrSimpleFunctionSymbol,
|
createToOverride: IrSimpleFunctionSymbol,
|
||||||
fieldsForUnbound: List<IrField>
|
fieldsForUnbound: List<ParameterInfo>
|
||||||
) = addFunctionOverride(createToOverride.owner) { function ->
|
) = addFunctionOverride(createToOverride.owner) { function ->
|
||||||
+irReturn(cloneLambda(function, constructor, fieldsForUnbound))
|
+irReturn(cloneLambda(function, constructor, fieldsForUnbound))
|
||||||
}
|
}
|
||||||
@@ -308,7 +321,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
private fun IrBlockBodyBuilder.cloneLambda(
|
private fun IrBlockBodyBuilder.cloneLambda(
|
||||||
scope: IrFunction,
|
scope: IrFunction,
|
||||||
constructor: IrFunction,
|
constructor: IrFunction,
|
||||||
fieldsForUnbound: List<IrField>
|
fieldsForUnbound: List<ParameterInfo>
|
||||||
): IrExpression {
|
): IrExpression {
|
||||||
val constructorCall = irCall(constructor).also {
|
val constructorCall = irCall(constructor).also {
|
||||||
for (typeParameter in constructor.parentAsClass.typeParameters) {
|
for (typeParameter in constructor.parentAsClass.typeParameters) {
|
||||||
@@ -316,12 +329,14 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
}
|
}
|
||||||
it.putValueArgument(0, irGet(scope.valueParameters.last()))
|
it.putValueArgument(0, irGet(scope.valueParameters.last()))
|
||||||
}
|
}
|
||||||
if (fieldsForUnbound.isEmpty()) {
|
if (fieldsForUnbound.none { it.isUsed }) {
|
||||||
return constructorCall
|
return constructorCall
|
||||||
}
|
}
|
||||||
val result = irTemporary(constructorCall, "result")
|
val result = irTemporary(constructorCall, "result")
|
||||||
for ((index, field) in fieldsForUnbound.withIndex()) {
|
for ((index, field) in fieldsForUnbound.withIndex()) {
|
||||||
+irSetField(irGet(result), field, irGet(scope.valueParameters[index]))
|
if (field.isUsed) {
|
||||||
|
+irSetField(irGet(result), field.field!!, irGet(scope.valueParameters[index]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return irGet(result)
|
return irGet(result)
|
||||||
}
|
}
|
||||||
@@ -348,3 +363,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class ParameterInfo(val field: IrField?, val type: IrType, val name: Name) {
|
||||||
|
val isUsed = field != null
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -28,8 +28,8 @@ suspend fun foo(data: Data, body: suspend Long.(String, Data, Int) -> Unit) {
|
|||||||
// JVM_IR_TEMPLATES
|
// JVM_IR_TEMPLATES
|
||||||
// VARIABLE : NAME=$dstr$x$_u24__u24$z TYPE=LData; INDEX=*
|
// VARIABLE : NAME=$dstr$x$_u24__u24$z TYPE=LData; INDEX=*
|
||||||
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=*
|
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=*
|
||||||
|
// VARIABLE : NAME=$this$foo TYPE=J INDEX=*
|
||||||
// VARIABLE : NAME=i TYPE=I INDEX=*
|
// VARIABLE : NAME=i TYPE=I INDEX=*
|
||||||
// VARIABLE : NAME=p$ TYPE=J INDEX=*
|
|
||||||
// VARIABLE : NAME=str TYPE=Ljava/lang/String; INDEX=*
|
// VARIABLE : NAME=str TYPE=Ljava/lang/String; INDEX=*
|
||||||
// VARIABLE : NAME=this TYPE=LParametersKt$test$2; INDEX=*
|
// VARIABLE : NAME=this TYPE=LParametersKt$test$2; INDEX=*
|
||||||
// VARIABLE : NAME=x TYPE=Ljava/lang/String; INDEX=*
|
// VARIABLE : NAME=x TYPE=Ljava/lang/String; INDEX=*
|
||||||
|
|||||||
+1
-1
@@ -3,8 +3,8 @@
|
|||||||
final class LambdaWithLongReceiverKt$box$1$1 {
|
final class LambdaWithLongReceiverKt$box$1$1 {
|
||||||
// source: 'lambdaWithLongReceiver.kt'
|
// source: 'lambdaWithLongReceiver.kt'
|
||||||
enclosing method LambdaWithLongReceiverKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
|
enclosing method LambdaWithLongReceiverKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
private synthetic field J$0: long
|
||||||
field label: int
|
field label: int
|
||||||
private synthetic field p$: long
|
|
||||||
inner (anonymous) class LambdaWithLongReceiverKt$box$1
|
inner (anonymous) class LambdaWithLongReceiverKt$box$1
|
||||||
inner (anonymous) class LambdaWithLongReceiverKt$box$1$1
|
inner (anonymous) class LambdaWithLongReceiverKt$box$1$1
|
||||||
method <init>(p0: kotlin.coroutines.Continuation): void
|
method <init>(p0: kotlin.coroutines.Continuation): void
|
||||||
|
|||||||
+6
-6
@@ -3,12 +3,12 @@
|
|||||||
final class LambdaWithMultipleParametersKt$box$1$1 {
|
final class LambdaWithMultipleParametersKt$box$1$1 {
|
||||||
// source: 'lambdaWithMultipleParameters.kt'
|
// source: 'lambdaWithMultipleParameters.kt'
|
||||||
enclosing method LambdaWithMultipleParametersKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
|
enclosing method LambdaWithMultipleParametersKt$box$1.invokeSuspend(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
synthetic field a: java.lang.String
|
synthetic field J$0: long
|
||||||
synthetic field b: long
|
synthetic field J$1: long
|
||||||
synthetic field c: long
|
synthetic field J$2: long
|
||||||
synthetic field d: long
|
synthetic field J$3: long
|
||||||
synthetic field e: long
|
synthetic field J$4: long
|
||||||
synthetic field f: long
|
synthetic field L$0: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
inner (anonymous) class LambdaWithMultipleParametersKt$box$1
|
inner (anonymous) class LambdaWithMultipleParametersKt$box$1
|
||||||
inner (anonymous) class LambdaWithMultipleParametersKt$box$1$1
|
inner (anonymous) class LambdaWithMultipleParametersKt$box$1$1
|
||||||
|
|||||||
+1
-2
@@ -44,10 +44,9 @@ final class CoroutineFieldsKt$box$1 {
|
|||||||
enclosing method CoroutineFieldsKt.box()Ljava/lang/String;
|
enclosing method CoroutineFieldsKt.box()Ljava/lang/String;
|
||||||
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
|
synthetic final field $result: kotlin.jvm.internal.Ref$ObjectRef
|
||||||
field J$0: long
|
field J$0: long
|
||||||
field L$0: java.lang.Object
|
private synthetic field L$0: java.lang.Object
|
||||||
field L$1: java.lang.Object
|
field L$1: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
private synthetic field p$: Controller
|
|
||||||
inner (anonymous) class CoroutineFieldsKt$box$1
|
inner (anonymous) class CoroutineFieldsKt$box$1
|
||||||
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void
|
method <init>(p0: kotlin.jvm.internal.Ref$ObjectRef, p1: kotlin.coroutines.Continuation): void
|
||||||
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ final class BooleanParameterKt$box$1 {
|
|||||||
final class BooleanParameterKt$box$lambda$1 {
|
final class BooleanParameterKt$box$lambda$1 {
|
||||||
// source: 'booleanParameter.kt'
|
// source: 'booleanParameter.kt'
|
||||||
enclosing method BooleanParameterKt.box()Ljava/lang/String;
|
enclosing method BooleanParameterKt.box()Ljava/lang/String;
|
||||||
synthetic field it: boolean
|
synthetic field Z$0: boolean
|
||||||
field label: int
|
field label: int
|
||||||
inner (anonymous) class BooleanParameterKt$box$lambda$1
|
inner (anonymous) class BooleanParameterKt$box$lambda$1
|
||||||
method <init>(p0: kotlin.coroutines.Continuation): void
|
method <init>(p0: kotlin.coroutines.Continuation): void
|
||||||
|
|||||||
+1
-1
@@ -3,8 +3,8 @@
|
|||||||
final class Component1Kt$test$1 {
|
final class Component1Kt$test$1 {
|
||||||
// source: 'component1.kt'
|
// source: 'component1.kt'
|
||||||
enclosing method Component1Kt.test()V
|
enclosing method Component1Kt.test()V
|
||||||
|
private synthetic field L$0: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
private synthetic field p$: Foo
|
|
||||||
inner (anonymous) class Component1Kt$test$1
|
inner (anonymous) class Component1Kt$test$1
|
||||||
method <init>(p0: kotlin.coroutines.Continuation): void
|
method <init>(p0: kotlin.coroutines.Continuation): void
|
||||||
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
final class FieldKt$test$1 {
|
final class FieldKt$test$1 {
|
||||||
// source: 'field.kt'
|
// source: 'field.kt'
|
||||||
enclosing method FieldKt.test()V
|
enclosing method FieldKt.test()V
|
||||||
|
private synthetic field L$0: java.lang.Object
|
||||||
field label: int
|
field label: int
|
||||||
private synthetic field p$: Foo
|
|
||||||
inner (anonymous) class FieldKt$test$1
|
inner (anonymous) class FieldKt$test$1
|
||||||
method <init>(p0: kotlin.coroutines.Continuation): void
|
method <init>(p0: kotlin.coroutines.Continuation): void
|
||||||
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation
|
||||||
|
|||||||
Reference in New Issue
Block a user