Make IrScript a statement container first, update JS support

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