[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(
|
class JsIrBackendContext(
|
||||||
val module: ModuleDescriptor,
|
val module: ModuleDescriptor,
|
||||||
override val irBuiltIns: IrBuiltIns,
|
override val irBuiltIns: IrBuiltIns,
|
||||||
irModuleFragment: IrModuleFragment,
|
val symbolTable: SymbolTable,
|
||||||
symbolTable: SymbolTable
|
irModuleFragment: IrModuleFragment
|
||||||
) : CommonBackendContext {
|
) : CommonBackendContext {
|
||||||
|
|
||||||
val intrinsics = JsIntrinsics(module, irBuiltIns, symbolTable)
|
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.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
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.backend.js.transformers.irToJs.IrModuleToJsTransformer
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
|
||||||
@@ -38,8 +41,8 @@ fun compile(
|
|||||||
val context = JsIrBackendContext(
|
val context = JsIrBackendContext(
|
||||||
analysisResult.moduleDescriptor,
|
analysisResult.moduleDescriptor,
|
||||||
psi2IrContext.irBuiltIns,
|
psi2IrContext.irBuiltIns,
|
||||||
moduleFragment,
|
psi2IrContext.symbolTable,
|
||||||
psi2IrContext.symbolTable
|
moduleFragment
|
||||||
)
|
)
|
||||||
|
|
||||||
ExternalDependenciesGenerator(psi2IrContext.symbolTable, psi2IrContext.irBuiltIns).generateUnboundSymbolsAsDependencies(moduleFragment)
|
ExternalDependenciesGenerator(psi2IrContext.symbolTable, psi2IrContext.irBuiltIns).generateUnboundSymbolsAsDependencies(moduleFragment)
|
||||||
@@ -62,6 +65,7 @@ fun JsIrBackendContext.lower(file: IrFile) {
|
|||||||
InnerClassConstructorCallsLowering(this).runOnFilePostfix(file)
|
InnerClassConstructorCallsLowering(this).runOnFilePostfix(file)
|
||||||
PropertiesLowering().lower(file)
|
PropertiesLowering().lower(file)
|
||||||
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file)
|
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(file)
|
||||||
|
BlockDecomposerLowering(this).runOnFilePostfix(file)
|
||||||
SecondaryCtorLowering(this).runOnFilePostfix(file)
|
SecondaryCtorLowering(this).runOnFilePostfix(file)
|
||||||
IntrinsicifyCallsLowering(this).lower(file)
|
IntrinsicifyCallsLowering(this).lower(file)
|
||||||
FunctionReferenceLowering(this).lower(file)
|
FunctionReferenceLowering(this).lower(file)
|
||||||
|
|||||||
+12
@@ -5,17 +5,20 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.descriptors
|
package org.jetbrains.kotlin.ir.backend.js.descriptors
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.FunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
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.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
@@ -85,6 +88,15 @@ object JsSymbolBuilder {
|
|||||||
SourceElement.NO_SOURCE
|
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.IrValueParameterImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
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.IrStatementOriginImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.*
|
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 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) =
|
fun buildFunctionReference(type: KotlinType, symbol: IrFunctionSymbol) =
|
||||||
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor)
|
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> {
|
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 =
|
override fun visitExpressionBody(body: IrExpressionBody, context: JsGenerationContext): JsExpression =
|
||||||
body.expression.accept(this, context)
|
body.expression.accept(this, context)
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ object Namer {
|
|||||||
val CALL_FUNCTION = "call"
|
val CALL_FUNCTION = "call"
|
||||||
val APPLY_FUNCTION = "apply"
|
val APPLY_FUNCTION = "apply"
|
||||||
|
|
||||||
|
val UNREACHABLE_NAME = "\$unreachable"
|
||||||
|
|
||||||
val OUTER_FIELD_NAME = "\$outer"
|
val OUTER_FIELD_NAME = "\$outer"
|
||||||
|
|
||||||
val DELEGATE = "\$delegate"
|
val DELEGATE = "\$delegate"
|
||||||
|
|||||||
Reference in New Issue
Block a user