[JS IR BE] Implement block decomposition lowering
This commit is contained in:
@@ -31,8 +31,8 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
class JsIrBackendContext(
|
||||
val module: ModuleDescriptor,
|
||||
override val irBuiltIns: IrBuiltIns,
|
||||
irModuleFragment: IrModuleFragment,
|
||||
symbolTable: SymbolTable
|
||||
val symbolTable: SymbolTable,
|
||||
irModuleFragment: IrModuleFragment
|
||||
) : CommonBackendContext {
|
||||
|
||||
val intrinsics = JsIntrinsics(module, irBuiltIns, symbolTable)
|
||||
|
||||
@@ -9,7 +9,10 @@ import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.BlockDecomposerLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.FunctionReferenceLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.IntrinsicifyCallsLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.lower.SecondaryCtorLowering
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||
@@ -38,8 +41,8 @@ fun compile(
|
||||
val context = JsIrBackendContext(
|
||||
analysisResult.moduleDescriptor,
|
||||
psi2IrContext.irBuiltIns,
|
||||
moduleFragment,
|
||||
psi2IrContext.symbolTable
|
||||
psi2IrContext.symbolTable,
|
||||
moduleFragment
|
||||
)
|
||||
|
||||
ExternalDependenciesGenerator(psi2IrContext.symbolTable, psi2IrContext.irBuiltIns).generateUnboundSymbolsAsDependencies(moduleFragment)
|
||||
@@ -62,6 +65,7 @@ fun JsIrBackendContext.lower(file: IrFile) {
|
||||
InnerClassConstructorCallsLowering(this).runOnFilePostfix(file)
|
||||
PropertiesLowering().lower(file)
|
||||
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file)
|
||||
BlockDecomposerLowering(this).runOnFilePostfix(file)
|
||||
SecondaryCtorLowering(this).runOnFilePostfix(file)
|
||||
IntrinsicifyCallsLowering(this).lower(file)
|
||||
FunctionReferenceLowering(this).lower(file)
|
||||
|
||||
+12
@@ -5,17 +5,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.descriptors
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.createFunctionSymbol
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
@@ -85,6 +88,15 @@ object JsSymbolBuilder {
|
||||
SourceElement.NO_SOURCE
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
fun buildTempVar(containingSymbol: IrFunctionSymbol, type: KotlinType, name: String? = null, mutable: Boolean = false) = IrVariableSymbolImpl(
|
||||
IrTemporaryVariableDescriptorImpl(
|
||||
containingSymbol.descriptor,
|
||||
Name.identifier(name ?: "tmp"),
|
||||
type, mutable
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
@@ -42,13 +43,39 @@ object JsIrBuilder {
|
||||
|
||||
fun buildFunction(symbol: IrSimpleFunctionSymbol) = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol)
|
||||
|
||||
fun buildGetValue(symbol: IrValueSymbol) = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, SYNTHESIZED_STATEMENT)
|
||||
fun buildGetObjectValue(type: KotlinType, classSymbol: IrClassSymbol) =
|
||||
IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, classSymbol)
|
||||
fun buildGetClass(expression: IrExpression, type: KotlinType) = IrGetClassImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, expression)
|
||||
|
||||
fun buildBlockBody(stmts: List<IrStatement>) = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, stmts)
|
||||
fun buildGetValue(symbol: IrValueSymbol) = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, SYNTHESIZED_STATEMENT)
|
||||
fun buildSetVariable(symbol: IrVariableSymbol, value: IrExpression) =
|
||||
IrSetVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, value, SYNTHESIZED_STATEMENT)
|
||||
|
||||
fun buildGetField(symbol: IrFieldSymbol, receiver: IrExpression?, superQualifierSymbol: IrClassSymbol? = null) =
|
||||
IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, receiver, SYNTHESIZED_STATEMENT, superQualifierSymbol)
|
||||
|
||||
fun buildSetField(symbol: IrFieldSymbol, receiver: IrExpression?, value: IrExpression, superQualifierSymbol: IrClassSymbol? = null) =
|
||||
IrSetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, symbol, receiver, value, SYNTHESIZED_STATEMENT, superQualifierSymbol)
|
||||
|
||||
fun buildBlockBody(statements: List<IrStatement>) = IrBlockBodyImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, statements)
|
||||
|
||||
fun buildBlock(type: KotlinType) = IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, SYNTHESIZED_STATEMENT)
|
||||
fun buildBlock(type: KotlinType, statements: List<IrStatement>) =
|
||||
IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, SYNTHESIZED_STATEMENT, statements)
|
||||
|
||||
fun buildFunctionReference(type: KotlinType, symbol: IrFunctionSymbol) =
|
||||
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor)
|
||||
|
||||
fun buildVar(symbol: IrVariableSymbol) = IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol)
|
||||
fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null) =
|
||||
IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol).apply { this.initializer = initializer }
|
||||
|
||||
fun buildBreak(type: KotlinType, loop: IrLoop) = IrBreakImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop)
|
||||
fun buildContinue(type: KotlinType, loop: IrLoop) = IrContinueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop)
|
||||
|
||||
fun buildIfElse(type: KotlinType, cond: IrExpression, thenBranch: IrExpression, elseBranch: IrExpression? = null) = IrIfThenElseImpl(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, cond, thenBranch, elseBranch, SYNTHESIZED_STATEMENT
|
||||
)
|
||||
|
||||
fun buildNull(type: KotlinType) = IrConstImpl.constNull(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type)
|
||||
fun buildBoolean(type: KotlinType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
|
||||
}
|
||||
+708
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. 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.FunctionLoweringPass
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.descriptors.JsSymbolBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.descriptors.initialize
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.util.transformFlat
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
|
||||
private typealias VisitData = Nothing?
|
||||
|
||||
class BlockDecomposerLowering(val context: JsIrBackendContext) : FunctionLoweringPass {
|
||||
|
||||
private lateinit var function: IrFunction
|
||||
private var tmpVarCounter: Int = 0
|
||||
|
||||
private val statementVisitor = StatementVisitor()
|
||||
private val expressionVisitor = ExpressionVisitor()
|
||||
|
||||
private val constTrue = JsIrBuilder.buildBoolean(context.builtIns.booleanType, true)
|
||||
private val constFalse = JsIrBuilder.buildBoolean(context.builtIns.booleanType, false)
|
||||
private val nothingType = context.builtIns.nullableNothingType
|
||||
|
||||
private val unitValue =
|
||||
JsIrBuilder.buildGetObjectValue(context.builtIns.unitType, context.symbolTable.referenceClass(context.builtIns.unit))
|
||||
|
||||
private val unreachableFunction =
|
||||
JsSymbolBuilder.buildSimpleFunction(context.module, Namer.UNREACHABLE_NAME).initialize(type = nothingType)
|
||||
|
||||
override fun lower(irFunction: IrFunction) {
|
||||
function = irFunction
|
||||
tmpVarCounter = 0
|
||||
irFunction.body?.accept(statementVisitor, null)
|
||||
}
|
||||
|
||||
enum class VisitStatus {
|
||||
DECOMPOSED,
|
||||
TERMINATED,
|
||||
KEPT
|
||||
}
|
||||
|
||||
abstract class VisitResult(val status: VisitStatus) {
|
||||
open val statements: MutableList<IrStatement> get() = error("No statement is possible here")
|
||||
open val resultValue: IrExpression get() = error("This result has no value")
|
||||
|
||||
abstract fun runIfChanged(action: VisitResult.() -> VisitResult): VisitResult
|
||||
abstract fun <T> runIfChangedOrDefault(default: T, action: VisitResult.(d: T) -> T): T
|
||||
abstract fun applyIfChanged(action: VisitResult.() -> Unit): VisitResult
|
||||
|
||||
}
|
||||
|
||||
object KeptResult : VisitResult(VisitStatus.KEPT) {
|
||||
override fun runIfChanged(action: VisitResult.() -> VisitResult) = this
|
||||
override fun <T> runIfChangedOrDefault(default: T, action: VisitResult.(d: T) -> T) = default
|
||||
override fun applyIfChanged(action: VisitResult.() -> Unit) = this
|
||||
}
|
||||
|
||||
abstract class ChangedResult(
|
||||
override val statements: MutableList<IrStatement>,
|
||||
override val resultValue: IrExpression,
|
||||
status: VisitStatus
|
||||
) :
|
||||
VisitResult(status) {
|
||||
override fun runIfChanged(action: VisitResult.() -> VisitResult) = run { action() }
|
||||
override fun <T> runIfChangedOrDefault(default: T, action: VisitResult.(d: T) -> T) = run { action(default) }
|
||||
override fun applyIfChanged(action: VisitResult.() -> Unit) = apply { action() }
|
||||
}
|
||||
|
||||
class DecomposedResult(statements: MutableList<IrStatement>, resultValue: IrExpression) :
|
||||
ChangedResult(statements, resultValue, VisitStatus.DECOMPOSED) {
|
||||
|
||||
constructor(statement: IrStatement, resultValue: IrExpression) : this(mutableListOf(statement), resultValue)
|
||||
}
|
||||
|
||||
class TerminatedResult(statements: MutableList<IrStatement>, resultValue: IrExpression) :
|
||||
ChangedResult(statements, resultValue, VisitStatus.TERMINATED)
|
||||
|
||||
abstract inner class DecomposerVisitor : IrElementVisitor<VisitResult, VisitData> {
|
||||
override fun visitElement(element: IrElement, data: VisitData) = KeptResult
|
||||
}
|
||||
|
||||
inner class StatementVisitor : DecomposerVisitor() {
|
||||
override fun visitBlockBody(body: IrBlockBody, data: VisitData): VisitResult {
|
||||
body.statements.transformFlat {
|
||||
processStatement(it, data)
|
||||
}
|
||||
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: VisitData): VisitResult {
|
||||
expression.statements.transformFlat {
|
||||
processStatement(it, data)
|
||||
}
|
||||
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
private fun processStatement(statement: IrStatement, data: VisitData): List<IrStatement>? {
|
||||
val result = statement.accept(this, data)
|
||||
|
||||
if (result == KeptResult) return null
|
||||
return result.statements
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: IrExpression, data: VisitData): VisitResult = expression.accept(expressionVisitor, data)
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: VisitData): VisitResult {
|
||||
val expressionResult = expression.value.accept(expressionVisitor, data)
|
||||
|
||||
return expressionResult.runIfChanged {
|
||||
val returnValue = expressionResult.resultValue
|
||||
expressionResult.statements += IrReturnImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
expression.returnTargetSymbol,
|
||||
returnValue
|
||||
)
|
||||
DecomposedResult(expressionResult.statements, unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitBreakContinue(jump: IrBreakContinue, data: VisitData) = KeptResult as VisitResult
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: VisitData): VisitResult {
|
||||
val expressionResult = expression.value.accept(expressionVisitor, data)
|
||||
|
||||
return expressionResult.runIfChanged {
|
||||
val returnValue = expressionResult.resultValue
|
||||
expressionResult.statements += IrThrowImpl(expression.startOffset, expression.endOffset, expression.type, returnValue)
|
||||
DecomposedResult(expressionResult.statements, unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: VisitData): VisitResult {
|
||||
declaration.initializer?.let {
|
||||
val initResult = it.accept(expressionVisitor, data)
|
||||
return initResult.runIfChanged {
|
||||
statements += declaration.apply { initializer = resultValue }
|
||||
DecomposedResult(statements, unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: VisitData): VisitResult {
|
||||
val newWhen = processWhen(expression, data, expressionVisitor, this) { visitResult, original ->
|
||||
visitResult.runIfChangedOrDefault(listOf(original)) { statements }
|
||||
}
|
||||
|
||||
if (newWhen != expression) {
|
||||
return DecomposedResult(newWhen, unitValue)
|
||||
}
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
private inner class BreakContinueUpdater(val breakLoop: IrLoop, val continueLoop: IrLoop) : IrElementTransformer<IrLoop> {
|
||||
override fun visitBreak(jump: IrBreak, data: IrLoop) = jump.apply {
|
||||
if (loop == data) loop = breakLoop
|
||||
}
|
||||
|
||||
override fun visitContinue(jump: IrContinue, data: IrLoop) = jump.apply {
|
||||
if (loop == data) loop = continueLoop
|
||||
}
|
||||
}
|
||||
|
||||
// while (c_block {}) {
|
||||
// body {}
|
||||
// }
|
||||
//
|
||||
// is transformed into
|
||||
//
|
||||
// while (true) {
|
||||
// var cond = c_block {}
|
||||
// if (!cond) break
|
||||
// body {}
|
||||
// }
|
||||
//
|
||||
override fun visitWhileLoop(loop: IrWhileLoop, data: VisitData): VisitResult {
|
||||
|
||||
val conditionResult = loop.condition.accept(expressionVisitor, data)
|
||||
val bodyResult = loop.body?.accept(this, data)
|
||||
val unitType = context.builtIns.unitType
|
||||
|
||||
return conditionResult.runIfChanged {
|
||||
bodyResult?.run { assert(status == VisitStatus.KEPT) }
|
||||
|
||||
val condVariable = conditionResult.resultValue
|
||||
val thenBlock = JsIrBuilder.buildBlock(unitType, listOf(JsIrBuilder.buildBreak(unitType, loop)))
|
||||
val breakCond = JsIrBuilder.buildCall(context.irBuiltIns.booleanNotSymbol).apply {
|
||||
putValueArgument(0, condVariable)
|
||||
}
|
||||
|
||||
statements.add(JsIrBuilder.buildIfElse(unitType, breakCond, thenBlock))
|
||||
|
||||
val oldBody = loop.body!!
|
||||
|
||||
val newBody = statements + oldBody
|
||||
|
||||
val newLoop = IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||
label = loop.label
|
||||
condition = constTrue
|
||||
body = oldBody.run { IrBlockImpl(startOffset, endOffset, type, origin, newBody) }
|
||||
}
|
||||
|
||||
newLoop.body?.transform(BreakContinueUpdater(newLoop, newLoop), loop)
|
||||
|
||||
DecomposedResult(newLoop, unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
// do {
|
||||
// body {}
|
||||
// } while (c_block {})
|
||||
//
|
||||
// is transformed into
|
||||
//
|
||||
// do {
|
||||
// do {
|
||||
// body {}
|
||||
// } while (false)
|
||||
// cond = c_block {}
|
||||
// } while (cond)
|
||||
//
|
||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: VisitData): VisitResult {
|
||||
|
||||
val bodyResult = loop.body?.accept(this, data)
|
||||
val conditionResult = loop.condition.accept(expressionVisitor, data)
|
||||
val unitType = context.builtIns.unitType
|
||||
|
||||
return conditionResult.runIfChanged {
|
||||
bodyResult?.run { assert(status == VisitStatus.KEPT) }
|
||||
|
||||
val body = loop.body!!
|
||||
|
||||
val innerLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, unitType, loop.origin, body, constFalse).apply {
|
||||
label = makeLoopLabel()
|
||||
}
|
||||
|
||||
val condVariable = conditionResult.resultValue
|
||||
val newBody = JsIrBuilder.buildBlock(body.type, listOf(innerLoop) + statements)
|
||||
val newLoop = IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, unitType, loop.origin, newBody, condVariable).apply {
|
||||
label = loop.label ?: makeLoopLabel()
|
||||
}
|
||||
|
||||
body.transform(BreakContinueUpdater(newLoop, innerLoop), loop)
|
||||
|
||||
DecomposedResult(newLoop, unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: VisitData): VisitResult {
|
||||
val argumentResult = expression.argument.accept(expressionVisitor, data)
|
||||
|
||||
return argumentResult.runIfChanged {
|
||||
val newOperator = IrTypeOperatorCallImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
expression.type,
|
||||
expression.operator,
|
||||
expression.typeOperand,
|
||||
resultValue,
|
||||
expression.typeOperandClassifier
|
||||
)
|
||||
|
||||
val resValue: IrExpression
|
||||
|
||||
if (!KotlinBuiltIns.isUnit(expression.type)) {
|
||||
val resVar = makeTempVar(expression.type)
|
||||
statements += JsIrBuilder.buildVar(resVar, newOperator)
|
||||
resValue = JsIrBuilder.buildGetValue(resVar)
|
||||
} else {
|
||||
statements += newOperator
|
||||
resValue = unitValue
|
||||
}
|
||||
|
||||
DecomposedResult(statements, resValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitField(declaration: IrField, data: VisitData): VisitResult {
|
||||
if (declaration.initializer == null) return KeptResult
|
||||
|
||||
val result = declaration.initializer!!.accept(expressionVisitor, data)
|
||||
|
||||
return result.runIfChanged {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSetField(expression: IrSetField, data: VisitData): VisitResult {
|
||||
var needNew = false
|
||||
|
||||
val receiverResult = expression.receiver?.accept(expressionVisitor, data)
|
||||
val valueResult = expression.value.accept(expressionVisitor, data)
|
||||
|
||||
val newStatements = mutableListOf<IrStatement>()
|
||||
|
||||
val newReceiver = receiverResult?.runIfChangedOrDefault(expression.receiver) {
|
||||
newStatements += statements
|
||||
needNew = true
|
||||
resultValue
|
||||
}
|
||||
|
||||
val newValue = valueResult.runIfChangedOrDefault(expression.value) {
|
||||
newStatements += statements
|
||||
needNew = true
|
||||
resultValue
|
||||
}
|
||||
|
||||
if (needNew) {
|
||||
newStatements += JsIrBuilder.buildSetField(expression.symbol, newReceiver, newValue, expression.superQualifierSymbol)
|
||||
return DecomposedResult(newStatements, unitValue)
|
||||
}
|
||||
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
override fun visitSetVariable(expression: IrSetVariable, data: VisitData): VisitResult {
|
||||
val valueResult = expression.value.accept(expressionVisitor, data)
|
||||
|
||||
return valueResult.runIfChanged {
|
||||
statements += JsIrBuilder.buildSetVariable(expression.symbol, resultValue)
|
||||
DecomposedResult(statements, unitValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ExpressionVisitor : DecomposerVisitor() {
|
||||
override fun visitExpression(expression: IrExpression, data: VisitData) = KeptResult
|
||||
|
||||
//
|
||||
// val x = block {
|
||||
// ...
|
||||
// expr
|
||||
// }
|
||||
//
|
||||
// is transformed into
|
||||
//
|
||||
// val x_tmp
|
||||
// block {
|
||||
// ...
|
||||
// x_tmp = expr
|
||||
// }
|
||||
// val x = x_tmp
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, data: VisitData): VisitResult {
|
||||
val variable = makeTempVar(expression.type)
|
||||
val varDeclaration = JsIrBuilder.buildVar(variable)
|
||||
|
||||
val blockStatements = expression.statements
|
||||
val lastStatement: IrStatement? = blockStatements.lastOrNull()
|
||||
|
||||
val body = when (expression) {
|
||||
is IrBlock -> IrBlockImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
context.builtIns.unitType,
|
||||
expression.origin
|
||||
)
|
||||
is IrComposite -> IrCompositeImpl(
|
||||
expression.startOffset,
|
||||
expression.endOffset,
|
||||
context.builtIns.unitType,
|
||||
expression.origin
|
||||
)
|
||||
else -> error("Unsupported block type")
|
||||
}
|
||||
|
||||
val collectingList = body.statements
|
||||
|
||||
for (i in 0 until blockStatements.size - 1) {
|
||||
val statement = blockStatements[i]
|
||||
val tempResult = statement.accept(statementVisitor, data)
|
||||
collectingList += tempResult.runIfChangedOrDefault(listOf(statement)) { statements }
|
||||
}
|
||||
|
||||
if (lastStatement != null) {
|
||||
val result = lastStatement.accept(expressionVisitor, data).runIfChangedOrDefault(lastStatement as IrExpression) {
|
||||
collectingList += statements
|
||||
resultValue
|
||||
}
|
||||
collectingList += JsIrBuilder.buildSetVariable(variable, result)
|
||||
return DecomposedResult(mutableListOf(varDeclaration, body), JsIrBuilder.buildGetValue(variable))
|
||||
} else {
|
||||
// do not allow variable to be uninitialized
|
||||
return DecomposedResult(mutableListOf(), unitValue)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitGetClass(expression: IrGetClass, data: VisitData): VisitResult {
|
||||
val res = super.visitGetClass(expression, data)
|
||||
|
||||
return res.runIfChanged { DecomposedResult(statements, JsIrBuilder.buildGetClass(resultValue, expression.type)) }
|
||||
}
|
||||
|
||||
override fun visitGetField(expression: IrGetField, data: VisitData): VisitResult {
|
||||
val res = super.visitGetField(expression, data)
|
||||
|
||||
return res.runIfChanged {
|
||||
DecomposedResult(statements, JsIrBuilder.buildGetField(expression.symbol, resultValue, expression.superQualifierSymbol))
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareArgument(arg: IrExpression, needWrap: Boolean, statements: MutableList<IrStatement>): IrExpression {
|
||||
return if (needWrap) {
|
||||
val wrapVar = makeTempVar(arg.type)
|
||||
statements += JsIrBuilder.buildVar(wrapVar, arg)
|
||||
JsIrBuilder.buildGetValue(wrapVar)
|
||||
} else arg
|
||||
}
|
||||
|
||||
private fun mapArguments(
|
||||
argResults: List<Pair<IrExpression?, VisitResult?>>,
|
||||
toDecompose: Int,
|
||||
newStatements: MutableList<IrStatement>
|
||||
): List<IrExpression?> {
|
||||
var decomposed = 0
|
||||
return argResults.map { (original, result) ->
|
||||
val needWrap = decomposed < toDecompose
|
||||
original?.let {
|
||||
val evaluated = result!!.runIfChangedOrDefault(it) {
|
||||
newStatements += statements
|
||||
decomposed++
|
||||
resultValue
|
||||
}
|
||||
prepareArgument(evaluated, needWrap, newStatements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The point here is to keep original evaluation order so (there is the same story for StringConcat)
|
||||
// d.foo(p1, p2, block {}, p4, block {}, p6, p7)
|
||||
//
|
||||
// is transformed into
|
||||
//
|
||||
// var d_tmp = d
|
||||
// var p1_tmp = p1
|
||||
// var p2_tmp = p2
|
||||
// var p3_tmp = block {}
|
||||
// var p4_tmp = p4
|
||||
// var p5_tmp = block {}
|
||||
// d_tmp.foo(p1_tmp, p2_tmp, p3_tmp, p4_tmp, p5_tmp, p6, p7)
|
||||
override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: VisitData): VisitResult {
|
||||
var argsCounter = 0
|
||||
|
||||
val dispatchResult = expression.dispatchReceiver?.accept(this, data)?.applyIfChanged { argsCounter++ }
|
||||
val extensionResult = expression.extensionReceiver?.accept(this, data)?.applyIfChanged { argsCounter++ }
|
||||
|
||||
val argumentResults = mutableListOf<Pair<IrExpression?, VisitResult?>>().also {
|
||||
for (i in 0 until expression.valueArgumentsCount) {
|
||||
val arg = expression.getValueArgument(i)
|
||||
val result = arg?.accept(this, data)?.applyIfChanged { argsCounter++ }
|
||||
it += Pair(arg, result)
|
||||
}
|
||||
}
|
||||
|
||||
if (argsCounter == 0) return KeptResult
|
||||
|
||||
val newStatements = mutableListOf<IrStatement>()
|
||||
|
||||
val needWrapDR = 0 != argsCounter
|
||||
val newDispatchReceiverValue = dispatchResult?.runIfChangedOrDefault(expression.dispatchReceiver) {
|
||||
newStatements += statements
|
||||
argsCounter--
|
||||
resultValue
|
||||
}
|
||||
val newDispatchReceiver = newDispatchReceiverValue?.let { prepareArgument(it, needWrapDR, newStatements) }
|
||||
|
||||
val needWrapER = 0 != argsCounter
|
||||
val newExtensionReceiverValue = extensionResult?.runIfChangedOrDefault(expression.extensionReceiver) {
|
||||
newStatements += statements
|
||||
argsCounter--
|
||||
resultValue
|
||||
}
|
||||
val newExtensionReceiver = newExtensionReceiverValue?.let { prepareArgument(it, needWrapER, newStatements) }
|
||||
|
||||
val newArguments = mapArguments(argumentResults, argsCounter, newStatements)
|
||||
|
||||
expression.dispatchReceiver = newDispatchReceiver
|
||||
expression.extensionReceiver = newExtensionReceiver
|
||||
|
||||
newArguments.forEachIndexed { i, v -> expression.putValueArgument(i, v) }
|
||||
|
||||
val resultVar = makeTempVar(expression.type)
|
||||
|
||||
// TODO: get rid of temporary variable
|
||||
newStatements += JsIrBuilder.buildVar(resultVar, expression)
|
||||
|
||||
return DecomposedResult(newStatements, JsIrBuilder.buildGetValue(resultVar))
|
||||
}
|
||||
|
||||
override fun visitWhen(expression: IrWhen, data: VisitData): VisitResult {
|
||||
val collectiveVar = makeTempVar(expression.type)
|
||||
val varDeclaration = JsIrBuilder.buildVar(collectiveVar)
|
||||
val newWhen = processWhen(expression, data, this, this) { visitResult, original ->
|
||||
val resultList = mutableListOf<IrStatement>()
|
||||
val newResult = visitResult.runIfChangedOrDefault(original as IrExpression) {
|
||||
resultList += statements
|
||||
resultValue
|
||||
}
|
||||
resultList.apply { add(JsIrBuilder.buildSetVariable(collectiveVar, newResult)) }
|
||||
}
|
||||
|
||||
if (newWhen != expression) {
|
||||
return DecomposedResult(mutableListOf(varDeclaration, newWhen), JsIrBuilder.buildGetValue(collectiveVar))
|
||||
}
|
||||
return KeptResult
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: VisitData): VisitResult {
|
||||
var decomposed = 0
|
||||
|
||||
val arguments = expression.arguments.map {
|
||||
Pair(it, it.accept(this, data).applyIfChanged { decomposed++ })
|
||||
}
|
||||
|
||||
if (decomposed == 0) return KeptResult
|
||||
|
||||
val newStatements = mutableListOf<IrStatement>()
|
||||
val newArguments = mapArguments(arguments, decomposed, newStatements)
|
||||
val newExpression =
|
||||
IrStringConcatenationImpl(expression.startOffset, expression.endOffset, expression.type, newArguments.map { it!! })
|
||||
|
||||
// Unlike to IrCall string concatenation has no side effects itself so it is possible to pass it as return value
|
||||
return DecomposedResult(newStatements, newExpression)
|
||||
}
|
||||
|
||||
private fun <T : IrStatement> transformTermination(
|
||||
value: IrExpression,
|
||||
instantiater: (value: IrExpression) -> T,
|
||||
data: VisitData
|
||||
): TerminatedResult {
|
||||
val valueResult = value.accept(this, data)
|
||||
val newStatements = mutableListOf<IrStatement>()
|
||||
|
||||
val returnValue = valueResult.runIfChangedOrDefault(value) {
|
||||
newStatements += statements
|
||||
resultValue
|
||||
}
|
||||
|
||||
newStatements += instantiater(returnValue)
|
||||
|
||||
return TerminatedResult(newStatements, JsIrBuilder.buildCall(unreachableFunction))
|
||||
}
|
||||
|
||||
|
||||
override fun visitReturn(expression: IrReturn, data: VisitData) = transformTermination(
|
||||
expression.value,
|
||||
{ v -> IrReturnImpl(expression.startOffset, expression.endOffset, expression.type, expression.returnTargetSymbol, v) },
|
||||
data
|
||||
)
|
||||
|
||||
override fun visitThrow(expression: IrThrow, data: VisitData) = transformTermination(
|
||||
expression.value,
|
||||
{ v -> IrThrowImpl(expression.startOffset, expression.endOffset, expression.type, v) },
|
||||
data
|
||||
)
|
||||
|
||||
override fun visitBreakContinue(jump: IrBreakContinue, data: VisitData): VisitResult {
|
||||
return DecomposedResult(jump, JsIrBuilder.buildCall(unreachableFunction))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun makeTempVar(type: KotlinType) =
|
||||
JsSymbolBuilder.buildTempVar(function.symbol, type, "tmp\$dcms\$${tmpVarCounter++}", true)
|
||||
|
||||
fun makeLoopLabel() = "\$l\$${tmpVarCounter++}"
|
||||
|
||||
private fun processWhen(
|
||||
expression: IrWhen,
|
||||
data: VisitData,
|
||||
expressionVisitor: DecomposerVisitor,
|
||||
statementVisitor: DecomposerVisitor,
|
||||
bodyBuilder: (VisitResult, IrStatement) -> List<IrStatement>
|
||||
): IrStatement {
|
||||
var needNewConds = false
|
||||
var needNewBodies = false
|
||||
|
||||
val branches = expression.branches.map {
|
||||
val conditionResult = it.condition.accept(expressionVisitor, data).applyIfChanged { needNewConds = true }
|
||||
val bodyResult = it.result.accept(statementVisitor, data).applyIfChanged { needNewBodies = true }
|
||||
Pair(conditionResult, bodyResult)
|
||||
}
|
||||
|
||||
// keep it as is
|
||||
if (!(needNewConds || needNewBodies)) return expression
|
||||
|
||||
val unitType = context.builtIns.unitType
|
||||
|
||||
if (needNewConds) {
|
||||
|
||||
// [val x = ] when {
|
||||
// c1_block {} -> b1_block {}
|
||||
// ....
|
||||
// cn_block {} -> bn_block {}
|
||||
// else -> else_block {}
|
||||
// }
|
||||
//
|
||||
// transformed into if-else chain
|
||||
// [var x_tmp]
|
||||
// val c1 = c1_block {}
|
||||
// if (c1) {
|
||||
// [x_tmp =] b1_block {}
|
||||
// } else {
|
||||
// val c2 = c2_block {}
|
||||
// if (c2) {
|
||||
// [x_tmp =] b2_block{}
|
||||
// } else {
|
||||
// ...
|
||||
// else {
|
||||
// [x_tmp =] else_block {}
|
||||
// }
|
||||
// }
|
||||
// [val x = x_tmp]
|
||||
//
|
||||
val block = IrBlockImpl(expression.startOffset, expression.endOffset, unitType, expression.origin)
|
||||
|
||||
branches.foldIndexed(block) { i, appendBlock, (condResult, bodyResult) ->
|
||||
val originalBranch = expression.branches[i]
|
||||
val originalCondition = originalBranch.condition
|
||||
val originalResult = originalBranch.result
|
||||
|
||||
val irCondition = condResult.runIfChangedOrDefault(originalCondition) {
|
||||
appendBlock.statements += statements
|
||||
resultValue
|
||||
}
|
||||
|
||||
val thenBlock = IrBlockImpl(originalResult.startOffset, originalResult.endOffset, unitType).apply {
|
||||
statements += bodyBuilder(bodyResult, originalResult)
|
||||
}
|
||||
|
||||
JsIrBuilder.buildBlock(unitType).also {
|
||||
val elseBlock = if (originalBranch is IrElseBranch) null else it
|
||||
|
||||
val ifElseNode = IrIfThenElseImpl(
|
||||
originalBranch.startOffset,
|
||||
originalBranch.endOffset,
|
||||
unitType,
|
||||
irCondition,
|
||||
thenBlock,
|
||||
elseBlock,
|
||||
expression.origin
|
||||
)
|
||||
appendBlock.statements += ifElseNode
|
||||
}
|
||||
}
|
||||
|
||||
return block
|
||||
}
|
||||
|
||||
// [val x = ] when {
|
||||
// c1 -> b1_block {}
|
||||
// ....
|
||||
// cn -> bn_block {}
|
||||
// else -> else_block {}
|
||||
// }
|
||||
//
|
||||
// transformed into if-else chain
|
||||
//
|
||||
// [var x_tmp]
|
||||
// when {
|
||||
// c1 -> [x_tmp =] b1_block {}
|
||||
// ...
|
||||
// cn -> [x_tmp =] bn_block {}
|
||||
// else -> [x_tmp =] else_block {}
|
||||
// }
|
||||
// [val x = x_tmp]
|
||||
|
||||
val newWhen = IrWhenImpl(expression.startOffset, expression.endOffset, unitType, expression.origin)
|
||||
|
||||
branches.forEachIndexed { i, (_, bodyResult) ->
|
||||
val originalBranch = expression.branches[i]
|
||||
val condition = originalBranch.condition
|
||||
val originalResult = originalBranch.result
|
||||
|
||||
val body = IrBlockImpl(originalResult.startOffset, originalResult.endOffset, unitType).apply {
|
||||
statements += bodyBuilder(bodyResult, originalResult)
|
||||
}
|
||||
|
||||
newWhen.branches += when (originalBranch) {
|
||||
is IrElseBranch -> IrElseBranchImpl(originalBranch.startOffset, originalBranch.endOffset, condition, body)
|
||||
else /* IrBranch */ -> IrBranchImpl(originalBranch.startOffset, originalBranch.endOffset, condition, body)
|
||||
}
|
||||
}
|
||||
|
||||
return newWhen
|
||||
}
|
||||
}
|
||||
-7
@@ -19,13 +19,6 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
|
||||
|
||||
private val neutralExpression: JsExpression = JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
|
||||
|
||||
override fun visitContainerExpression(expression: IrContainerExpression, context: JsGenerationContext): JsExpression =
|
||||
expression.statements.map { it.accept(this, context) }.fold(neutralExpression) { left, right ->
|
||||
if (left != neutralExpression) JsBinaryOperation(JsBinaryOperator.COMMA, left, right) else right
|
||||
}
|
||||
|
||||
override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression =
|
||||
body.expression.accept(this, context)
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ object Namer {
|
||||
val CALL_FUNCTION = "call"
|
||||
val APPLY_FUNCTION = "apply"
|
||||
|
||||
val UNREACHABLE_NAME = "\$unreachable"
|
||||
|
||||
val OUTER_FIELD_NAME = "\$outer"
|
||||
|
||||
val DELEGATE = "\$delegate"
|
||||
|
||||
Reference in New Issue
Block a user