diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Lower.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Lower.kt index 3dd02d18014..34932c56eab 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Lower.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Lower.kt @@ -17,9 +17,12 @@ package org.jetbrains.kotlin.backend.common import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.IrStatementContainer import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.util.transformSubsetFlat import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -111,11 +114,6 @@ private class DeclarationContainerLoweringVisitor( declaration.acceptChildrenVoid(this) loweringPass.lower(declaration) } - - override fun visitScript(declaration: IrScript) { - declaration.acceptChildrenVoid(this) - loweringPass.lower(declaration) - } } fun BodyLoweringPass.runOnFilePostfix( @@ -160,11 +158,8 @@ private class BodyLoweringVisitor( } override fun visitScript(declaration: IrScript, data: IrDeclaration?) { - ArrayList(declaration.declarations).forEach { it.accept(this, declaration) } - if (withLocalDeclarations) { - declaration.statements.forEach { it.accept(this, null) } - } declaration.thisReceiver.accept(this, declaration) + ArrayList(declaration.statements).forEach { it.accept(this, declaration) } } } @@ -214,22 +209,26 @@ interface DeclarationTransformer : FileLoweringPass { val visitor = this + fun IrDeclaration.replaceInContainer(container: MutableList, result: List): Boolean { + var index = container.indexOf(this) + if (index == -1) { + index = container.indexOf(declaration) + } else { + container.removeAt(index) + --index + } + return container.addAll(index + 1, result) + } + fun IrDeclaration.transform() { acceptVoid(visitor) val result = transformer.transformFlatRestricted(this) if (result != null) { - (parent as? IrDeclarationContainer)?.let { - var index = it.declarations.indexOf(this) - if (index == -1) { - index = it.declarations.indexOf(declaration) - } else { - it.declarations.removeAt(index) - --index - } - - it.declarations.addAll(index + 1, result) + when (val parentCopy = parent) { + is IrDeclarationContainer -> replaceInContainer(parentCopy.declarations, result) + is IrStatementContainer -> replaceInContainer(parentCopy.statements, result) } } } @@ -248,12 +247,12 @@ interface DeclarationTransformer : FileLoweringPass { } override fun visitScript(declaration: IrScript) { - ArrayList(declaration.declarations).forEach { it.accept(this, null) } - declaration.declarations.transformFlat(transformer::transformFlatRestricted) - - if (transformer.withLocalDeclarations) { - declaration.statements.forEach { it.accept(this, null) } + ArrayList(declaration.statements).forEach { + if (transformer.withLocalDeclarations || it is IrDeclaration) { + it.accept(this, null) + } } + declaration.statements.transformSubsetFlat(transformer::transformFlatRestricted) declaration.thisReceiver.accept(this, null) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt index 46df03a6163..39452721e20 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LocalClassPopupLowering.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.ir.addChild +import org.jetbrains.kotlin.backend.common.ir.setDeclarationsParent import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrBody +import org.jetbrains.kotlin.ir.expressions.IrStatementContainer import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -22,8 +24,12 @@ open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPa runOnFilePostfix(irFile, withLocalDeclarations = true, allowDeclarationModification = true) } + private data class ExtractedLocalClass( + val local: IrClass, val newContainer: IrDeclarationParent, val extractedUnder: IrStatement? + ) + override fun lower(irBody: IrBody, container: IrDeclaration) { - val extractedLocalClasses = arrayListOf>() + val extractedLocalClasses = arrayListOf() irBody.transform(object : IrElementTransformerVoidWithContext() { @@ -32,22 +38,46 @@ open class LocalClassPopupLowering(val context: BackendContext) : BodyLoweringPa if (allScopes.size > 1) allScopes[allScopes.lastIndex - 1] else createScope(container as IrSymbolOwner) if (!shouldPopUp(declaration, currentScope)) return declaration - val newContainer = run { - var currentParent = declaration.parent - while (currentParent is IrDeclaration && currentParent !is IrClass && currentParent !is IrScript) { - currentParent = currentParent.parent + var extractedUnder: IrStatement? = declaration + var newContainer = declaration.parent + while (newContainer is IrDeclaration && newContainer !is IrClass && newContainer !is IrScript) { + extractedUnder = newContainer + newContainer = newContainer.parent + } + when (newContainer) { + is IrStatementContainer -> { + // TODO: check if it is the correct behavior + if (extractedUnder == declaration) { + extractedUnder = (newContainer.statements.indexOf(extractedUnder) + 1) + .takeIf { it > 0 && it < newContainer.statements.size } + ?.let { newContainer.statements[it] } + } + extractedLocalClasses.add(ExtractedLocalClass(declaration, newContainer, extractedUnder)) } - - currentParent as IrDeclarationContainer // IrClass or IrScript or IrPackageFragment + is IrDeclarationContainer -> extractedLocalClasses.add(ExtractedLocalClass(declaration, newContainer, extractedUnder)) + else -> error("Inexpected container type $newContainer") } - extractedLocalClasses.add(declaration to newContainer) return IrCompositeImpl(declaration.startOffset, declaration.endOffset, context.irBuiltIns.unitType) } }, null) - for ((local, newContainer) in extractedLocalClasses) { - newContainer.addChild(local) + for ((local, newContainer, extractedUnder) in extractedLocalClasses) { + when (newContainer) { + is IrStatementContainer -> { + val insertIndex = extractedUnder?.let { newContainer.statements.indexOf(it) } ?: -1 + if (insertIndex >= 0) { + newContainer.statements.add(insertIndex, local) + } else { + newContainer.statements.add(local) + } + local.setDeclarationsParent(newContainer) + } + is IrDeclarationContainer -> { + newContainer.addChild(local) + } + else -> error("Inexpected container type $newContainer") + } local.acceptVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index de30feb8ee4..cf606ccaada 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -19,9 +19,9 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.BackendContext import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl -import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* @@ -162,6 +162,12 @@ open class IrBuildingTransformer(private val context: BackendContext) : IrElemen return super.visitEnumEntry(declaration) } } + + override fun visitScript(declaration: IrScript): IrStatement { + withBuilder(declaration.symbol) { + return super.visitScript(declaration) + } + } } fun IrConstructor.callsSuper(irBuiltIns: IrBuiltIns): Boolean { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt index 6a48e8b5cf0..dec10d32c08 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt @@ -117,10 +117,6 @@ class BlockDecomposerTransformer( function = declaration with(declaration) { - val transformedDeclarations = declarations.map { it.transform(statementTransformer, null) as IrDeclaration } - declarations.clear() - declarations.addAll(transformedDeclarations) - val transformedStatements = mutableListOf() statements.forEach { val transformer = if (it === statements.last()) expressionTransformer else statementTransformer diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt index bf3c94fe70e..7aaedc68a59 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CreateScriptFunctionsPhase.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.util.transformFlat import org.jetbrains.kotlin.name.Name @@ -35,7 +36,7 @@ class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweri private fun lower(irScript: IrScript): List { val (startOffset, endOffset) = getFunctionBodyOffsets(irScript) - val initializeStatements = irScript.declarations + val initializeStatements = irScript.statements .filterIsInstance() .mapNotNull { it.backingField } .filter { it.initializer != null } @@ -55,14 +56,16 @@ class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweri it.body = it.factory.createBlockBody( startOffset, endOffset, - irScript.statements.prepareForEvaluateScriptFunction(it) + irScript.statements.filter { it !is IrDeclaration }.prepareForEvaluateScriptFunction(it) ) } with(irScript) { - declarations += initializeScriptFunction - declarations += evaluateScriptFunction - statements.clear() + statements.removeIf { it !is IrDeclaration } + statements += initializeScriptFunction + initializeScriptFunction.patchDeclarationParents(this) + statements += evaluateScriptFunction + evaluateScriptFunction.patchDeclarationParents(this) statements += createCall(initializeScriptFunction) statements += createCall(evaluateScriptFunction) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt index 994000f5eb0..87cadb725da 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ScriptRemoveReceiverLowering.kt @@ -152,7 +152,7 @@ class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLowe script.transformChildrenVoid(transformer) - script.declarations.forEach { + script.statements.forEach { when (it) { is IrSimpleFunction -> it.dispatchReceiverParameter = null is IrProperty -> { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt index 856a6f68070..6941fa43863 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/StaticMembersLowering.kt @@ -29,8 +29,19 @@ class StaticMembersLowering(val context: JsIrBackendContext) : DeclarationTransf if (declaration.isExported(context)) { context.additionalExportedDeclarations.add(declaration) } + var extractedUnder = declaration + var newContainer = declaration.parent + while (newContainer is IrDeclaration && newContainer != irClass.file) { + extractedUnder = newContainer + newContainer = newContainer.parent + } + val insertBefore = irClass.file.declarations.indexOf(extractedUnder) + if (insertBefore >= 0) { + irClass.file.declarations.add(insertBefore, declaration) + } else { + irClass.file.declarations += declaration + } declaration.parent = irClass.file - irClass.file.declarations += declaration return listOf() } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt index 7ba9b9c1803..1bbe466cf31 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrDeclarationToJsTransformer.kt @@ -52,8 +52,11 @@ class IrDeclarationToJsTransformer : BaseIrElementToJsNodeTransformer + declareSimpleFunctionInner(descriptor, ktElement, IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR).buildWithScope { irAccessor -> declarationGenerator.generateScopedTypeParameterDeclarations(irAccessor, descriptor.typeParameters) irAccessor.returnType = descriptor.returnType!!.toIrType() FunctionGenerator(declarationGenerator).generateSyntheticFunctionParameterDeclarations(irAccessor) diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/PropertyGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/PropertyGenerator.kt index 57babedd01b..341cb71a613 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/PropertyGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/PropertyGenerator.kt @@ -16,10 +16,11 @@ package org.jetbrains.kotlin.psi2ir.generators -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpressionBody import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl @@ -41,38 +42,74 @@ class PropertyGenerator(declarationGenerator: DeclarationGenerator) : Declaratio generateSimpleProperty(ktProperty, propertyDescriptor) } + fun generateDestructuringDeclarationEntryAsPropertyDeclaration(ktEntry: KtDestructuringDeclarationEntry, entryInitializer: IrExpression): IrProperty { + val propertyDescriptor = getPropertyDescriptor(ktEntry) + return context.symbolTable.declareProperty( + ktEntry.startOffsetSkippingComments, ktEntry.endOffset, + IrDeclarationOrigin.DEFINED, + propertyDescriptor, + isDelegated = false + ).buildWithScope { irProperty -> + irProperty.backingField = generatePropertyBackingField(ktEntry, propertyDescriptor) { null } + + irProperty.getter = generateGetterIfRequired(ktEntry, propertyDescriptor) + irProperty.setter = generateSetterIfRequired(ktEntry, propertyDescriptor) + + irProperty.linkCorrespondingPropertySymbol() + } + } + fun generatePropertyForPrimaryConstructorParameter(ktParameter: KtParameter, irValueParameter: IrValueParameter): IrDeclaration { val propertyDescriptor = getOrFail(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, ktParameter) + return generateSyntheticProperty(ktParameter, propertyDescriptor, irValueParameter) + } + fun generateSyntheticProperty( + ktDeclarationContainer: KtElement, propertyDescriptor: PropertyDescriptor, + irValueParameter: IrValueParameter?, generateSyntheticAccessors: Boolean = false + ): IrProperty { val irPropertyType = propertyDescriptor.type.toIrType() return context.symbolTable.declareProperty( - ktParameter.startOffsetSkippingComments, ktParameter.endOffset, + ktDeclarationContainer.startOffsetSkippingComments, ktDeclarationContainer.endOffset, IrDeclarationOrigin.DEFINED, propertyDescriptor, isDelegated = false ).also { irProperty -> irProperty.backingField = - generatePropertyBackingField(ktParameter, propertyDescriptor) { - context.irFactory.createExpressionBody( - IrGetValueImpl( - ktParameter.startOffsetSkippingComments, ktParameter.endOffset, - irPropertyType, - irValueParameter.symbol, - IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER + generatePropertyBackingField(ktDeclarationContainer, propertyDescriptor) { + if (irValueParameter == null) null + else { + context.irFactory.createExpressionBody( + IrGetValueImpl( + ktDeclarationContainer.startOffsetSkippingComments, ktDeclarationContainer.endOffset, + irPropertyType, + irValueParameter.symbol, + IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER + ) ) - ) + } } val getter = propertyDescriptor.getter - ?: throw AssertionError("Property declared in primary constructor has no getter: $propertyDescriptor") + ?: if (generateSyntheticAccessors) + // TODO: check if this is the correct way to generate synthetic accessor + PropertyGetterDescriptorImpl( + propertyDescriptor, + Annotations.EMPTY, Modality.FINAL, DescriptorVisibilities.PUBLIC, false, false, false, + CallableMemberDescriptor.Kind.SYNTHESIZED, null, propertyDescriptor.source + ).apply { + initialize(propertyDescriptor.type) + } + else throw AssertionError("Property declared in primary constructor has no getter: $propertyDescriptor") irProperty.getter = - FunctionGenerator(declarationGenerator).generateDefaultAccessorForPrimaryConstructorParameter(getter, ktParameter) + FunctionGenerator(declarationGenerator).generateDefaultAccessorForPrimaryConstructorParameter(getter, ktDeclarationContainer) if (propertyDescriptor.isVar) { val setter = propertyDescriptor.setter + // TODO: implement setter descriptor generation, if needed ?: throw AssertionError("Property declared in primary constructor has no setter: $propertyDescriptor") irProperty.setter = - FunctionGenerator(declarationGenerator).generateDefaultAccessorForPrimaryConstructorParameter(setter, ktParameter) + FunctionGenerator(declarationGenerator).generateDefaultAccessorForPrimaryConstructorParameter(setter, ktDeclarationContainer) } irProperty.linkCorrespondingPropertySymbol() @@ -105,7 +142,7 @@ class PropertyGenerator(declarationGenerator: DeclarationGenerator) : Declaratio private fun PropertyDescriptor.actuallyHasBackingField(bindingContext: BindingContext) = hasBackingField(bindingContext) || context.extensions.isPropertyWithPlatformField(this) - private fun generateSimpleProperty(ktProperty: KtProperty, propertyDescriptor: PropertyDescriptor): IrProperty = + private fun generateSimpleProperty(ktProperty: KtVariableDeclaration, propertyDescriptor: PropertyDescriptor): IrProperty = context.symbolTable.declareProperty( ktProperty.startOffsetSkippingComments, ktProperty.endOffset, IrDeclarationOrigin.DEFINED, @@ -154,18 +191,18 @@ class PropertyGenerator(declarationGenerator: DeclarationGenerator) : Declaratio } } - private fun generateGetterIfRequired(ktProperty: KtProperty, property: PropertyDescriptor): IrSimpleFunction? { + private fun generateGetterIfRequired(ktProperty: KtVariableDeclaration, property: PropertyDescriptor): IrSimpleFunction? { val getter = property.getter ?: return null - return FunctionGenerator(declarationGenerator).generatePropertyAccessor(getter, ktProperty, ktProperty.getter) + return FunctionGenerator(declarationGenerator).generatePropertyAccessor(getter, ktProperty, (ktProperty as? KtProperty)?.getter) } - private fun generateSetterIfRequired(ktProperty: KtProperty, property: PropertyDescriptor): IrSimpleFunction? { + private fun generateSetterIfRequired(ktProperty: KtVariableDeclaration, property: PropertyDescriptor): IrSimpleFunction? { if (!property.isVar) return null val setter = property.setter ?: return null - return FunctionGenerator(declarationGenerator).generatePropertyAccessor(setter, ktProperty, ktProperty.setter) + return FunctionGenerator(declarationGenerator).generatePropertyAccessor(setter, ktProperty, (ktProperty as? KtProperty)?.setter) } - private fun getPropertyDescriptor(ktProperty: KtProperty): PropertyDescriptor { + private fun getPropertyDescriptor(ktProperty: KtVariableDeclaration): PropertyDescriptor { val variableDescriptor = getOrFail(BindingContext.VARIABLE, ktProperty) return variableDescriptor as? PropertyDescriptor ?: TODO("not a property: $variableDescriptor") } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt index cd28b7c9baa..b4277521399 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/ScriptGenerator.kt @@ -5,14 +5,36 @@ package org.jetbrains.kotlin.psi2ir.generators +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ScriptDescriptor -import org.jetbrains.kotlin.ir.declarations.IrDeclaration -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.assertCast +import org.jetbrains.kotlin.ir.builders.declarations.IrFunctionBuilder +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyFunction +import org.jetbrains.kotlin.ir.descriptors.WrappedFunctionDescriptorWithContainerSource +import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl +import org.jetbrains.kotlin.ir.util.varargElementType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtScript import org.jetbrains.kotlin.psi.KtScriptInitializer +import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.pureEndOffset import org.jetbrains.kotlin.psi.psiUtil.pureStartOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffsetSkippingComments +import org.jetbrains.kotlin.psi2ir.deparenthesize +import org.jetbrains.kotlin.psi2ir.intermediate.createTemporaryVariableInBlock +import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationGeneratorExtension(declarationGenerator) { fun generateScriptDeclaration(ktScript: KtScript): IrDeclaration? { @@ -29,20 +51,102 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG val startOffset = ktScript.pureStartOffset val endOffset = ktScript.pureEndOffset - irScript.thisReceiver = context.symbolTable.declareValueParameter( - startOffset, endOffset, - IrDeclarationOrigin.INSTANCE_RECEIVER, - descriptor.thisAsReceiverParameter, - descriptor.thisAsReceiverParameter.type.toIrType() - ).also { it.parent = irScript } + fun makeReceiver(descriptor: ClassDescriptor): IrValueParameter { + val receiverParameterDescriptor = descriptor.thisAsReceiverParameter + return context.symbolTable.declareValueParameter( + startOffset, endOffset, + IrDeclarationOrigin.INSTANCE_RECEIVER, + receiverParameterDescriptor, + receiverParameterDescriptor.type.toIrType() + ).also { it.parent = irScript } + } + + irScript.thisReceiver = makeReceiver(descriptor) for (d in ktScript.declarations) { - if (d is KtScriptInitializer) irScript.statements += BodyGenerator( - irScript.symbol, - context - ).generateExpressionBody(d.body!!).expression - else irScript.declarations += declarationGenerator.generateMemberDeclaration(d)!! + when (d) { + is KtScriptInitializer -> { + irScript.statements += BodyGenerator( + irScript.symbol, + context + ).generateExpressionBody(d.body!!).expression + } + is KtDestructuringDeclaration -> { + // copied with modifications from StatementGenerator.visitDestructuringDeclaration + // TODO: consider code deduplication + val bodyGenerator = BodyGenerator(irScript.symbol, context) + val statementGenerator = bodyGenerator.createStatementGenerator() + val irBlock = IrCompositeImpl( + d.startOffsetSkippingComments, d.endOffset, + context.irBuiltIns.unitType, IrStatementOrigin.DESTRUCTURING_DECLARATION + ) + val ktInitializer = d.initializer!! + val initializerExpr = ktInitializer.deparenthesize().accept(statementGenerator, null).assertCast() + val containerValue = + statementGenerator.scope.createTemporaryVariableInBlock(context, initializerExpr, irBlock, "container") + + val callGenerator = CallGenerator(statementGenerator) + + for ((index, ktEntry) in d.entries.withIndex()) { + val componentResolvedCall = getOrFail(BindingContext.COMPONENT_RESOLVED_CALL, ktEntry) + + val componentSubstitutedCall = statementGenerator.pregenerateCall(componentResolvedCall) + componentSubstitutedCall.setExplicitReceiverValue(containerValue) + + val componentVariable = getOrFail(BindingContext.VARIABLE, ktEntry) + + // componentN for '_' SHOULD NOT be evaluated + if (componentVariable.name.isSpecial || ktEntry.isSingleUnderscore) continue + + val irComponentCall = callGenerator.generateCall( + ktEntry.startOffsetSkippingComments, ktEntry.endOffset, componentSubstitutedCall, + IrStatementOrigin.COMPONENT_N.withIndex(index + 1) + ) + + val irComponentProperty = + PropertyGenerator(declarationGenerator).generateDestructuringDeclarationEntryAsPropertyDeclaration( + ktEntry, irComponentCall + ) + val irComponentBackingField = irComponentProperty.backingField!! + + irScript.statements += irComponentProperty + + val irComponentInitializer = IrSetFieldImpl( + ktEntry.startOffsetSkippingComments, ktEntry.endOffset, + irComponentBackingField.symbol, + context.irBuiltIns.unitType, + origin = null, superQualifierSymbol = null + ).apply { + value = irComponentCall + receiver = IrGetValueImpl( + ktEntry.startOffsetSkippingComments, ktEntry.endOffset, irScript.thisReceiver.symbol + ) + } + irBlock.statements.add(irComponentInitializer) + } + irScript.statements += irBlock + } + else -> irScript.statements += declarationGenerator.generateMemberDeclaration(d)!! + } + } + + descriptor.resultValue?.let { resultDescriptor -> + // TODO: initializer + // TODO: do not keet direct link + val resultProperty = + PropertyGenerator(declarationGenerator) + .generateSyntheticProperty(ktScript, resultDescriptor, null, generateSyntheticAccessors = true) + resultProperty.origin = IrDeclarationOrigin.SCRIPT_RESULT_PROPERTY + irScript.statements += resultProperty } } } + + private fun ParameterDescriptor.toIrValueParameter(startOffset: Int, endOffset: Int, origin: IrDeclarationOrigin) = + context.symbolTable.declareValueParameter( + startOffset, endOffset, origin, + this, + type.toIrType(), + varargElementType?.toIrType() + ) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrScript.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrScript.kt index 7a05b835510..ec3b4c08c40 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrScript.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/IrScript.kt @@ -5,14 +5,14 @@ package org.jetbrains.kotlin.ir.declarations -import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.expressions.IrStatementContainer import org.jetbrains.kotlin.ir.symbols.IrScriptSymbol //TODO: make IrScript as IrPackageFragment, because script is used as a file, not as a class //NOTE: declarations and statements stored separately abstract class IrScript : - IrDeclarationBase(), IrSymbolDeclaration, IrDeclarationContainer, IrDeclarationWithName, IrDeclarationParent { - abstract val statements: MutableList + IrDeclarationBase(), IrSymbolDeclaration, IrDeclarationWithName, + IrDeclarationParent, IrStatementContainer { // NOTE: is the result of the FE conversion, because there script interpreted as a class and has receiver abstract var thisReceiver: IrValueParameter diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrScriptImpl.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrScriptImpl.kt index 50a7d8db8a9..9d8ff5558d6 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrScriptImpl.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/impl/IrScriptImpl.kt @@ -18,7 +18,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.SmartList -private val SCRIPT_ORIGIN = object : IrDeclarationOriginImpl("FIELD_FOR_OBJECT_INSTANCE") {} +private val SCRIPT_ORIGIN = object : IrDeclarationOriginImpl("SCRIPT") {} class IrScriptImpl( override val symbol: IrScriptSymbol, @@ -38,7 +38,6 @@ class IrScriptImpl( override var annotations: List = SmartList() - override val declarations: MutableList = mutableListOf() override val statements: MutableList = mutableListOf() override lateinit var thisReceiver: IrValueParameter @@ -59,13 +58,11 @@ class IrScriptImpl( } override fun acceptChildren(visitor: IrElementVisitor, data: D) { - declarations.forEach { it.accept(visitor, data) } statements.forEach { it.accept(visitor, data) } thisReceiver.accept(visitor, data) } override fun transformChildren(transformer: IrElementTransformer, data: D) { - declarations.transformInPlace(transformer, data) statements.transformInPlace(transformer, data) thisReceiver = thisReceiver.transform(transformer, data) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt index 2a1bc1313d4..59dcd417ef4 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeepCopyIrTreeWithSymbols.kt @@ -117,10 +117,9 @@ open class DeepCopyIrTreeWithSymbols( //TODO: something may go wrong, because expected using symbolRemapper IrScriptSymbolImpl(declaration.descriptor as ScriptDescriptor), declaration.name - ).also { - it.thisReceiver = declaration.thisReceiver.transform() - declaration.transformDeclarationsTo(it) - it.statements.addAll(declaration.statements.map { it.transform() }) + ).also { scriptCopy -> + scriptCopy.thisReceiver = declaration.thisReceiver.transform() + declaration.statements.mapTo(scriptCopy.statements) { it.transform() } } } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt index 15499835b6d..aa68c6acf8d 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/InlineClasses.kt @@ -51,6 +51,7 @@ private tailrec fun erase(type: IrType): IrClass? { return when (classifier) { is IrClassSymbol -> classifier.owner + is IrScriptSymbol -> null // TODO: check if correct is IrTypeParameterSymbol -> erase(classifier.owner.superTypes.first()) else -> error(classifier) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/transform.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/transform.kt index 77496f9351e..95601418c3c 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/transform.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/transform.kt @@ -46,19 +46,42 @@ inline fun MutableList.transformFlat(transformation: (T) -> List?) { while (i < size) { val item = get(i) - val transformed = transformation(item) + i = replaceInPlace(transformation(item), i) + } +} - when (transformed?.size) { - null -> i++ - 0 -> removeAt(i) - 1 -> set(i++, transformed.first()) - else -> { - addAll(i, transformed) - i += transformed.size - removeAt(i) - } +/** + * Transforms a subset of a mutable list in place. + * Each element `it` that has a type S is replaced with a result of `transformation(it)`, + * `null` means "keep existing element" (to avoid creating excessive singleton lists). + */ +inline fun MutableList.transformSubsetFlat(transformation: (S) -> List?) { + var i = 0 + while (i < size) { + val item = get(i) + + if (item !is S) { + i++ + continue + } + + i = replaceInPlace(transformation(item), i) + } +} + +@PublishedApi internal fun MutableList.replaceInPlace(transformed: List?, atIndex: Int): Int { + var i = atIndex + when (transformed?.size) { + null -> i++ + 0 -> removeAt(i) + 1 -> set(i++, transformed.first()) + else -> { + addAll(i, transformed) + i += transformed.size + removeAt(i) } } + return i } /** diff --git a/libraries/scripting/js-test/test/org/jetbrains/kotlin/scripting/repl/js/test/ReplTest.kt b/libraries/scripting/js-test/test/org/jetbrains/kotlin/scripting/repl/js/test/ReplTest.kt index 60d5bbf5d11..bd72817ec26 100644 --- a/libraries/scripting/js-test/test/org/jetbrains/kotlin/scripting/repl/js/test/ReplTest.kt +++ b/libraries/scripting/js-test/test/org/jetbrains/kotlin/scripting/repl/js/test/ReplTest.kt @@ -143,7 +143,10 @@ abstract class AbstractReplTestRunner : TestCase() { var t = 2 * 2 class A(val value: Int = 5) { fun bar(): Int { - var q = 4 + class B(val value: Int = 4) { + fun baz(): Int = value + } + var q = B().baz() var w = 1 return q + w } @@ -221,9 +224,11 @@ abstract class AbstractReplTestRunner : TestCase() { if (compileResult !is ReplCompileResult.CompiledClasses) return compileResult.toString() val evalResult = tester.evaluate(compileResult) - if (evalResult !is ReplEvalResult.ValueResult) return evalResult.toString() - - result = evalResult.value + when (evalResult) { + is ReplEvalResult.Error.Runtime -> return evalResult.cause.toString() + !is ReplEvalResult.ValueResult -> return evalResult.toString() + else -> result = evalResult.value + } } } return result