diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt index ce9d1292fff..4b7529e6efb 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ClosureAnnotator.kt @@ -177,7 +177,12 @@ class ClosureAnnotator(irElement: IrElement, declaration: IrDeclaration) { closureBuilder.declareVariable(this.thisReceiver) if (this.isInner) { - closureBuilder.declareVariable((this.parent as IrClass).thisReceiver) + val receiver = when (val parent = this.parent) { + is IrClass -> parent.thisReceiver + is IrScript -> parent.thisReceiver + else -> error("unexpected parent $parent") + } + closureBuilder.declareVariable(receiver) includeInParent(closureBuilder) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt index 6ef2fc04666..f612be61cb0 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/ScriptLowering.kt @@ -6,9 +6,11 @@ package org.jetbrains.kotlin.backend.jvm.lower import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor +import org.jetbrains.kotlin.backend.common.lower.ClosureAnnotator import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.phaser.makeCustomPhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.JvmInnerClassesSupport import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality @@ -19,18 +21,17 @@ import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.interpreter.toIrConst -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames @@ -42,12 +43,12 @@ internal val scriptsToClassesPhase = makeCustomPhase - ScriptsToClassesLowering(context).lower(input) + ScriptsToClassesLowering(context, context.innerClassesSupport).lower(input) } ) -private class ScriptsToClassesLowering(val context: JvmBackendContext) { +private class ScriptsToClassesLowering(val context: JvmBackendContext, val innerClassesSupport: JvmInnerClassesSupport) { fun lower(module: IrModuleFragment) { val scriptsToClasses = mutableMapOf() @@ -89,15 +90,35 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext) { } } + private fun collectCapturingClasses(irScript: IrScript): Set { + val annotator = ClosureAnnotator(irScript, irScript) + val capturingClasses = mutableSetOf() + for (clazz in irScript.statements) { + if (clazz is IrClassImpl && !clazz.isInner ) { + val closure = annotator.getClassClosure(clazz) + if (closure.capturedValues.singleOrNull()?.owner?.type == irScript.thisReceiver.type) { + if (clazz.isClass) { + capturingClasses.add(clazz) + } + } + } + } + return capturingClasses + } + private fun finalizeScriptClass(irScriptClass: IrClass, irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) { val typeRemapper = SimpleTypeRemapper(symbolRemapper) - val scriptTransformer = ScriptToClassTransformer(irScript, irScriptClass, symbolRemapper, typeRemapper, context) + val capturingClasses = collectCapturingClasses(irScript) + val scriptTransformer = ScriptToClassTransformer(irScript, irScriptClass, symbolRemapper, typeRemapper, context, capturingClasses, innerClassesSupport) val lambdaPatcher = ScriptFixLambdasTransformer(irScript, irScriptClass, context) - fun E.patchForClass(): IrElement = - transform(scriptTransformer, null).transform(lambdaPatcher, ScriptToClassTransformerContext()) + irScriptClass.thisReceiver = scriptTransformer.scriptClassReceiver - irScriptClass.thisReceiver = irScript.thisReceiver.transform(scriptTransformer, null) + val defaultContext = ScriptToClassTransformerContext(irScriptClass.thisReceiver?.symbol, null, null) + fun E.patchForClass(): IrElement { + return transform(scriptTransformer, defaultContext) + .transform(lambdaPatcher, ScriptFixLambdasTransformerContext()) + } irScript.constructor?.patchForClass()?.safeAs()!!.also { constructor -> val explicitParamsStartIndex = if (irScript.earlierScriptsParameter == null) 0 else 1 @@ -266,6 +287,12 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext) { } data class ScriptToClassTransformerContext( + val valueParameterForScriptThis: IrValueParameterSymbol?, + val fieldForScriptThis: IrFieldSymbol?, + val valueParameterForFieldReceiver: IrValueParameterSymbol? +) + +data class ScriptFixLambdasTransformerContext( val insideTopLevelDestructuringDeclaration: Boolean = false, val valueParameterToReplaceWithScript: IrValueParameter? = null ) @@ -275,36 +302,54 @@ private class ScriptToClassTransformer( val irScriptClass: IrClass, val symbolRemapper: SymbolRemapper, val typeRemapper: TypeRemapper, - val context: JvmBackendContext -) : IrElementTransformerVoid() { + val context: JvmBackendContext, + val capturingClasses: Set, + val innerClassesSupport: JvmInnerClassesSupport +) : IrElementTransformer { private fun IrType.remapType() = typeRemapper.remapType(this) + val capturingClassesConstructors = mutableMapOf().apply { + capturingClasses.forEach { c -> + c.declarations.forEach { d -> + if (d is IrConstructor) { + put(d, c) + } + } + } + } + + val scriptClassReceiver = + irScript.thisReceiver.transform(this, ScriptToClassTransformerContext(null, null, null)) + private fun IrDeclaration.transformParent() { if (parent == irScript) { parent = irScriptClass } } - private fun IrMutableAnnotationContainer.transformAnnotations() { - annotations = annotations.transform() + private fun IrMutableAnnotationContainer.transformAnnotations(data: ScriptToClassTransformerContext) { + annotations = annotations.transform(data) } - private inline fun T.transform() = - transform(this@ScriptToClassTransformer, null) as T + private inline fun T.transform(data: ScriptToClassTransformerContext) = + transform(this@ScriptToClassTransformer, data) as T - private inline fun List.transform() = - map { it.transform() } + private inline fun List.transform(data: ScriptToClassTransformerContext) = + map { it.transform(data) } - private fun T.transformFunctionChildren(): T = + private fun T.transformFunctionChildren(data: ScriptToClassTransformerContext): T = apply { - transformAnnotations() + transformAnnotations(data) typeRemapper.withinScope(this) { - dispatchReceiverParameter = dispatchReceiverParameter?.transform() - extensionReceiverParameter = extensionReceiverParameter?.transform() + dispatchReceiverParameter = dispatchReceiverParameter?.transform(data) + val dataForChildren = + if (dispatchReceiverParameter?.type != scriptClassReceiver.type) data + else ScriptToClassTransformerContext(dispatchReceiverParameter!!.symbol, null, null) + extensionReceiverParameter = extensionReceiverParameter?.transform(dataForChildren) returnType = returnType.remapType() - valueParameters = valueParameters.transform() - body = body?.transform() + valueParameters = valueParameters.transform(dataForChildren) + body = body?.transform(dataForChildren) } } @@ -315,89 +360,163 @@ private class ScriptToClassTransformer( private fun unexpectedElement(element: IrElement): Nothing = throw IllegalArgumentException("Unsupported element type: $element") - override fun visitElement(element: IrElement): IrElement = unexpectedElement(element) + override fun visitElement(element: IrElement, data: ScriptToClassTransformerContext): IrElement = unexpectedElement(element) - override fun visitModuleFragment(declaration: IrModuleFragment): IrModuleFragment = unexpectedElement(declaration) - override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment) = unexpectedElement(declaration) - override fun visitFile(declaration: IrFile): IrFile = unexpectedElement(declaration) - override fun visitScript(declaration: IrScript): IrStatement = unexpectedElement(declaration) + override fun visitModuleFragment(declaration: IrModuleFragment, data: ScriptToClassTransformerContext): IrModuleFragment = unexpectedElement(declaration) + override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment, data: ScriptToClassTransformerContext) = unexpectedElement(declaration) + override fun visitFile(declaration: IrFile, data: ScriptToClassTransformerContext): IrFile = unexpectedElement(declaration) + override fun visitScript(declaration: IrScript, data: ScriptToClassTransformerContext): IrStatement = unexpectedElement(declaration) - override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement = declaration.apply { + override fun visitDeclaration(declaration: IrDeclarationBase, data: ScriptToClassTransformerContext): IrStatement = declaration.apply { transformParent() - transformAnnotations() - transformChildren(this@ScriptToClassTransformer, null) + transformAnnotations(data) + transformChildren(this@ScriptToClassTransformer, data) } - override fun visitClass(declaration: IrClass): IrClass = declaration.apply { + override fun visitClass(declaration: IrClass, data: ScriptToClassTransformerContext): IrClass = declaration.apply { superTypes = superTypes.map { it.remapType() } - visitDeclaration(declaration) - } - - override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction = declaration.apply { transformParent() - transformFunctionChildren() + var dataForChildren = data + (declaration as? IrClassImpl)?.let { + if (it in capturingClasses) { + it.isInner = true + dataForChildren = + ScriptToClassTransformerContext( + null, innerClassesSupport.getOuterThisField(it).symbol, it.thisReceiver?.symbol + ) + } + } + transformAnnotations(dataForChildren) + transformChildren(this@ScriptToClassTransformer, dataForChildren) } - override fun visitConstructor(declaration: IrConstructor): IrConstructor = declaration.apply { + override fun visitSimpleFunction(declaration: IrSimpleFunction, data: ScriptToClassTransformerContext): IrSimpleFunction = + declaration.apply { + transformParent() + val dataForChildren = + if (data.valueParameterForFieldReceiver?.owner?.type.let { it != null && it == this.dispatchReceiverParameter?.type }) + ScriptToClassTransformerContext( + data.valueParameterForScriptThis, data.fieldForScriptThis, this.dispatchReceiverParameter?.symbol + ) + else data + transformFunctionChildren(dataForChildren) + } + + override fun visitConstructor(declaration: IrConstructor, data: ScriptToClassTransformerContext): IrConstructor = declaration.apply { + if (declaration in capturingClassesConstructors) { + declaration.dispatchReceiverParameter = + IrValueParameterBuilder().run { + name = SpecialNames.THIS + type = scriptClassReceiver.type + declaration.factory.createValueParameter( + startOffset, endOffset, IrDeclarationOrigin.INSTANCE_RECEIVER, + IrValueParameterSymbolImpl(), + name, index, type, varargElementType, isCrossInline, isNoinline, isHidden, isAssignable + ).also { + it.parent = declaration + } + } + } transformParent() - transformFunctionChildren() + transformFunctionChildren(data) } - override fun visitVariable(declaration: IrVariable): IrVariable = declaration.apply { + override fun visitVariable(declaration: IrVariable, data: ScriptToClassTransformerContext): IrVariable = declaration.apply { type = type.remapType() - visitDeclaration(declaration) + visitDeclaration(declaration, data) } - override fun visitTypeParameter(declaration: IrTypeParameter): IrTypeParameter = declaration.apply { - remapSuperTypes() - visitDeclaration(declaration) - } + override fun visitTypeParameter(declaration: IrTypeParameter, data: ScriptToClassTransformerContext): IrTypeParameter = + declaration.apply { + remapSuperTypes() + visitDeclaration(declaration, data) + } - override fun visitValueParameter(declaration: IrValueParameter): IrValueParameter = declaration.apply { - type = type.remapType() - varargElementType = varargElementType?.remapType() - visitDeclaration(declaration) - } + override fun visitValueParameter(declaration: IrValueParameter, data: ScriptToClassTransformerContext): IrValueParameter = + declaration.apply { + type = type.remapType() + varargElementType = varargElementType?.remapType() + visitDeclaration(declaration, data) + } - override fun visitTypeAlias(declaration: IrTypeAlias): IrTypeAlias = declaration.apply { + override fun visitTypeAlias(declaration: IrTypeAlias, data: ScriptToClassTransformerContext): IrTypeAlias = declaration.apply { expandedType = expandedType.remapType() - visitDeclaration(declaration) + visitDeclaration(declaration, data) } - override fun visitVararg(expression: IrVararg): IrVararg = expression.apply { + override fun visitVararg(expression: IrVararg, data: ScriptToClassTransformerContext): IrVararg = expression.apply { type = type.remapType() varargElementType = varargElementType.remapType() - transformChildren(this@ScriptToClassTransformer, null) + transformChildren(this@ScriptToClassTransformer, data) } - override fun visitSpreadElement(spread: IrSpreadElement): IrSpreadElement = spread.apply { - transformChildren(this@ScriptToClassTransformer, null) + override fun visitSpreadElement(spread: IrSpreadElement, data: ScriptToClassTransformerContext): IrSpreadElement = spread.apply { + transformChildren(this@ScriptToClassTransformer, data) } - override fun visitExpression(expression: IrExpression): IrExpression = expression.apply { + override fun visitExpression(expression: IrExpression, data: ScriptToClassTransformerContext): IrExpression = expression.apply { type = type.remapType() - transformChildren(this@ScriptToClassTransformer, null) + transformChildren(this@ScriptToClassTransformer, data) } - override fun visitClassReference(expression: IrClassReference): IrClassReference = expression.apply { - type = type.remapType() - classType = classType.remapType() - transformChildren(this@ScriptToClassTransformer, null) - } - - override fun visitTypeOperator(expression: IrTypeOperatorCall): IrTypeOperatorCall = expression.apply { - type = type.remapType() - typeOperand = typeOperand.remapType() - transformChildren(this@ScriptToClassTransformer, null) - } - - override fun visitMemberAccess(expression: IrMemberAccessExpression<*>): IrExpression = expression.apply { - for (i in 0 until typeArgumentsCount) { - putTypeArgument(i, getTypeArgument(i)?.remapType()) + override fun visitClassReference(expression: IrClassReference, data: ScriptToClassTransformerContext): IrClassReference = + expression.apply { + type = type.remapType() + classType = classType.remapType() + transformChildren(this@ScriptToClassTransformer, data) } - visitExpression(expression) + + override fun visitTypeOperator(expression: IrTypeOperatorCall, data: ScriptToClassTransformerContext): IrTypeOperatorCall = + expression.apply { + type = type.remapType() + typeOperand = typeOperand.remapType() + transformChildren(this@ScriptToClassTransformer, data) + } + + override fun visitMemberAccess(expression: IrMemberAccessExpression<*>, data: ScriptToClassTransformerContext): IrExpression = + expression.apply { + for (i in 0 until typeArgumentsCount) { + putTypeArgument(i, getTypeArgument(i)?.remapType()) + } + visitExpression(expression, data) + } + + override fun visitConstructorCall(expression: IrConstructorCall, data: ScriptToClassTransformerContext): IrExpression { + capturingClassesConstructors.keys.find { it.symbol == expression.symbol }?.let { + expression.dispatchReceiver = + getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, expression.origin) + } + return super.visitConstructorCall(expression, data) as IrExpression + } + + private fun getAccessCallForScriptInstance( + data: ScriptToClassTransformerContext, startOffset: Int, endOffset: Int, origin: IrStatementOrigin? + ) = when { + data.fieldForScriptThis != null -> + IrGetFieldImpl( + startOffset, endOffset, + data.fieldForScriptThis, + scriptClassReceiver.type, + origin + ).apply { + receiver = + IrGetValueImpl( + startOffset, endOffset, + data.valueParameterForFieldReceiver!!.owner.type, + data.valueParameterForFieldReceiver, + origin + ) + } + data.valueParameterForScriptThis != null -> + IrGetValueImpl( + startOffset, endOffset, + scriptClassReceiver.type, + data.valueParameterForScriptThis, + origin + ) + else -> error("Unexpected script transformation state: $data") } private fun getAccessCallForEarlierScript(expression: IrDeclarationReference, maybeScriptType: IrType): IrExpression? { @@ -432,7 +551,7 @@ private class ScriptToClassTransformer( return null } - override fun visitGetField(expression: IrGetField): IrExpression { + override fun visitGetField(expression: IrGetField, data: ScriptToClassTransformerContext): IrExpression { if (irScript.earlierScripts != null) { val receiver = expression.receiver if (receiver is IrGetValue && receiver.symbol.owner.name == SpecialNames.THIS) { @@ -440,14 +559,14 @@ private class ScriptToClassTransformer( if (newReceiver != null) { val newGetField = IrGetFieldImpl(expression.startOffset, expression.endOffset, expression.symbol, expression.type, newReceiver) - return super.visitGetField(newGetField) + return super.visitGetField(newGetField, data) } } } - return super.visitGetField(expression) + return super.visitGetField(expression, data) } - override fun visitCall(expression: IrCall): IrExpression { + override fun visitCall(expression: IrCall, data: ScriptToClassTransformerContext): IrExpression { if (irScript.earlierScripts != null) { val target = expression.symbol.owner val receiver: IrValueParameter? = target.dispatchReceiverParameter @@ -458,34 +577,36 @@ private class ScriptToClassTransformer( } } } - return super.visitCall(expression) + return super.visitCall(expression, data) as IrExpression } + + } private class ScriptFixLambdasTransformer( val irScript: IrScript, val irScriptClass: IrClass, val context: JvmBackendContext -) : IrElementTransformer { +) : IrElementTransformer { private fun unexpectedElement(element: IrElement): Nothing = throw IllegalArgumentException("Unsupported element type: $element") - override fun visitElement(element: IrElement, data: ScriptToClassTransformerContext): IrElement = unexpectedElement(element) + override fun visitElement(element: IrElement, data: ScriptFixLambdasTransformerContext): IrElement = unexpectedElement(element) - override fun visitModuleFragment(declaration: IrModuleFragment, data: ScriptToClassTransformerContext): IrModuleFragment = + override fun visitModuleFragment(declaration: IrModuleFragment, data: ScriptFixLambdasTransformerContext): IrModuleFragment = unexpectedElement(declaration) override fun visitExternalPackageFragment( declaration: IrExternalPackageFragment, - data: ScriptToClassTransformerContext + data: ScriptFixLambdasTransformerContext ): IrExternalPackageFragment = unexpectedElement(declaration) - override fun visitFile(declaration: IrFile, data: ScriptToClassTransformerContext): IrFile = unexpectedElement(declaration) - override fun visitScript(declaration: IrScript, data: ScriptToClassTransformerContext): IrScript = unexpectedElement(declaration) + override fun visitFile(declaration: IrFile, data: ScriptFixLambdasTransformerContext): IrFile = unexpectedElement(declaration) + override fun visitScript(declaration: IrScript, data: ScriptFixLambdasTransformerContext): IrScript = unexpectedElement(declaration) - override fun visitGetValue(expression: IrGetValue, data: ScriptToClassTransformerContext): IrExpression { + override fun visitGetValue(expression: IrGetValue, data: ScriptFixLambdasTransformerContext): IrExpression { if (data.valueParameterToReplaceWithScript == expression.symbol.owner) { val newGetValue = IrGetValueImpl( expression.startOffset, expression.endOffset, @@ -497,7 +618,7 @@ private class ScriptFixLambdasTransformer( } else return super.visitGetValue(expression, data) } - override fun visitSimpleFunction(declaration: IrSimpleFunction, data: ScriptToClassTransformerContext): IrSimpleFunction = + override fun visitSimpleFunction(declaration: IrSimpleFunction, data: ScriptFixLambdasTransformerContext): IrSimpleFunction = with(declaration) { if (data.insideTopLevelDestructuringDeclaration && origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA) { visibility = DescriptorVisibilities.LOCAL @@ -513,7 +634,7 @@ private class ScriptFixLambdasTransformer( } } as IrSimpleFunction - override fun visitComposite(expression: IrComposite, data: ScriptToClassTransformerContext): IrComposite { + override fun visitComposite(expression: IrComposite, data: ScriptFixLambdasTransformerContext): IrComposite { val dataForChildren = if (expression.origin == IrStatementOrigin.DESTRUCTURING_DECLARATION && expression.statements.firstIsInstanceOrNull()?.parent == irScriptClass diff --git a/compiler/ir/ir.tree.impl/src/org/jetbrains/kotlin/ir/declarations/impl/IrClassImpl.kt b/compiler/ir/ir.tree.impl/src/org/jetbrains/kotlin/ir/declarations/impl/IrClassImpl.kt index 39ccda2069c..04d2b435793 100644 --- a/compiler/ir/ir.tree.impl/src/org/jetbrains/kotlin/ir/declarations/impl/IrClassImpl.kt +++ b/compiler/ir/ir.tree.impl/src/org/jetbrains/kotlin/ir/declarations/impl/IrClassImpl.kt @@ -35,7 +35,7 @@ open class IrClassImpl( override var visibility: DescriptorVisibility, override var modality: Modality, override val isCompanion: Boolean = false, - override val isInner: Boolean = false, + override var isInner: Boolean = false, override val isData: Boolean = false, override val isExternal: Boolean = false, override val isInline: Boolean = false, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt index 106185663bb..7861d72e64a 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt @@ -147,6 +147,7 @@ val IrDeclaration.isLocal: Boolean } if (current.isAnonymousObject) return true + if (current is IrScript || (current is IrClass && current.origin == IrDeclarationOrigin.SCRIPT_CLASS)) return true current = current.parent } diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/anonymousObjectCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/anonymousObjectCapturesProperty.kts new file mode 100644 index 00000000000..b030bf9263c --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/anonymousObjectCapturesProperty.kts @@ -0,0 +1,17 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM + +// expected: rv: 42 + +fun foo() = B.bar() + +val life = 42 + +interface A { + fun bar(): Int +} + +val B = object : A { + override fun bar() = life +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect.kts new file mode 100644 index 00000000000..a06ed8bb425 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect.kts @@ -0,0 +1,20 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM + +// expected: rv: kotlin.Unit + +fun foo() { + B() +} +val b = B() + +class A +fun A.ext() = Unit + +class B { + fun bar() { + A().ext() + } +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect2x.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect2x.kts new file mode 100644 index 00000000000..e6daecd4dc6 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect2x.kts @@ -0,0 +1,21 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM + +// expected: rv: kotlin.Unit + +class C { + fun foo() { + B() + } +} + +class A +fun A.ext() = Unit + +class B { + fun bar() { + A().ext() + } +} + +val rv = C().foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesFunction.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesFunction.kts new file mode 100644 index 00000000000..c84d218bf1e --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesFunction.kts @@ -0,0 +1,14 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM + +// expected: rv: 42 + +val x = 6 +val y = 7 + +fun foo() = x + +class A { + fun bar() = foo() * y +} + +val rv = A().bar() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesProperty.kts new file mode 100644 index 00000000000..2cf88bf8124 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesProperty.kts @@ -0,0 +1,11 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM + +// expected: rv: abc + +// KT-19423 +val used = "abc" +class User { + val property = used // error: Expression is inaccessible from a nested class +} + +val rv = User().property diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyInStringTemplate.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyInStringTemplate.kts new file mode 100644 index 00000000000..83d782778ed --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyInStringTemplate.kts @@ -0,0 +1,18 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM + +// expected: rv: 42 + +fun foo() = B().bar() + +val life = 42 + +class A { + val x = "$life" +} + +class B { + fun bar() = A().x +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect.kts new file mode 100644 index 00000000000..96e9786a6cd --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect.kts @@ -0,0 +1,18 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM + +// expected: rv: 42 + +fun foo() = B().bar() + +val life = 42 + +class A { + val x = life +} + +class B { + fun bar() = A().x +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect2x.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect2x.kts new file mode 100644 index 00000000000..0d556b0115a --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect2x.kts @@ -0,0 +1,20 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM + +// expected: rv: 42 + +class C { + fun foo() = B().bar() +} + +val life = 42 + +class A { + val x = life +} + +class B { + fun bar() = A().x +} + +val rv = C().foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/companionCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/companionCapturesProperty.kts new file mode 100644 index 00000000000..0564b783dde --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/companionCapturesProperty.kts @@ -0,0 +1,15 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: + +// KT-30616 +val foo = "hello" + +class Bar(val s: String) { + companion object { + fun t() { + Bar(foo) + } + } +} diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/enumCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/enumCapturesProperty.kts new file mode 100644 index 00000000000..abc9a2e675b --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/enumCapturesProperty.kts @@ -0,0 +1,12 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: + +// KT-30616 +val foo = "hello" + +enum class Bar(val s: String = foo) { + + Eleven("0") +} diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/enumEntryCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/enumEntryCapturesProperty.kts new file mode 100644 index 00000000000..48858e7435f --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/enumEntryCapturesProperty.kts @@ -0,0 +1,11 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: + +// KT-30616 +val foo = "hello" + +enum class Bar(val s: String) { + Eleven(s = foo) +} diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/interfaceCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/interfaceCapturesProperty.kts new file mode 100644 index 00000000000..57021a5d3e2 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/interfaceCapturesProperty.kts @@ -0,0 +1,18 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: 42 + +fun foo() = B().bar() + +val life = 42 + +interface A { + val x get() = life +} + +class B : A { + fun bar() = x +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesProperty.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesProperty.kts new file mode 100644 index 00000000000..dd995cc7a82 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesProperty.kts @@ -0,0 +1,14 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: 42 + +fun foo() = B.bar() + +val life = 42 + +object B { + fun bar() = life +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyIndirect.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyIndirect.kts new file mode 100644 index 00000000000..4757e6f3600 --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyIndirect.kts @@ -0,0 +1,18 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM, JVM_IR + +// expected: rv: 42 + +fun foo() = B.bar() + +val life = 42 + +class A { + val x = life +} + +object B { + fun bar() = A().x +} + +val rv = foo() diff --git a/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyViaExtension.kts b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyViaExtension.kts new file mode 100644 index 00000000000..0f280dd4a5b --- /dev/null +++ b/compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyViaExtension.kts @@ -0,0 +1,34 @@ +// IGNORE_BACKEND: JS, JS_IR, JS_IR_ES6, NATIVE, WASM +// IGNORE_BACKEND: JVM_IR + +// expected: rv: + +// This example shows a bug in the old BE +// original issue - KT-49443, this is the reduced example of it. +// Here the old BE manages to compile the script, because it assumes incorrect constructor of the DefaultEachEntryConfiguration class +// on generating code for buildZip. +// The original example from the issue works supposedly because the constructor is not called on the runtime. +// In this example, uncommenting call to buildZip leads to the runtime exception, while uncommenting the call to copy or +// moving DefaultEachEntryConfiguration class definition before ZipHelper breaks codegeneration (somewhat expectedly). +// The JVM IR BE now generates correct error about invalid script instance capturing. + +interface I { + fun rename() +} + +object ZipHelper { + fun buildZip() { + DefaultEachEntryConfiguration(0).rename() + // 0.copy() + } +} + +class DefaultEachEntryConfiguration(val entry: Int) : I { + override fun rename() { + entry.copy() + } +} + +fun Int.copy() = Unit + +//ZipHelper.buildZip() diff --git a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt index 298f1bbcb27..44ef6addce8 100644 --- a/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt +++ b/compiler/tests-for-compiler-generator/tests/org/jetbrains/kotlin/test/generators/GenerateJUnit3CompilerTests.kt @@ -107,7 +107,7 @@ fun generateJUnit3CompilerTests(args: Array) { } testClass { - model("codegen/script", extension = "kts") + model("codegen/script", extension = "kts", targetBackend = TargetBackend.JVM) } testClass { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ScriptCodegenTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ScriptCodegenTestGenerated.java index d66f2965798..84ce38ff640 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ScriptCodegenTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ScriptCodegenTestGenerated.java @@ -9,6 +9,7 @@ import com.intellij.testFramework.TestDataPath; import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; import org.jetbrains.kotlin.test.KotlinTestUtils; import org.jetbrains.kotlin.test.util.KtTestUtil; +import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.test.TestMetadata; import org.junit.runner.RunWith; @@ -22,7 +23,7 @@ import java.util.regex.Pattern; @RunWith(JUnit3RunnerWithInners.class) public class ScriptCodegenTestGenerated extends AbstractScriptCodegenTest { private void runTest(String testDataFilePath) throws Exception { - KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } @TestMetadata("adder.kts") @@ -31,7 +32,7 @@ public class ScriptCodegenTestGenerated extends AbstractScriptCodegenTest { } public void testAllFilesPresentInScript() throws Exception { - KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/script"), Pattern.compile("^(.+)\\.kts$"), null, true); + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/script"), Pattern.compile("^(.+)\\.kts$"), null, TargetBackend.JVM, true); } @TestMetadata("classLiteralInsideFunction.kts") @@ -183,4 +184,92 @@ public class ScriptCodegenTestGenerated extends AbstractScriptCodegenTest { public void testTopLevelTypealias() throws Exception { runTest("compiler/testData/codegen/script/topLevelTypealias.kts"); } + + @TestMetadata("compiler/testData/codegen/script/scriptInstanceCapturing") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ScriptInstanceCapturing extends AbstractScriptCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInScriptInstanceCapturing() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/script/scriptInstanceCapturing"), Pattern.compile("^(.+)\\.kts$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("anonymousObjectCapturesProperty.kts") + public void testAnonymousObjectCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/anonymousObjectCapturesProperty.kts"); + } + + @TestMetadata("classCapturesExtensionIndirect.kts") + public void testClassCapturesExtensionIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect.kts"); + } + + @TestMetadata("classCapturesExtensionIndirect2x.kts") + public void testClassCapturesExtensionIndirect2x() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect2x.kts"); + } + + @TestMetadata("classCapturesFunction.kts") + public void testClassCapturesFunction() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesFunction.kts"); + } + + @TestMetadata("classCapturesProperty.kts") + public void testClassCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesProperty.kts"); + } + + @TestMetadata("classCapturesPropertyInStringTemplate.kts") + public void testClassCapturesPropertyInStringTemplate() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyInStringTemplate.kts"); + } + + @TestMetadata("classCapturesPropertyIndirect.kts") + public void testClassCapturesPropertyIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect.kts"); + } + + @TestMetadata("classCapturesPropertyIndirect2x.kts") + public void testClassCapturesPropertyIndirect2x() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect2x.kts"); + } + + @TestMetadata("companionCapturesProperty.kts") + public void testCompanionCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/companionCapturesProperty.kts"); + } + + @TestMetadata("enumCapturesProperty.kts") + public void testEnumCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/enumCapturesProperty.kts"); + } + + @TestMetadata("enumEntryCapturesProperty.kts") + public void testEnumEntryCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/enumEntryCapturesProperty.kts"); + } + + @TestMetadata("interfaceCapturesProperty.kts") + public void testInterfaceCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/interfaceCapturesProperty.kts"); + } + + @TestMetadata("objectCapturesProperty.kts") + public void testObjectCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesProperty.kts"); + } + + @TestMetadata("objectCapturesPropertyIndirect.kts") + public void testObjectCapturesPropertyIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyIndirect.kts"); + } + + @TestMetadata("objectCapturesPropertyViaExtension.kts") + public void testObjectCapturesPropertyViaExtension() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyViaExtension.kts"); + } + } } diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrScriptCodegenTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrScriptCodegenTestGenerated.java index f6c182292c8..5996605d15f 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrScriptCodegenTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/ir/IrScriptCodegenTestGenerated.java @@ -184,4 +184,92 @@ public class IrScriptCodegenTestGenerated extends AbstractIrScriptCodegenTest { public void testTopLevelTypealias() throws Exception { runTest("compiler/testData/codegen/script/topLevelTypealias.kts"); } + + @TestMetadata("compiler/testData/codegen/script/scriptInstanceCapturing") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ScriptInstanceCapturing extends AbstractIrScriptCodegenTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInScriptInstanceCapturing() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/script/scriptInstanceCapturing"), Pattern.compile("^(.+)\\.kts$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("anonymousObjectCapturesProperty.kts") + public void testAnonymousObjectCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/anonymousObjectCapturesProperty.kts"); + } + + @TestMetadata("classCapturesExtensionIndirect.kts") + public void testClassCapturesExtensionIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect.kts"); + } + + @TestMetadata("classCapturesExtensionIndirect2x.kts") + public void testClassCapturesExtensionIndirect2x() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesExtensionIndirect2x.kts"); + } + + @TestMetadata("classCapturesFunction.kts") + public void testClassCapturesFunction() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesFunction.kts"); + } + + @TestMetadata("classCapturesProperty.kts") + public void testClassCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesProperty.kts"); + } + + @TestMetadata("classCapturesPropertyInStringTemplate.kts") + public void testClassCapturesPropertyInStringTemplate() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyInStringTemplate.kts"); + } + + @TestMetadata("classCapturesPropertyIndirect.kts") + public void testClassCapturesPropertyIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect.kts"); + } + + @TestMetadata("classCapturesPropertyIndirect2x.kts") + public void testClassCapturesPropertyIndirect2x() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/classCapturesPropertyIndirect2x.kts"); + } + + @TestMetadata("companionCapturesProperty.kts") + public void testCompanionCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/companionCapturesProperty.kts"); + } + + @TestMetadata("enumCapturesProperty.kts") + public void testEnumCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/enumCapturesProperty.kts"); + } + + @TestMetadata("enumEntryCapturesProperty.kts") + public void testEnumEntryCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/enumEntryCapturesProperty.kts"); + } + + @TestMetadata("interfaceCapturesProperty.kts") + public void testInterfaceCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/interfaceCapturesProperty.kts"); + } + + @TestMetadata("objectCapturesProperty.kts") + public void testObjectCapturesProperty() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesProperty.kts"); + } + + @TestMetadata("objectCapturesPropertyIndirect.kts") + public void testObjectCapturesPropertyIndirect() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyIndirect.kts"); + } + + @TestMetadata("objectCapturesPropertyViaExtension.kts") + public void testObjectCapturesPropertyViaExtension() throws Exception { + runTest("compiler/testData/codegen/script/scriptInstanceCapturing/objectCapturesPropertyViaExtension.kts"); + } + } }