IR Scripting: update capturing logic for the REPL, imported scripts,
as well as all other implicit receivers.
This commit is contained in:
+166
-42
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
|
|||||||
import org.jetbrains.kotlin.ir.interpreter.toIrConst
|
import org.jetbrains.kotlin.ir.interpreter.toIrConst
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
import org.jetbrains.kotlin.ir.symbols.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
@@ -99,7 +100,7 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectCapturingClasses(irScript: IrScript): Set<IrClassImpl> {
|
private fun collectCapturingClasses(irScript: IrScript, typeRemapper: SimpleTypeRemapper): Set<IrClassImpl> {
|
||||||
val annotator = ClosureAnnotator(irScript, irScript)
|
val annotator = ClosureAnnotator(irScript, irScript)
|
||||||
val capturingClasses = mutableSetOf<IrClassImpl>()
|
val capturingClasses = mutableSetOf<IrClassImpl>()
|
||||||
val collector = object : IrElementVisitorVoid {
|
val collector = object : IrElementVisitorVoid {
|
||||||
@@ -110,7 +111,14 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
override fun visitClass(declaration: IrClass) {
|
override fun visitClass(declaration: IrClass) {
|
||||||
if (declaration is IrClassImpl && !declaration.isInner) {
|
if (declaration is IrClassImpl && !declaration.isInner) {
|
||||||
val closure = annotator.getClassClosure(declaration)
|
val closure = annotator.getClassClosure(declaration)
|
||||||
if (closure.capturedValues.singleOrNull()?.owner?.type == irScript.thisReceiver.type) {
|
val scriptsReceivers = mutableSetOf(irScript.thisReceiver.type)
|
||||||
|
irScript.earlierScripts?.forEach { scriptsReceivers.add(it.owner.thisReceiver.type) }
|
||||||
|
irScript.implicitReceiversParameters.forEach {
|
||||||
|
scriptsReceivers.add(it.type)
|
||||||
|
scriptsReceivers.add(typeRemapper.remapType(it.type))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closure.capturedValues.any { it.owner.type in scriptsReceivers }) {
|
||||||
fun reportError(factory: KtDiagnosticFactory1<String>, name: Name? = null) {
|
fun reportError(factory: KtDiagnosticFactory1<String>, name: Name? = null) {
|
||||||
context.ktDiagnosticReporter.at(declaration).report(factory, (name ?: declaration.name).asString())
|
context.ktDiagnosticReporter.at(declaration).report(factory, (name ?: declaration.name).asString())
|
||||||
}
|
}
|
||||||
@@ -124,11 +132,14 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
|
|
||||||
declaration.isClass ->
|
declaration.isClass ->
|
||||||
if (declaration.parent != irScript) {
|
if (declaration.parent != irScript) {
|
||||||
context.ktDiagnosticReporter.at(declaration).report(
|
if ((declaration.parent as? IrClass)?.isInner == false) {
|
||||||
JvmBackendErrors.SCRIPT_CAPTURING_NESTED_CLASS,
|
context.ktDiagnosticReporter.at(declaration).report(
|
||||||
declaration.name.asString(),
|
JvmBackendErrors.SCRIPT_CAPTURING_NESTED_CLASS,
|
||||||
((declaration.parent as? IrDeclarationWithName)?.name ?: SpecialNames.NO_NAME_PROVIDED).asString()
|
declaration.name.asString(),
|
||||||
)
|
((declaration.parent as? IrDeclarationWithName)?.name
|
||||||
|
?: SpecialNames.NO_NAME_PROVIDED).asString()
|
||||||
|
)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
capturingClasses.add(declaration)
|
capturingClasses.add(declaration)
|
||||||
}
|
}
|
||||||
@@ -148,20 +159,48 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
|
|
||||||
private fun finalizeScriptClass(irScriptClass: IrClass, irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) {
|
private fun finalizeScriptClass(irScriptClass: IrClass, irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) {
|
||||||
val typeRemapper = SimpleTypeRemapper(symbolRemapper)
|
val typeRemapper = SimpleTypeRemapper(symbolRemapper)
|
||||||
val capturingClasses = collectCapturingClasses(irScript)
|
val capturingClasses = collectCapturingClasses(irScript, typeRemapper)
|
||||||
|
|
||||||
|
val earlierScriptField = irScript.earlierScriptsParameter?.let { earlierScriptsParameter ->
|
||||||
|
irScriptClass.factory.createField(
|
||||||
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.SCRIPT_EARLIER_SCRIPTS,
|
||||||
|
IrFieldSymbolImpl(), Name.identifier("\$\$earlierScripts"), earlierScriptsParameter.type,
|
||||||
|
DescriptorVisibilities.PRIVATE, isFinal = true, isExternal = false, isStatic = false
|
||||||
|
)
|
||||||
|
}?.also {
|
||||||
|
it.parent = irScriptClass
|
||||||
|
irScriptClass.declarations.add(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val implicitReceiversFieldsWithParameters = arrayListOf<Pair<IrField, IrValueParameter>>().apply {
|
||||||
|
irScript.implicitReceiversParameters.forEach { param ->
|
||||||
|
val field = irScriptClass.factory.createField(
|
||||||
|
UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.SCRIPT_IMPLICIT_RECEIVER,
|
||||||
|
IrFieldSymbolImpl(), Name.identifier("\$\$implicitReceiver_${param.type.classFqName?.shortName()?.asString()!!}"),
|
||||||
|
typeRemapper.remapType(param.type),
|
||||||
|
DescriptorVisibilities.PRIVATE, isFinal = true, isExternal = false, isStatic = false
|
||||||
|
)
|
||||||
|
field.parent = irScriptClass
|
||||||
|
irScriptClass.declarations.add(field)
|
||||||
|
add(field to param)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val scriptTransformer = ScriptToClassTransformer(
|
val scriptTransformer = ScriptToClassTransformer(
|
||||||
irScript,
|
irScript,
|
||||||
irScriptClass,
|
irScriptClass,
|
||||||
typeRemapper,
|
typeRemapper,
|
||||||
context,
|
context,
|
||||||
capturingClasses,
|
capturingClasses,
|
||||||
innerClassesSupport
|
innerClassesSupport,
|
||||||
|
earlierScriptField,
|
||||||
|
implicitReceiversFieldsWithParameters
|
||||||
)
|
)
|
||||||
val lambdaPatcher = ScriptFixLambdasTransformer(irScript, irScriptClass, context)
|
val lambdaPatcher = ScriptFixLambdasTransformer(irScriptClass)
|
||||||
|
|
||||||
irScriptClass.thisReceiver = scriptTransformer.scriptClassReceiver
|
irScriptClass.thisReceiver = scriptTransformer.scriptClassReceiver
|
||||||
|
|
||||||
val defaultContext = ScriptToClassTransformerContext(irScriptClass.thisReceiver?.symbol, null, null)
|
val defaultContext = ScriptToClassTransformerContext(irScriptClass.thisReceiver?.symbol, null, null, false)
|
||||||
fun <E: IrElement> E.patchForClass(): IrElement {
|
fun <E: IrElement> E.patchForClass(): IrElement {
|
||||||
return transform(scriptTransformer, defaultContext)
|
return transform(scriptTransformer, defaultContext)
|
||||||
.transform(lambdaPatcher, ScriptFixLambdasTransformerContext())
|
.transform(lambdaPatcher, ScriptFixLambdasTransformerContext())
|
||||||
@@ -192,6 +231,16 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (earlierScriptField != null) {
|
||||||
|
+irSetField(irGet(irScriptClass.thisReceiver!!), earlierScriptField, irGet(irScript.earlierScriptsParameter!!))
|
||||||
|
}
|
||||||
|
implicitReceiversFieldsWithParameters.forEach { (field, correspondingParameter) ->
|
||||||
|
+irSetField(
|
||||||
|
irGet(irScriptClass.thisReceiver!!),
|
||||||
|
field,
|
||||||
|
irGet(correspondingParameter.patchForClass().safeAs<IrValueParameter>()!!)
|
||||||
|
)
|
||||||
|
}
|
||||||
+IrInstanceInitializerCallImpl(
|
+IrInstanceInitializerCallImpl(
|
||||||
irScript.startOffset, irScript.endOffset,
|
irScript.startOffset, irScript.endOffset,
|
||||||
irScriptClass.symbol,
|
irScriptClass.symbol,
|
||||||
@@ -335,7 +384,8 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
|||||||
data class ScriptToClassTransformerContext(
|
data class ScriptToClassTransformerContext(
|
||||||
val valueParameterForScriptThis: IrValueParameterSymbol?,
|
val valueParameterForScriptThis: IrValueParameterSymbol?,
|
||||||
val fieldForScriptThis: IrFieldSymbol?,
|
val fieldForScriptThis: IrFieldSymbol?,
|
||||||
val valueParameterForFieldReceiver: IrValueParameterSymbol?
|
val valueParameterForFieldReceiver: IrValueParameterSymbol?,
|
||||||
|
val isInScriptConstructor: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ScriptFixLambdasTransformerContext(
|
data class ScriptFixLambdasTransformerContext(
|
||||||
@@ -349,7 +399,9 @@ private class ScriptToClassTransformer(
|
|||||||
val typeRemapper: TypeRemapper,
|
val typeRemapper: TypeRemapper,
|
||||||
val context: JvmBackendContext,
|
val context: JvmBackendContext,
|
||||||
val capturingClasses: Set<IrClassImpl>,
|
val capturingClasses: Set<IrClassImpl>,
|
||||||
val innerClassesSupport: JvmInnerClassesSupport
|
val innerClassesSupport: JvmInnerClassesSupport,
|
||||||
|
val earlierScriptsField: IrField?,
|
||||||
|
val implicitReceiversFieldsWithParameters: Collection<Pair<IrField, IrValueParameter>>
|
||||||
) : IrElementTransformer<ScriptToClassTransformerContext> {
|
) : IrElementTransformer<ScriptToClassTransformerContext> {
|
||||||
|
|
||||||
private fun IrType.remapType() = typeRemapper.remapType(this)
|
private fun IrType.remapType() = typeRemapper.remapType(this)
|
||||||
@@ -365,7 +417,7 @@ private class ScriptToClassTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val scriptClassReceiver =
|
val scriptClassReceiver =
|
||||||
irScript.thisReceiver.transform(this, ScriptToClassTransformerContext(null, null, null))
|
irScript.thisReceiver.transform(this, ScriptToClassTransformerContext(null, null, null, false))
|
||||||
|
|
||||||
private fun IrDeclaration.transformParent() {
|
private fun IrDeclaration.transformParent() {
|
||||||
if (parent == irScript) {
|
if (parent == irScript) {
|
||||||
@@ -387,10 +439,22 @@ private class ScriptToClassTransformer(
|
|||||||
apply {
|
apply {
|
||||||
transformAnnotations(data)
|
transformAnnotations(data)
|
||||||
typeRemapper.withinScope(this) {
|
typeRemapper.withinScope(this) {
|
||||||
dispatchReceiverParameter = dispatchReceiverParameter?.transform(data)
|
val newDispatchReceiverParameter = dispatchReceiverParameter?.transform(data)
|
||||||
val dataForChildren =
|
val dataForChildren =
|
||||||
if (dispatchReceiverParameter?.type != scriptClassReceiver.type) data
|
when {
|
||||||
else ScriptToClassTransformerContext(dispatchReceiverParameter!!.symbol, null, null)
|
newDispatchReceiverParameter == null -> data
|
||||||
|
newDispatchReceiverParameter.type == scriptClassReceiver.type ->
|
||||||
|
ScriptToClassTransformerContext(newDispatchReceiverParameter.symbol, null, null, this is IrConstructor)
|
||||||
|
newDispatchReceiverParameter.type == data.valueParameterForFieldReceiver?.owner?.type ->
|
||||||
|
ScriptToClassTransformerContext(
|
||||||
|
null,
|
||||||
|
data.fieldForScriptThis,
|
||||||
|
newDispatchReceiverParameter.symbol,
|
||||||
|
this is IrConstructor
|
||||||
|
)
|
||||||
|
else -> data
|
||||||
|
}
|
||||||
|
dispatchReceiverParameter = newDispatchReceiverParameter
|
||||||
extensionReceiverParameter = extensionReceiverParameter?.transform(dataForChildren)
|
extensionReceiverParameter = extensionReceiverParameter?.transform(dataForChildren)
|
||||||
returnType = returnType.remapType()
|
returnType = returnType.remapType()
|
||||||
valueParameters = valueParameters.transform(dataForChildren)
|
valueParameters = valueParameters.transform(dataForChildren)
|
||||||
@@ -407,8 +471,12 @@ private class ScriptToClassTransformer(
|
|||||||
|
|
||||||
override fun visitElement(element: IrElement, data: ScriptToClassTransformerContext): IrElement = unexpectedElement(element)
|
override fun visitElement(element: IrElement, data: ScriptToClassTransformerContext): IrElement = unexpectedElement(element)
|
||||||
|
|
||||||
override fun visitModuleFragment(declaration: IrModuleFragment, data: ScriptToClassTransformerContext): IrModuleFragment = unexpectedElement(declaration)
|
override fun visitModuleFragment(declaration: IrModuleFragment, data: ScriptToClassTransformerContext): IrModuleFragment =
|
||||||
override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment, data: ScriptToClassTransformerContext) = unexpectedElement(declaration)
|
unexpectedElement(declaration)
|
||||||
|
|
||||||
|
override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment, data: ScriptToClassTransformerContext) =
|
||||||
|
unexpectedElement(declaration)
|
||||||
|
|
||||||
override fun visitFile(declaration: IrFile, data: ScriptToClassTransformerContext): IrFile = unexpectedElement(declaration)
|
override fun visitFile(declaration: IrFile, data: ScriptToClassTransformerContext): IrFile = unexpectedElement(declaration)
|
||||||
override fun visitScript(declaration: IrScript, data: ScriptToClassTransformerContext): IrStatement = unexpectedElement(declaration)
|
override fun visitScript(declaration: IrScript, data: ScriptToClassTransformerContext): IrStatement = unexpectedElement(declaration)
|
||||||
|
|
||||||
@@ -429,7 +497,7 @@ private class ScriptToClassTransformer(
|
|||||||
it.isInner = true
|
it.isInner = true
|
||||||
dataForChildren =
|
dataForChildren =
|
||||||
ScriptToClassTransformerContext(
|
ScriptToClassTransformerContext(
|
||||||
null, innerClassesSupport.getOuterThisField(it).symbol, it.thisReceiver?.symbol
|
null, innerClassesSupport.getOuterThisField(it).symbol, it.thisReceiver?.symbol, false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -440,13 +508,7 @@ private class ScriptToClassTransformer(
|
|||||||
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: ScriptToClassTransformerContext): IrSimpleFunction =
|
override fun visitSimpleFunction(declaration: IrSimpleFunction, data: ScriptToClassTransformerContext): IrSimpleFunction =
|
||||||
declaration.apply {
|
declaration.apply {
|
||||||
transformParent()
|
transformParent()
|
||||||
val dataForChildren =
|
transformFunctionChildren(data)
|
||||||
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 {
|
override fun visitConstructor(declaration: IrConstructor, data: ScriptToClassTransformerContext): IrConstructor = declaration.apply {
|
||||||
@@ -529,13 +591,31 @@ private class ScriptToClassTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitConstructorCall(expression: IrConstructorCall, data: ScriptToClassTransformerContext): IrExpression {
|
override fun visitConstructorCall(expression: IrConstructorCall, data: ScriptToClassTransformerContext): IrExpression {
|
||||||
capturingClassesConstructors.keys.find { it.symbol == expression.symbol }?.let {
|
if (expression.dispatchReceiver == null) {
|
||||||
expression.dispatchReceiver =
|
// first part of the expression triggers if the ctor itself is transformed before call
|
||||||
getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, expression.origin)
|
// but the second part is not enough by itself if capturing class is defined in an earlier snippet (we are not keeping
|
||||||
|
// capturing classes from earlier snippets)
|
||||||
|
val ctorDispatchReceiverType = expression.symbol.owner.dispatchReceiverParameter?.type
|
||||||
|
?: if (capturingClassesConstructors.keys.any { it.symbol == expression.symbol }) scriptClassReceiver.type else null
|
||||||
|
if (ctorDispatchReceiverType != null) {
|
||||||
|
getDispatchReceiverExpression(data, expression, ctorDispatchReceiverType, expression.origin)?.let {
|
||||||
|
expression.dispatchReceiver = it
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.visitConstructorCall(expression, data) as IrExpression
|
return super.visitConstructorCall(expression, data) as IrExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getDispatchReceiverExpression(
|
||||||
|
data: ScriptToClassTransformerContext, expression: IrDeclarationReference, receiverType: IrType, origin: IrStatementOrigin?,
|
||||||
|
): IrExpression? {
|
||||||
|
return if (receiverType == scriptClassReceiver.type) {
|
||||||
|
getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, origin)
|
||||||
|
} else {
|
||||||
|
getAccessCallForImplicitReceiver(data, expression, receiverType, origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getAccessCallForScriptInstance(
|
private fun getAccessCallForScriptInstance(
|
||||||
data: ScriptToClassTransformerContext, startOffset: Int, endOffset: Int, origin: IrStatementOrigin?
|
data: ScriptToClassTransformerContext, startOffset: Int, endOffset: Int, origin: IrStatementOrigin?
|
||||||
) = when {
|
) = when {
|
||||||
@@ -564,7 +644,33 @@ private class ScriptToClassTransformer(
|
|||||||
else -> error("Unexpected script transformation state: $data")
|
else -> error("Unexpected script transformation state: $data")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getAccessCallForEarlierScript(expression: IrDeclarationReference, maybeScriptType: IrType): IrExpression? {
|
private fun getAccessCallForImplicitReceiver(
|
||||||
|
data: ScriptToClassTransformerContext,
|
||||||
|
expression: IrDeclarationReference,
|
||||||
|
receiverType: IrType,
|
||||||
|
expressionOrigin: IrStatementOrigin?
|
||||||
|
): IrExpression? {
|
||||||
|
// implicit receivers has priority (as per descriptor outer scopes)
|
||||||
|
implicitReceiversFieldsWithParameters.firstOrNull { it.second.type == receiverType }?.let { (field, param) ->
|
||||||
|
val builder = context.createIrBuilder(expression.symbol)
|
||||||
|
return if (data.isInScriptConstructor) {
|
||||||
|
builder.irGet(param.type, param.symbol)
|
||||||
|
} else {
|
||||||
|
val scriptReceiver =
|
||||||
|
getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, expressionOrigin)
|
||||||
|
builder.irGetField(scriptReceiver, field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// earlier scripts are processed next
|
||||||
|
return getAccessCallForEarlierScript(data, expression, receiverType, expressionOrigin)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAccessCallForEarlierScript(
|
||||||
|
data: ScriptToClassTransformerContext,
|
||||||
|
expression: IrDeclarationReference,
|
||||||
|
maybeScriptType: IrType,
|
||||||
|
expressionOrigin: IrStatementOrigin?
|
||||||
|
): IrExpression? {
|
||||||
if (irScript.earlierScripts?.isEmpty() != false) return null
|
if (irScript.earlierScripts?.isEmpty() != false) return null
|
||||||
val scriptSymbol = maybeScriptType.classifierOrNull ?: return null
|
val scriptSymbol = maybeScriptType.classifierOrNull ?: return null
|
||||||
val scriptSymbolOwner = scriptSymbol.owner
|
val scriptSymbolOwner = scriptSymbol.owner
|
||||||
@@ -580,8 +686,17 @@ private class ScriptToClassTransformer(
|
|||||||
val objArray = context.irBuiltIns.arrayClass
|
val objArray = context.irBuiltIns.arrayClass
|
||||||
val objArrayGet = objArray.functions.single { it.owner.name == OperatorNameConventions.GET }
|
val objArrayGet = objArray.functions.single { it.owner.name == OperatorNameConventions.GET }
|
||||||
val builder = context.createIrBuilder(expression.symbol)
|
val builder = context.createIrBuilder(expression.symbol)
|
||||||
|
|
||||||
|
val irGetEarlierScripts =
|
||||||
|
if (data.isInScriptConstructor) {
|
||||||
|
builder.irGet(objArray.defaultType, irScript.earlierScriptsParameter!!.symbol)
|
||||||
|
} else {
|
||||||
|
val scriptReceiver =
|
||||||
|
getAccessCallForScriptInstance(data, expression.startOffset, expression.endOffset, expressionOrigin)
|
||||||
|
builder.irGetField(scriptReceiver, earlierScriptsField!!)
|
||||||
|
}
|
||||||
val getPrevScriptObjectExpression = builder.irCall(objArrayGet).apply {
|
val getPrevScriptObjectExpression = builder.irCall(objArrayGet).apply {
|
||||||
dispatchReceiver = builder.irGet(objArray.defaultType, irScript.earlierScriptsParameter!!.symbol)
|
dispatchReceiver = irGetEarlierScripts
|
||||||
putValueArgument(0, earlierScriptIndex.toIrConst(objArrayGet.owner.valueParameters.first().type))
|
putValueArgument(0, earlierScriptIndex.toIrConst(objArrayGet.owner.valueParameters.first().type))
|
||||||
}
|
}
|
||||||
val prevScriptClassType =
|
val prevScriptClassType =
|
||||||
@@ -597,10 +712,10 @@ private class ScriptToClassTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitGetField(expression: IrGetField, data: ScriptToClassTransformerContext): IrExpression {
|
override fun visitGetField(expression: IrGetField, data: ScriptToClassTransformerContext): IrExpression {
|
||||||
if (irScript.earlierScripts != null) {
|
if (irScript.needsReceiverProcessing) {
|
||||||
val receiver = expression.receiver
|
val receiver = expression.receiver
|
||||||
if (receiver is IrGetValue && receiver.symbol.owner.name == SpecialNames.THIS) {
|
if (receiver is IrGetValue && receiver.symbol.owner.name == SpecialNames.THIS) {
|
||||||
val newReceiver = getAccessCallForEarlierScript(expression, receiver.type)
|
val newReceiver = getDispatchReceiverExpression(data, expression, receiver.type, expression.origin)
|
||||||
if (newReceiver != null) {
|
if (newReceiver != null) {
|
||||||
val newGetField =
|
val newGetField =
|
||||||
IrGetFieldImpl(expression.startOffset, expression.endOffset, expression.symbol, expression.type, newReceiver)
|
IrGetFieldImpl(expression.startOffset, expression.endOffset, expression.symbol, expression.type, newReceiver)
|
||||||
@@ -612,11 +727,11 @@ private class ScriptToClassTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall, data: ScriptToClassTransformerContext): IrExpression {
|
override fun visitCall(expression: IrCall, data: ScriptToClassTransformerContext): IrExpression {
|
||||||
if (irScript.earlierScripts != null) {
|
if (irScript.needsReceiverProcessing) {
|
||||||
val target = expression.symbol.owner
|
val target = expression.symbol.owner
|
||||||
val receiver: IrValueParameter? = target.dispatchReceiverParameter
|
val receiver: IrValueParameter? = target.dispatchReceiverParameter
|
||||||
if (receiver?.name == SpecialNames.THIS) {
|
if (receiver?.name == SpecialNames.THIS) {
|
||||||
val newReceiver = getAccessCallForEarlierScript(expression, receiver.type)
|
val newReceiver = getDispatchReceiverExpression(data, expression, receiver.type, expression.origin)
|
||||||
if (newReceiver != null) {
|
if (newReceiver != null) {
|
||||||
expression.dispatchReceiver = newReceiver
|
expression.dispatchReceiver = newReceiver
|
||||||
}
|
}
|
||||||
@@ -625,14 +740,21 @@ private class ScriptToClassTransformer(
|
|||||||
return super.visitCall(expression, data) as IrExpression
|
return super.visitCall(expression, data) as IrExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitGetValue(expression: IrGetValue, data: ScriptToClassTransformerContext): IrExpression {
|
||||||
|
if (irScript.needsReceiverProcessing) {
|
||||||
|
val getValueParameter = expression.symbol.owner as? IrValueParameter
|
||||||
|
if (getValueParameter != null && getValueParameter.name == SpecialNames.THIS) {
|
||||||
|
val newExpression = getDispatchReceiverExpression(data, expression, getValueParameter.type, expression.origin)
|
||||||
|
if (newExpression != null) {
|
||||||
|
return super.visitExpression(newExpression, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visitGetValue(expression, data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ScriptFixLambdasTransformer(
|
private class ScriptFixLambdasTransformer(val irScriptClass: IrClass) : IrElementTransformer<ScriptFixLambdasTransformerContext> {
|
||||||
val irScript: IrScript,
|
|
||||||
val irScriptClass: IrClass,
|
|
||||||
val context: JvmBackendContext
|
|
||||||
) : IrElementTransformer<ScriptFixLambdasTransformerContext> {
|
|
||||||
|
|
||||||
private fun unexpectedElement(element: IrElement): Nothing =
|
private fun unexpectedElement(element: IrElement): Nothing =
|
||||||
throw IllegalArgumentException("Unsupported element type: $element")
|
throw IllegalArgumentException("Unsupported element type: $element")
|
||||||
@@ -713,3 +835,5 @@ private inline fun IrClass.addAnonymousInitializer(builder: IrFunctionBuilder.()
|
|||||||
anonymousInitializer.parent = this@addAnonymousInitializer
|
anonymousInitializer.parent = this@addAnonymousInitializer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val IrScript.needsReceiverProcessing: Boolean
|
||||||
|
get() = earlierScripts?.isNotEmpty() == true || implicitReceiversParameters.isNotEmpty()
|
||||||
+1
@@ -40,6 +40,7 @@ open class GeneratorExtensions : StubGeneratorExtensions() {
|
|||||||
get() = false
|
get() = false
|
||||||
|
|
||||||
open fun getPreviousScripts(): List<IrScriptSymbol>? = null
|
open fun getPreviousScripts(): List<IrScriptSymbol>? = null
|
||||||
|
open val lowerScriptToClass: Boolean get() = true
|
||||||
|
|
||||||
open fun unwrapSyntheticJavaProperty(descriptor: PropertyDescriptor): Pair<FunctionDescriptor, FunctionDescriptor?>? = null
|
open fun unwrapSyntheticJavaProperty(descriptor: PropertyDescriptor): Pair<FunctionDescriptor, FunctionDescriptor?>? = null
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ class ScriptGenerator(declarationGenerator: DeclarationGenerator) : DeclarationG
|
|||||||
).also { it.parent = irScript }
|
).also { it.parent = irScript }
|
||||||
}
|
}
|
||||||
|
|
||||||
irScript.earlierScriptsParameter = descriptor.earlierScriptsConstructorParameter?.let(::createValueParameter)
|
if (context.extensions.lowerScriptToClass) {
|
||||||
|
irScript.earlierScriptsParameter = descriptor.earlierScriptsConstructorParameter?.let(::createValueParameter)
|
||||||
|
}
|
||||||
|
|
||||||
irScript.explicitCallParameters = descriptor.explicitConstructorParameters.map(::createValueParameter)
|
irScript.explicitCallParameters = descriptor.explicitConstructorParameters.map(::createValueParameter)
|
||||||
|
|
||||||
|
|||||||
+24
@@ -67,6 +67,30 @@ class ReplTest : TestCase() {
|
|||||||
Assert.assertEquals("x = 3", out)
|
Assert.assertEquals("x = 3", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCaptureFromPreviousSnippets() {
|
||||||
|
val out = captureOut {
|
||||||
|
checkEvaluateInRepl(
|
||||||
|
sequenceOf(
|
||||||
|
"val x = 3",
|
||||||
|
"fun b() = x",
|
||||||
|
"b()",
|
||||||
|
"println(\"b() = \${b()}\")",
|
||||||
|
"val y = x + 2",
|
||||||
|
"y",
|
||||||
|
"class Nested { fun c() = x + 4 }",
|
||||||
|
"Nested().c()",
|
||||||
|
"inner class Inner { fun d() = x + 6 }",
|
||||||
|
"Inner().d()",
|
||||||
|
"class TwoLevel { inner class Inner { fun e() = x + 8 } }",
|
||||||
|
"TwoLevel().Inner().e()",
|
||||||
|
),
|
||||||
|
sequenceOf(null, null, 3, null, null, 5, null, 7, null, 9, null, 11)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Assert.assertEquals("b() = 3", out)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testEvalWithResult() {
|
fun testEvalWithResult() {
|
||||||
checkEvaluateInRepl(
|
checkEvaluateInRepl(
|
||||||
|
|||||||
+11
@@ -122,6 +122,17 @@ class MainKtsTest {
|
|||||||
Assert.assertEquals(OUT_FROM_IMPORT_TEST, out)
|
Assert.assertEquals(OUT_FROM_IMPORT_TEST, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testImportWithCapture() {
|
||||||
|
|
||||||
|
val out = captureOut {
|
||||||
|
val res = evalFile(File("$TEST_DATA_ROOT/import-with-capture-test.main.kts"))
|
||||||
|
assertSucceeded(res)
|
||||||
|
}.lines()
|
||||||
|
|
||||||
|
Assert.assertEquals(OUT_FROM_IMPORT_TEST, out)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDuplicateImportError() {
|
fun testDuplicateImportError() {
|
||||||
val res = evalFile(File("$TEST_DATA_ROOT/import-duplicate-test.main.kts"))
|
val res = evalFile(File("$TEST_DATA_ROOT/import-duplicate-test.main.kts"))
|
||||||
|
|||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
@file:Import("import-common.main.kts")
|
||||||
|
@file:Import("import-middle.main.kts")
|
||||||
|
|
||||||
|
sharedVar = sharedVar + 1
|
||||||
|
|
||||||
|
class CapturingClass1 {
|
||||||
|
val value = sharedVar
|
||||||
|
}
|
||||||
|
|
||||||
|
class CapturingClass2 {
|
||||||
|
fun f() = CapturingClass1().value
|
||||||
|
}
|
||||||
|
|
||||||
|
println("${SharedObject.greeting} ${from.msg} main")
|
||||||
|
println("sharedVar == ${CapturingClass2().f()}")
|
||||||
+1
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
|
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
|||||||
+1
@@ -73,6 +73,7 @@ class JsCoreScriptingCompiler(
|
|||||||
if (replCompilerState == null) GeneratorExtensions()
|
if (replCompilerState == null) GeneratorExtensions()
|
||||||
else object : GeneratorExtensions() {
|
else object : GeneratorExtensions() {
|
||||||
override fun getPreviousScripts() = replCompilerState.history.map { it.item.scriptSymbol }
|
override fun getPreviousScripts() = replCompilerState.history.map { it.item.scriptSymbol }
|
||||||
|
override val lowerScriptToClass: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
val psi2irContext = psi2ir.createGeneratorContext(module, bindingContext, symbolTable, generatorExtensions)
|
val psi2irContext = psi2ir.createGeneratorContext(module, bindingContext, symbolTable, generatorExtensions)
|
||||||
|
|||||||
Reference in New Issue
Block a user