K2 Scripting: add support for imported scripts
This commit is contained in:
committed by
Space Team
parent
d24fc3b581
commit
96bde033e1
+86
-23
@@ -45,6 +45,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.topologicalSort
|
||||
|
||||
internal val scriptsToClassesPhase = makeCustomPhase<JvmBackendContext, IrModuleFragment>(
|
||||
name = "ScriptsToClasses",
|
||||
@@ -58,7 +59,8 @@ internal val scriptsToClassesPhase = makeCustomPhase<JvmBackendContext, IrModule
|
||||
private class ScriptsToClassesLowering(val context: JvmBackendContext, val innerClassesSupport: JvmInnerClassesSupport) {
|
||||
|
||||
fun lower(module: IrModuleFragment) {
|
||||
val scriptsToClasses = mutableMapOf<IrScript, IrClass>()
|
||||
val scripts = mutableListOf<IrScript>()
|
||||
val scriptDependencies = mutableMapOf<IrScript, List<IrScript>>()
|
||||
|
||||
for (irFile in module.files) {
|
||||
val iterator = irFile.declarations.listIterator()
|
||||
@@ -66,7 +68,10 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
val declaration = iterator.next()
|
||||
if (declaration is IrScript) {
|
||||
val scriptClass = prepareScriptClass(irFile, declaration)
|
||||
scriptsToClasses[declaration] = scriptClass
|
||||
scripts.add(declaration)
|
||||
declaration.importedScripts.takeUnless { it.isNullOrEmpty() }?.let {
|
||||
scriptDependencies[declaration] = it.map { it.owner }
|
||||
}
|
||||
iterator.set(scriptClass)
|
||||
}
|
||||
}
|
||||
@@ -74,10 +79,11 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
|
||||
val symbolRemapper = ScriptsToClassesSymbolRemapper()
|
||||
|
||||
for ((irScript, irScriptClass) in scriptsToClasses) {
|
||||
finalizeScriptClass(irScriptClass, irScript, symbolRemapper)
|
||||
val orderedScripts = topologicalSort(scripts) { scriptDependencies[this] ?: emptyList() }.reversed()
|
||||
for (irScript in orderedScripts) {
|
||||
finalizeScriptClass(irScript, symbolRemapper)
|
||||
// TODO fix parents in script classes
|
||||
irScriptClass.patchDeclarationParents(irScript.parent)
|
||||
irScript.targetClass!!.owner.patchDeclarationParents(irScript.parent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +117,9 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
it.addIfNotNull(irScript.thisReceiver?.type)
|
||||
}
|
||||
irScript.earlierScripts?.forEach { scriptsReceivers.addIfNotNull(it.owner.thisReceiver?.type) }
|
||||
irScript.importedScripts?.forEach {
|
||||
scriptsReceivers.add(it.owner.targetClass!!.owner.thisReceiver!!.type)
|
||||
}
|
||||
irScript.implicitReceiversParameters.forEach {
|
||||
scriptsReceivers.add(it.type)
|
||||
scriptsReceivers.add(typeRemapper.remapType(it.type))
|
||||
@@ -165,7 +174,8 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
return capturingClasses
|
||||
}
|
||||
|
||||
private fun finalizeScriptClass(irScriptClass: IrClass, irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) {
|
||||
private fun finalizeScriptClass(irScript: IrScript, symbolRemapper: ScriptsToClassesSymbolRemapper) {
|
||||
val irScriptClass = irScript.targetClass!!.owner
|
||||
val typeRemapper = SimpleTypeRemapper(symbolRemapper)
|
||||
val capturingClasses = collectCapturingClasses(irScript, typeRemapper)
|
||||
|
||||
@@ -188,22 +198,44 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
}
|
||||
|
||||
val implicitReceiversFieldsWithParameters = arrayListOf<Pair<IrField, IrValueParameter>>().apply {
|
||||
irScript.implicitReceiversParameters.forEach { param ->
|
||||
|
||||
fun createField(name: Name, type: IrType): IrField {
|
||||
val field = irScriptClass.factory.createField(
|
||||
startOffset = UNDEFINED_OFFSET,
|
||||
endOffset = UNDEFINED_OFFSET,
|
||||
origin = IrDeclarationOrigin.SCRIPT_IMPLICIT_RECEIVER,
|
||||
name = Name.identifier("\$\$implicitReceiver_${param.type.classFqName?.shortName()?.asString()!!}"),
|
||||
name = name,
|
||||
visibility = DescriptorVisibilities.PRIVATE,
|
||||
symbol = IrFieldSymbolImpl(),
|
||||
type = typeRemapper.remapType(param.type),
|
||||
type = typeRemapper.remapType(type),
|
||||
isFinal = true,
|
||||
isStatic = false,
|
||||
isExternal = false
|
||||
)
|
||||
field.parent = irScriptClass
|
||||
irScriptClass.declarations.add(field)
|
||||
add(field to param)
|
||||
return field
|
||||
}
|
||||
|
||||
irScript.importedScripts?.forEach {
|
||||
val importedScriptClass = it.owner.targetClass!!.owner
|
||||
val type = importedScriptClass.defaultType
|
||||
val name = Name.identifier("\$\$importedScript_${type.classFqName?.shortName()?.asString()!!}")
|
||||
val param = irScriptClass.factory.createValueParameter(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.SCRIPT_IMPLICIT_RECEIVER, name, type, isAssignable = false,
|
||||
IrValueParameterSymbolImpl(), UNDEFINED_PARAMETER_INDEX, varargElementType = null,
|
||||
isCrossinline = false, isNoinline = false, isHidden = false,
|
||||
)
|
||||
param.parent = irScriptClass
|
||||
add(createField(name, type) to param)
|
||||
}
|
||||
irScript.implicitReceiversParameters.forEach { param ->
|
||||
add(
|
||||
createField(
|
||||
Name.identifier("\$\$implicitReceiver_${param.type.classFqName?.shortName()?.asString()!!}"),
|
||||
param.type
|
||||
) to param
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,19 +253,30 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
|
||||
irScriptClass.thisReceiver = scriptTransformer.scriptClassReceiver
|
||||
|
||||
val defaultContext = ScriptToClassTransformerContext(
|
||||
val defaultContextForDeclarations = ScriptToClassTransformerContext(
|
||||
valueParameterForScriptThis = irScriptClass.thisReceiver?.symbol,
|
||||
fieldForScriptThis = null,
|
||||
valueParameterForFieldReceiver = null,
|
||||
isInScriptConstructor = false
|
||||
)
|
||||
|
||||
fun <E : IrElement> E.patchForClass(): IrElement =
|
||||
val defaultContextForTopLevelStatements = ScriptToClassTransformerContext(
|
||||
valueParameterForScriptThis = irScriptClass.thisReceiver?.symbol,
|
||||
fieldForScriptThis = null,
|
||||
valueParameterForFieldReceiver = null,
|
||||
isInScriptConstructor = true
|
||||
)
|
||||
|
||||
fun <E : IrElement> E.patchDeclarationForClass(): IrElement =
|
||||
transform(
|
||||
scriptTransformer,
|
||||
(this as? IrDeclaration)?.let { defaultContext.copy( topLevelDeclaration = it) } ?: defaultContext
|
||||
(this as? IrDeclaration)?.let { defaultContextForDeclarations.copy(topLevelDeclaration = it) } ?: defaultContextForDeclarations
|
||||
).transform(lambdaPatcher, ScriptFixLambdasTransformerContext())
|
||||
|
||||
fun <E : IrElement> E.patchTopLevelStatementForClass(): IrElement =
|
||||
transform(scriptTransformer, defaultContextForTopLevelStatements)
|
||||
.transform(lambdaPatcher, ScriptFixLambdasTransformerContext())
|
||||
|
||||
val explicitParametersWithFields = irScript.explicitCallParameters.map { parameter ->
|
||||
val field = irScriptClass.addField {
|
||||
startOffset = parameter.startOffset
|
||||
@@ -247,7 +290,7 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
parameter to field
|
||||
}
|
||||
|
||||
(irScript.constructor?.patchForClass() as? IrConstructor
|
||||
(irScript.constructor?.patchDeclarationForClass() as? IrConstructor
|
||||
?: createConstructor(irScriptClass, irScript, implicitReceiversFieldsWithParameters)).also { constructor ->
|
||||
val explicitParamsStartIndex = if (irScript.earlierScriptsParameter == null) 0 else 1
|
||||
val explicitParameters = constructor.valueParameters.subList(
|
||||
@@ -286,7 +329,7 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
+irSetField(
|
||||
irGet(irScriptClass.thisReceiver!!),
|
||||
field,
|
||||
irGet(correspondingParameter.patchForClass() as IrValueParameter)
|
||||
irGet(correspondingParameter.patchDeclarationForClass() as IrValueParameter)
|
||||
)
|
||||
}
|
||||
+IrInstanceInitializerCallImpl(
|
||||
@@ -303,11 +346,11 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
irScript.statements.forEach { scriptStatement ->
|
||||
when (scriptStatement) {
|
||||
is IrVariable -> {
|
||||
val copy = scriptStatement.patchForClass() as IrVariable
|
||||
val copy = scriptStatement.patchDeclarationForClass() as IrVariable
|
||||
irScriptClass.addSimplePropertyFrom(copy)
|
||||
}
|
||||
is IrDeclaration -> {
|
||||
val copy = scriptStatement.patchForClass() as IrDeclaration
|
||||
val copy = scriptStatement.patchDeclarationForClass() as IrDeclaration
|
||||
irScriptClass.declarations.add(copy)
|
||||
// temporary way to avoid name clashes
|
||||
// TODO: remove as soon as main generation become an explicit configuration option
|
||||
@@ -316,7 +359,7 @@ private class ScriptsToClassesLowering(val context: JvmBackendContext, val inner
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val transformedStatement = scriptStatement.patchForClass() as IrStatement
|
||||
val transformedStatement = scriptStatement.patchTopLevelStatementForClass() as IrStatement
|
||||
irScriptClass.addAnonymousInitializer().also { irInitializer ->
|
||||
irInitializer.body =
|
||||
context.createIrBuilder(irInitializer.symbol).irBlockBody {
|
||||
@@ -707,10 +750,20 @@ private class ScriptToClassTransformer(
|
||||
expression.putTypeArgument(i, expression.getTypeArgument(i)?.remapType())
|
||||
}
|
||||
if (expression.dispatchReceiver == null && (expression.symbol.owner as? IrDeclaration)?.needsScriptReceiver() == true) {
|
||||
val memberAccessTargetReceiverType = when (val callee = expression.symbol.owner) {
|
||||
is IrFunction -> callee.dispatchReceiverParameter?.type
|
||||
is IrProperty -> callee.getter?.dispatchReceiverParameter?.type
|
||||
else -> null
|
||||
}
|
||||
expression.dispatchReceiver =
|
||||
getAccessCallForScriptInstance(
|
||||
data, expression.startOffset, expression.endOffset, expression.origin, originalReceiverParameter = null
|
||||
)
|
||||
if (memberAccessTargetReceiverType != null && memberAccessTargetReceiverType != scriptClassReceiver.type)
|
||||
getAccessCallForImplicitReceiver(
|
||||
data, expression, memberAccessTargetReceiverType, expression.origin, originalReceiverParameter = null
|
||||
)
|
||||
else
|
||||
getAccessCallForScriptInstance(
|
||||
data, expression.startOffset, expression.endOffset, expression.origin, originalReceiverParameter = null
|
||||
)
|
||||
}
|
||||
return super.visitMemberAccess(expression, data) as IrExpression
|
||||
}
|
||||
@@ -909,7 +962,17 @@ private class ScriptToClassTransformer(
|
||||
}
|
||||
|
||||
private fun IrDeclaration.needsScriptReceiver() =
|
||||
(this as? IrFunction)?.dispatchReceiverParameter?.origin == IrDeclarationOrigin.SCRIPT_THIS_RECEIVER
|
||||
when (this) {
|
||||
is IrFunction -> this.dispatchReceiverParameter
|
||||
is IrProperty -> {
|
||||
this.getter?.takeIf {
|
||||
// without this exception, the PropertyReferenceLowering generates clinit with attempt to use script as receiver
|
||||
// TODO: find whether it is a valid exception and maybe how to make it more obvious
|
||||
it.origin != IrDeclarationOrigin.DELEGATED_PROPERTY_ACCESSOR
|
||||
}?.dispatchReceiverParameter
|
||||
}
|
||||
else -> null
|
||||
}?.origin == IrDeclarationOrigin.SCRIPT_THIS_RECEIVER
|
||||
}
|
||||
|
||||
private class ScriptFixLambdasTransformer(val irScriptClass: IrClass) : IrElementTransformer<ScriptFixLambdasTransformerContext> {
|
||||
@@ -994,4 +1057,4 @@ private inline fun IrClass.addAnonymousInitializer(builder: IrFunctionBuilder.()
|
||||
private val IrScript.needsReceiverProcessing: Boolean
|
||||
// in K2 we need to add dispatch receiver to the top-level declarations, and in all cases receivers should be replaced
|
||||
// for all kinds of implicit receivers
|
||||
get() = origin == SCRIPT_K2_ORIGIN || earlierScripts?.isNotEmpty() == true || implicitReceiversParameters.isNotEmpty()
|
||||
get() = origin == SCRIPT_K2_ORIGIN || importedScripts?.isNotEmpty() == true || earlierScripts?.isNotEmpty() == true || implicitReceiversParameters.isNotEmpty()
|
||||
@@ -43,6 +43,8 @@ abstract class IrScript : IrDeclarationBase(), IrDeclarationWithName, IrDeclarat
|
||||
|
||||
abstract var earlierScriptsParameter: IrValueParameter?
|
||||
|
||||
abstract var importedScripts: List<IrScriptSymbol>?
|
||||
|
||||
abstract var earlierScripts: List<IrScriptSymbol>?
|
||||
|
||||
abstract var targetClass: IrClassSymbol?
|
||||
|
||||
@@ -53,6 +53,7 @@ class IrScriptImpl(
|
||||
override lateinit var providedPropertiesParameters: List<IrValueParameter>
|
||||
override var resultProperty: IrPropertySymbol? = null
|
||||
override var earlierScriptsParameter: IrValueParameter? = null
|
||||
override var importedScripts: List<IrScriptSymbol>? = null
|
||||
override var earlierScripts: List<IrScriptSymbol>? = null
|
||||
override var targetClass: IrClassSymbol? = null
|
||||
override var constructor: IrConstructor? = null
|
||||
|
||||
@@ -131,6 +131,7 @@ open class DeepCopyIrTreeWithSymbols(
|
||||
).also { scriptCopy ->
|
||||
scriptCopy.thisReceiver = declaration.thisReceiver?.transform()
|
||||
declaration.statements.mapTo(scriptCopy.statements) { it.transform() }
|
||||
scriptCopy.importedScripts = declaration.importedScripts
|
||||
scriptCopy.earlierScripts = declaration.earlierScripts
|
||||
scriptCopy.earlierScriptsParameter = declaration.earlierScriptsParameter
|
||||
scriptCopy.explicitCallParameters = declaration.explicitCallParameters.memoryOptimizedMap { it.transform() }
|
||||
|
||||
@@ -548,6 +548,7 @@ object IrTree : AbstractTreeBuilder() {
|
||||
+listField("providedPropertiesParameters", valueParameter, mutability = Var, isChild = true)
|
||||
+field("resultProperty", propertySymbolType, nullable = true)
|
||||
+field("earlierScriptsParameter", valueParameter, nullable = true, isChild = true)
|
||||
+listField("importedScripts", scriptSymbolType, mutability = Var, nullable = true)
|
||||
+listField("earlierScripts", scriptSymbolType, mutability = Var, nullable = true)
|
||||
+field("targetClass", classSymbolType, nullable = true)
|
||||
+field("constructor", constructor, nullable = true) // K1
|
||||
|
||||
Reference in New Issue
Block a user