diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 5d00ef63d2e..7f0b08e9b79 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -389,7 +389,7 @@ val jvmPhases = NamedCompilerPhase( lower = validateIrBeforeLowering then processOptionalAnnotationsPhase then expectDeclarationsRemovingPhase then - scriptToClassPhase then + scriptsToClassesPhase then fileClassPhase then performByIrFile(lower = jvmFilePhases) then generateMultifileFacadesPhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt index 5ff9cd8bab5..be91a1749a4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt @@ -5,11 +5,10 @@ package org.jetbrains.kotlin.backend.jvm.lower -import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase +import org.jetbrains.kotlin.backend.common.phaser.makeCustomPhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -36,23 +35,40 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -internal val scriptToClassPhase = makeIrModulePhase( - ::ScriptToClassLowering, - name = "ScriptToClass", - description = "Put script declarations into a class", - stickyPostconditions = setOf(::checkAllFileLevelDeclarationsAreClasses) +internal val scriptsToClassesPhase = makeCustomPhase( + name = "ScriptsToClasses", + description = "Put script declarations into classes", + op = { context, input -> + ScriptsToClassesLowering(context).lower(input) + } ) -private class ScriptToClassLowering(val context: JvmBackendContext) : FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.declarations.replaceAll { declaration -> - if (declaration is IrScript) makeScriptClass(irFile, declaration) - else declaration +private class ScriptsToClassesLowering(val context: JvmBackendContext) { + + fun lower(module: IrModuleFragment) { + val scriptsToClasses = mutableMapOf() + + for (irFile in module.files) { + val iterator = irFile.declarations.listIterator() + while (iterator.hasNext()) { + val declaration = iterator.next() + if (declaration is IrScript) { + val scriptClass = prepareScriptClass(irFile, declaration) + scriptsToClasses[declaration] = scriptClass + iterator.set(scriptClass) + } + } + } + + val symbolRemapper = ScriptsToClassesSymbolRemapper(scriptsToClasses) + + for ((irScript, irScriptClass) in scriptsToClasses) { + finalizeScriptClass(irScriptClass, irScript, symbolRemapper) } } - private fun makeScriptClass(irFile: IrFile, irScript: IrScript): IrClass { + private fun prepareScriptClass(irFile: IrFile, irScript: IrScript): IrClass { val fileEntry = irFile.fileEntry return context.irFactory.buildClass { startOffset = 0 @@ -65,83 +81,93 @@ private class ScriptToClassLowering(val context: JvmBackendContext) : FileLoweri }.also { irScriptClass -> irScriptClass.superTypes += context.irBuiltIns.anyType irScriptClass.parent = irFile - irScriptClass.createImplicitParameterDeclarationWithWrappedDescriptor() - val symbolRemapper = ScriptToClassSymbolRemapper(irScript.symbol, irScriptClass.symbol) - val typeRemapper = ScriptTypeRemapper(symbolRemapper) - val scriptTransformer = ScriptToClassTransformer(irScript, irScriptClass, symbolRemapper, typeRemapper) - irScriptClass.thisReceiver = irScript.thisReceiver.run { - transform(scriptTransformer, null) - } - irScriptClass.addConstructor { - isPrimary = true - }.also { irConstructor -> - irScript.explicitCallParameters.forEach { scriptCallParameter -> - val callParameter = irConstructor.addValueParameter { - updateFrom(scriptCallParameter) - name = scriptCallParameter.name - } + } + } + + private fun finalizeScriptClass(irScriptClass: IrClass, irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) { + val typeRemapper = ScriptTypeRemapper(symbolRemapper) + val scriptTransformer = ScriptToClassTransformer(irScript, irScriptClass, symbolRemapper, typeRemapper) + irScriptClass.thisReceiver = irScript.thisReceiver.run { + transform(scriptTransformer, null) + } + + irScriptClass.addConstructor { + isPrimary = true + }.also { irConstructor -> + + fun addConstructorParameter(valueParameter: IrValueParameter, createCorrespondingProperty: Boolean) { + valueParameter.type = typeRemapper.remapType(valueParameter.type) + if (valueParameter.varargElementType != null) { + valueParameter.varargElementType = typeRemapper.remapType(valueParameter.varargElementType!!) + } + irConstructor.valueParameters = irConstructor.valueParameters + valueParameter + if (createCorrespondingProperty) { irScriptClass.addSimplePropertyFrom( - callParameter, + valueParameter, IrExpressionBodyImpl( IrGetValueImpl( - callParameter.startOffset, callParameter.endOffset, - callParameter.type, - callParameter.symbol, + valueParameter.startOffset, valueParameter.endOffset, + valueParameter.type, + valueParameter.symbol, IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER ) ) ) } - - irConstructor.body = context.createIrBuilder(irConstructor.symbol).irBlockBody { - +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) - +IrInstanceInitializerCallImpl( - irScript.startOffset, irScript.endOffset, - irScriptClass.symbol, - context.irBuiltIns.unitType - ) - } } - var hasMain = false - irScript.statements.forEach { scriptStatement -> - when (scriptStatement) { - is IrVariable -> irScriptClass.addSimplePropertyFrom(scriptStatement) - is IrDeclaration -> { - val copy = scriptStatement.transform(scriptTransformer, null) as IrDeclaration - irScriptClass.declarations.add(copy) - // temporary way to avoid name clashes - // TODO: remove as soon as main generation become an explicit configuration option - if (copy is IrSimpleFunction && copy.name.asString() == "main") { - hasMain = true - } + + irScript.explicitCallParameters.forEach { addConstructorParameter(it, true) } + irScript.implicitReceiversParameters.forEach { addConstructorParameter(it, false) } + irScript.providedProperties.forEach { addConstructorParameter(it.first, false) } + + irConstructor.body = context.createIrBuilder(irConstructor.symbol).irBlockBody { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + +IrInstanceInitializerCallImpl( + irScript.startOffset, irScript.endOffset, + irScriptClass.symbol, + context.irBuiltIns.unitType + ) + } + } + var hasMain = false + irScript.statements.forEach { scriptStatement -> + when (scriptStatement) { + is IrVariable -> irScriptClass.addSimplePropertyFrom(scriptStatement) + is IrDeclaration -> { + val copy = scriptStatement.transform(scriptTransformer, null) as IrDeclaration + irScriptClass.declarations.add(copy) + // temporary way to avoid name clashes + // TODO: remove as soon as main generation become an explicit configuration option + if (copy is IrSimpleFunction && copy.name.asString() == "main") { + hasMain = true } - else -> { - val transformedStatement = scriptStatement.transformStatement(scriptTransformer) - irScriptClass.addAnonymousInitializer().also { irInitializer -> - irInitializer.body = - context.createIrBuilder(irInitializer.symbol).irBlockBody { - if (transformedStatement is IrComposite) { - for (statement in transformedStatement.statements) - +statement - } else { - +transformedStatement - } + } + else -> { + val transformedStatement = scriptStatement.transformStatement(scriptTransformer) + irScriptClass.addAnonymousInitializer().also { irInitializer -> + irInitializer.body = + context.createIrBuilder(irInitializer.symbol).irBlockBody { + if (transformedStatement is IrComposite) { + for (statement in transformedStatement.statements) + +statement + } else { + +transformedStatement } - } + } } } } - if (!hasMain) { - irScriptClass.addScriptMainFun() - } + } + if (!hasMain) { + irScriptClass.addScriptMainFun() + } - irScriptClass.annotations += irFile.annotations - irScriptClass.metadata = irFile.metadata + irScriptClass.annotations += (irScriptClass.parent as IrFile).annotations + irScriptClass.metadata = (irScriptClass.parent as IrFile).metadata - irScript.resultProperty?.owner?.let { irResultProperty -> - context.state.scriptSpecific.resultFieldName = irResultProperty.name.identifier - context.state.scriptSpecific.resultTypeString = irResultProperty.backingField?.type?.render() - } + irScript.resultProperty?.owner?.let { irResultProperty -> + context.state.scriptSpecific.resultFieldName = irResultProperty.name.identifier + context.state.scriptSpecific.resultTypeString = irResultProperty.backingField?.type?.render() } } @@ -261,8 +287,8 @@ private class ScriptToClassLowering(val context: JvmBackendContext) : FileLoweri private class ScriptToClassTransformer( val irScript: IrScript, val irScriptClass: IrClass, - val symbolRemapper: SymbolRemapper = ScriptToClassSymbolRemapper(irScript.symbol, irScriptClass.symbol), - val typeRemapper: TypeRemapper = ScriptTypeRemapper(symbolRemapper) + val symbolRemapper: SymbolRemapper, + val typeRemapper: TypeRemapper ) : IrElementTransformerVoid() { private fun IrType.remapType() = typeRemapper.remapType(this) @@ -389,16 +415,14 @@ private class ScriptToClassTransformer( } } -private class ScriptToClassSymbolRemapper( - val irScriptSymbol: IrScriptSymbol, - val irScriptClassSymbol: IrClassSymbol +private class ScriptsToClassesSymbolRemapper( + val scriptsToClasses: Map ) : SymbolRemapper.Empty() { override fun getReferencedClassifier(symbol: IrClassifierSymbol): IrClassifierSymbol = - if (symbol != irScriptSymbol) symbol - else irScriptClassSymbol + (symbol.owner as? IrScript)?.let { scriptsToClasses[it] }?.symbol ?: symbol } -class ScriptTypeRemapper( +private class ScriptTypeRemapper( private val symbolRemapper: SymbolRemapper ) : TypeRemapper { 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 2e5f995a1c0..25ff964b3f4 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,7 +5,9 @@ package org.jetbrains.kotlin.psi2ir.generators -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.ScriptDescriptor import org.jetbrains.kotlin.ir.assertCast import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin @@ -15,6 +17,9 @@ 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.util.indexOrMinusOne +import org.jetbrains.kotlin.ir.util.isCrossinline +import org.jetbrains.kotlin.ir.util.isNoinline import org.jetbrains.kotlin.ir.util.varargElementType import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtScript @@ -37,10 +42,17 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG return context.symbolTable.declareScript(descriptor).buildWithScope { irScript -> + // A workaround for the JS/REPL backend: + // JS backend doesn't save previously executed snippets anywhere, they should be taken for now from the context.symbolTable.listExistedScripts() + // on the other hand imported scripts are also stored there so to avoid clashes the imported scripts should be filtered out + // NOTE: that JVM IR is not properly tested with REPL, so it might have the same problem + // TODO: design and implement other schema for handling previous snippets + val importedScripts = descriptor.implicitReceivers.filterIsInstanceTo(HashSet()) + // TODO: since script could reference instances of previous one their receivers have to be enlisted in its scope // Remove this code once script is no longer represented by Class existedScripts.forEach { - if (it.owner != irScript) { + if (it.owner != irScript && it.descriptor !in importedScripts) { context.symbolTable.introduceValueParameter(it.owner.thisReceiver) } } @@ -48,21 +60,60 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG val startOffset = ktScript.pureStartOffset val endOffset = ktScript.pureEndOffset - fun makeReceiver(descriptor: ClassDescriptor): IrValueParameter { - val receiverParameterDescriptor = descriptor.thisAsReceiverParameter + fun makeParameter(descriptor: ParameterDescriptor, origin: IrDeclarationOrigin, index: Int = -1): IrValueParameter { + val type = descriptor.type.toIrType() + val varargElementType = descriptor.varargElementType?.toIrType() return context.symbolTable.declareValueParameter( startOffset, endOffset, - IrDeclarationOrigin.INSTANCE_RECEIVER, - receiverParameterDescriptor, - receiverParameterDescriptor.type.toIrType() - ).also { it.parent = irScript } + origin, + descriptor, + type + ) { symbol -> + context.irFactory.createValueParameter( + startOffset, endOffset, + origin, symbol, context.symbolTable.nameProvider.nameForDeclaration(descriptor), + if (index != -1) index else descriptor.indexOrMinusOne, + type, varargElementType, + descriptor.isCrossinline, descriptor.isNoinline, false + ) + } .also { it.parent = irScript } } - irScript.thisReceiver = makeReceiver(descriptor) + irScript.thisReceiver = makeParameter(descriptor.thisAsReceiverParameter, IrDeclarationOrigin.INSTANCE_RECEIVER) irScript.baseClass = descriptor.typeConstructor.supertypes.single().toIrType() - irScript.implicitReceivers = descriptor.implicitReceivers.map(::makeReceiver) + // This is part of a hack for implicit receivers that converted to value parameters below + // The proper schema would be to get properly indexed parameters from frontend (descriptor.implicitReceiversParameters), + // but it seems would require a proper remapping for the script body + // TODO: implement implicit receiver parameters handlin properly + var parametersIndex = 0 + + irScript.explicitCallParameters = descriptor.explicitConstructorParameters.map { valueParameterDescriptor -> + parametersIndex++ + valueParameterDescriptor.toIrValueParameter(startOffset, endOffset, IrDeclarationOrigin.SCRIPT_CALL_PARAMETER).also { it.parent = irScript } + } + + irScript.implicitReceiversParameters = descriptor.implicitReceivers.map { + makeParameter(it.thisAsReceiverParameter, IrDeclarationOrigin.SCRIPT_IMPLICIT_RECEIVER, parametersIndex++) + } + + irScript.providedProperties = descriptor.scriptProvidedProperties.zip(descriptor.scriptProvidedPropertiesParameters) + .map { (providedProperty, parameter) -> + // TODO: initializer + // TODO: do not keep direct links + val type = providedProperty.type.toIrType() + val valueParameter = context.symbolTable.declareValueParameter( + startOffset, endOffset, IrDeclarationOrigin.SCRIPT_PROVIDED_PROPERTY, parameter, type + ) + val irProperty = + PropertyGenerator(declarationGenerator).generateSyntheticProperty(ktScript, providedProperty, valueParameter) + irProperty.origin = IrDeclarationOrigin.SCRIPT_PROVIDED_PROPERTY + irScript.statements += irProperty + valueParameter to irProperty.symbol + } + + irScript.earlierScripts = existedScripts for (d in ktScript.declarations) { when (d) { @@ -146,19 +197,6 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG else -> irScript.statements += declarationGenerator.generateMemberDeclaration(d)!! } } - - irScript.explicitCallParameters = descriptor.unsubstitutedPrimaryConstructor.valueParameters.map { valueParameterDescriptor -> - valueParameterDescriptor.toIrValueParameter(startOffset, endOffset, IrDeclarationOrigin.SCRIPT_CALL_PARAMETER) - } - - irScript.providedProperties = descriptor.scriptProvidedProperties.map { providedProperty -> - // TODO: initializer - // TODO: do not keep direct links - val irProperty = PropertyGenerator(declarationGenerator).generateSyntheticProperty(ktScript, providedProperty, null) - irProperty.origin = IrDeclarationOrigin.SCRIPT_PROVIDED_PROPERTY - irScript.statements += irProperty - irProperty.symbol - } } } 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 57ad5aa23b7..8f242cc2d3b 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 @@ -24,9 +24,11 @@ abstract class IrScript : abstract var explicitCallParameters: List - abstract var implicitReceivers: List + abstract var implicitReceiversParameters: List - abstract var providedProperties: List + abstract var providedProperties: List> abstract var resultProperty: IrPropertySymbol? + + abstract var earlierScripts: List? } 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 ab589b026a3..f72b8e59e3b 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 @@ -46,9 +46,10 @@ class IrScriptImpl( override lateinit var baseClass: IrType override lateinit var explicitCallParameters: List - override lateinit var implicitReceivers: List - override lateinit var providedProperties: List + override lateinit var implicitReceiversParameters: List + override lateinit var providedProperties: List> override var resultProperty: IrPropertySymbol? = null + override var earlierScripts: List? = null @ObsoleteDescriptorBasedAPI override val descriptor: ScriptDescriptor @@ -68,10 +69,16 @@ class IrScriptImpl( override fun acceptChildren(visitor: IrElementVisitor, data: D) { statements.forEach { it.accept(visitor, data) } thisReceiver.accept(visitor, data) + explicitCallParameters.forEach { it.accept(visitor, data) } + implicitReceiversParameters.forEach { it.accept(visitor, data) } + providedProperties.forEach { it.first.accept(visitor, data) } } override fun transformChildren(transformer: IrElementTransformer, data: D) { statements.transformInPlace(transformer, data) thisReceiver = thisReceiver.transform(transformer, data) + explicitCallParameters = explicitCallParameters.map { it.transform(transformer, data) } + implicitReceiversParameters = implicitReceiversParameters.map { it.transform(transformer, data) } + providedProperties = providedProperties.map { it.first.transform(transformer, data) to it.second } } } 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 851d547fb0f..8b3c605c126 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 @@ -119,6 +119,9 @@ open class DeepCopyIrTreeWithSymbols( ).also { scriptCopy -> scriptCopy.thisReceiver = declaration.thisReceiver.transform() declaration.statements.mapTo(scriptCopy.statements) { it.transform() } + scriptCopy.explicitCallParameters = declaration.explicitCallParameters.map { it.transform() } + scriptCopy.implicitReceiversParameters = declaration.implicitReceiversParameters.map { it.transform() } + scriptCopy.providedProperties = declaration.providedProperties.map { it.first.transform() to it.second } } }