[IR JS BE] create lowering for scripts

This commit is contained in:
Vitaliy.Tikhonov
2019-08-30 15:38:53 +03:00
committed by romanart
parent 240abdb750
commit 184ae2fa43
4 changed files with 316 additions and 1 deletions
@@ -229,6 +229,14 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, private val
open fun functionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getFunction(n)) open fun functionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getFunction(n))
open fun suspendFunctionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getSuspendFunction(n)) open fun suspendFunctionN(n: Int): IrClassSymbol = symbolTable.referenceClass(builtIns.getSuspendFunction(n))
fun kproperty0(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty0)
fun kproperty1(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty1)
fun kproperty2(): IrClassSymbol = symbolTable.referenceClass(builtIns.kProperty2)
fun kmutableproperty0(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty0)
fun kmutableproperty1(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty1)
fun kmutableproperty2(): IrClassSymbol = symbolTable.referenceClass(builtIns.kMutableProperty2)
val extensionToString = getSimpleFunction(Name.identifier("toString")) { val extensionToString = getSimpleFunction(Name.identifier("toString")) {
it.dispatchReceiverParameter == null && it.extensionReceiverParameter != null && it.dispatchReceiverParameter == null && it.extensionReceiverParameter != null &&
KotlinBuiltIns.isNullableAny(it.extensionReceiverParameter!!.type) && it.valueParameters.size == 0 KotlinBuiltIns.isNullableAny(it.extensionReceiverParameter!!.type) && it.valueParameters.size == 0
@@ -377,12 +377,14 @@ private val objectUsageLoweringPhase = makeCustomJsModulePhase(
val jsPhases = namedIrModulePhase( val jsPhases = namedIrModulePhase(
name = "IrModuleLowering", name = "IrModuleLowering",
description = "IR module lowering", description = "IR module lowering",
lower = validateIrBeforeLowering then lower = scriptRemoveReceiverLowering then
validateIrBeforeLowering then
testGenerationPhase then testGenerationPhase then
expectDeclarationsRemovingPhase then expectDeclarationsRemovingPhase then
stripTypeAliasDeclarationsPhase then stripTypeAliasDeclarationsPhase then
arrayConstructorPhase then arrayConstructorPhase then
functionInliningPhase then functionInliningPhase then
createScriptFunctionsPhase then
provisionalFunctionExpressionPhase then provisionalFunctionExpressionPhase then
lateinitLoweringPhase then lateinitLoweringPhase then
tailrecLoweringPhase then tailrecLoweringPhase then
@@ -0,0 +1,141 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrSetField
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.name.Name
val createScriptFunctionsPhase = makeIrModulePhase(
::CreateScriptFunctionsPhase,
name = "CreateScriptFunctionsPhase",
description = "Create functions for initialize and evaluate script"
)
private object SCRIPT_FUNCTION : IrDeclarationOriginImpl("SCRIPT_FUNCTION")
class CreateScriptFunctionsPhase(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.declarations.transformFlat { declaration ->
if (declaration is IrScript) lower(declaration)
else null
}
}
private fun lower(irScript: IrScript): List<IrDeclaration> {
val (startOffset, endOffset) = getFunctionBodyOffsets(irScript)
val initializeStatements = irScript.declarations
.filterIsInstance<IrProperty>()
.mapNotNull { it.backingField }
.filter { it.initializer != null }
.map { Pair(it, it.initializer!!.expression) }
initializeStatements.forEach { it.first.initializer = null }
val initializeScriptFunction = createFunction(irScript, "\$initializeScript\$", context.irBuiltIns.unitType).also {
it.body = IrBlockBodyImpl(
startOffset,
endOffset,
initializeStatements.map { (field, expression) -> createIrSetField(field, expression) }
)
}
val evaluateScriptFunction = createFunction(irScript, "\$evaluateScript\$", getReturnType(irScript)).also {
it.body = IrBlockBodyImpl(
startOffset,
endOffset,
irScript.statements.prepareForEvaluateScriptFunction(it)
)
}
with(irScript) {
declarations += initializeScriptFunction
declarations += evaluateScriptFunction
statements.clear()
statements += createCall(initializeScriptFunction)
statements += createCall(evaluateScriptFunction)
}
return listOf(irScript)
}
private fun getFunctionBodyOffsets(irScript: IrScript): Pair<Int, Int> {
return with(irScript.statements) {
if (isEmpty()) {
Pair(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
} else {
Pair(irScript.statements.first().startOffset, irScript.statements.last().endOffset)
}
}
}
private fun getReturnType(irScript: IrScript): IrType {
return (irScript.statements.lastOrNull() as? IrExpression)?.type ?: context.irBuiltIns.unitType
}
private fun createFunction(irScript: IrScript, name: String, returnType: IrType): IrFunctionImpl {
val (startOffset, endOffset) = getFunctionBodyOffsets(irScript)
val descriptor = WrappedSimpleFunctionDescriptor()
return IrFunctionImpl(
startOffset, endOffset, SCRIPT_FUNCTION,
IrSimpleFunctionSymbolImpl(descriptor),
Name.identifier(name),
Visibilities.PRIVATE, Modality.FINAL, returnType,
isInline = false, isExternal = false, isTailrec = false, isSuspend = false
).also {
descriptor.bind(it)
it.parent = irScript
}
}
private fun List<IrStatement>.prepareForEvaluateScriptFunction(evaluateScriptFunction: IrFunction): List<IrStatement> {
return if (isNotEmpty()) {
val returnStatement = IrReturnImpl(
last().startOffset,
last().endOffset,
context.irBuiltIns.nothingType,
evaluateScriptFunction.symbol,
last() as IrExpression
)
dropLast(1) + returnStatement
} else emptyList()
}
private fun createIrSetField(field: IrField, expression: IrExpression): IrSetField {
return IrSetFieldImpl(
field.startOffset,
field.endOffset,
field.symbol,
null,
expression,
expression.type
)
}
private fun createCall(function: IrFunction): IrCall {
return IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, function.returnType, function.symbol)
}
}
@@ -0,0 +1,164 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.descriptors.ScriptDescriptor
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrPropertyReferenceImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.transformFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import java.lang.IllegalArgumentException
val scriptRemoveReceiverLowering = makeIrModulePhase(
::ScriptRemoveReceiverLowering,
name = "ScriptRemoveReceiver",
description = "Remove receivers for declarations in script"
)
private class ScriptRemoveReceiverLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
if (context.scriptMode) {
irFile.declarations.transformFlat {
if (it is IrScript) {
lower(it)
} else null
}
}
}
fun lower(script: IrScript): List<IrScript> {
val transformer: IrElementTransformerVoid = object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
if (expression.symbol.owner.parent is IrScript) {
expression.dispatchReceiver = null
}
return super.visitCall(expression)
}
override fun visitFieldAccess(expression: IrFieldAccessExpression): IrExpression {
if (expression.symbol.owner.parent is IrScript) {
expression.receiver = null
}
return super.visitFieldAccess(expression)
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
if (expression.symbol.owner.parent is IrScript) {
expression.dispatchReceiver = null
val result = with(super.visitFunctionReference(expression) as IrFunctionReference) {
val arguments = (type as IrSimpleType).arguments.filter {
!(it is IrTypeProjection && it.type is IrSimpleType && (it.type as IrSimpleType).classifier.descriptor is ScriptDescriptor)
}
IrFunctionReferenceImpl(
startOffset,
endOffset,
IrSimpleTypeImpl(
context.ir.symbols.functionN(arguments.size),
(type as IrSimpleType).hasQuestionMark,
arguments,
type.annotations
),
symbol,
descriptor,
typeArgumentsCount,
valueArgumentsCount,
origin
).also {
it.dispatchReceiver = dispatchReceiver
it.extensionReceiver = extensionReceiver
}
}
return result
}
return expression
}
override fun visitPropertyReference(expression: IrPropertyReference): IrExpression {
expression.transformChildrenVoid(this)
if (expression.symbol.owner.parent is IrScript) {
expression.dispatchReceiver = null
val result = with(super.visitPropertyReference(expression) as IrPropertyReference) {
val arguments = (type as IrSimpleType).arguments.filter {
!(it is IrTypeProjection && it.type is IrSimpleType && (it.type as IrSimpleType).classifier.descriptor is ScriptDescriptor)
}
IrPropertyReferenceImpl(
startOffset,
endOffset,
IrSimpleTypeImpl(
(if (setter == null) getPropertyN(arguments.size) else getMutablePropertyN(arguments.size)),
(type as IrSimpleType).hasQuestionMark,
arguments,
type.annotations
),
symbol,
descriptor,
typeArgumentsCount,
field,
getter,
setter,
origin
).also {
it.dispatchReceiver = dispatchReceiver
it.extensionReceiver = extensionReceiver
}
}
return result
}
return expression
}
private fun getPropertyN(n: Int): IrClassSymbol {
return when (n) {
2 -> context.ir.symbols.kproperty2()
1 -> context.ir.symbols.kproperty1()
0 -> context.ir.symbols.kproperty0()
else -> throw IllegalArgumentException()
}
}
private fun getMutablePropertyN(n: Int): IrClassSymbol {
return when (n) {
2 -> context.ir.symbols.kmutableproperty2()
1 -> context.ir.symbols.kmutableproperty1()
0 -> context.ir.symbols.kmutableproperty0()
else -> throw IllegalArgumentException()
}
}
}
script.transformChildrenVoid(transformer)
script.declarations.forEach {
when (it) {
is IrSimpleFunction -> it.dispatchReceiverParameter = null
is IrProperty -> {
it.getter?.dispatchReceiverParameter = null
it.setter?.dispatchReceiverParameter = null
}
}
}
return listOf(script)
}
}