[JS IR BE] Refact MultipleCatchLowering

- catch dynamic type instead of common super type over all catch blocks
This commit is contained in:
Roman Artemev
2019-07-09 11:25:45 +03:00
committed by romanart
parent 1605dc3251
commit 131158df65
@@ -17,12 +17,8 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCatchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrElseBranchImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
/**
@@ -38,21 +34,20 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
* is converted into
*
* try {}
* catch (ex: Ex = LCA(Ex1, Ex2, Ex3)) {
* when (ex) {
* ex is Ex1 -> catch1((Ex1)ex)
* ex is Ex2 -> catch2((Ex2)ex)
* ex is Ex3 -> catch3((Ex3)ex)
* else throw ex [ | catch_dynamic(ex) ]
* catch ($p: dynamic) {
* when ($p) {
* ex is Ex1 -> catch1((Ex1)$p)
* ex is Ex2 -> catch2((Ex2)$p)
* ex is Ex3 -> catch3((Ex3)$p)
* else throw $p [ | catch_dynamic($p) ]
* }
* }
* finally {}
*/
class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPass {
val litTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
val unitType = context.irBuiltIns.unitType
val nothingType = context.irBuiltIns.nothingType
class MultipleCatchesLowering(private val context: JsIrBackendContext) : FileLoweringPass {
private val litTrue get() = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val nothingType = context.irBuiltIns.nothingType
override fun lower(irFile: IrFile) {
irFile.transformChildren(object : IrElementTransformer<IrDeclarationParent> {
@@ -65,23 +60,21 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
override fun visitTry(aTry: IrTry, data: IrDeclarationParent): IrExpression {
aTry.transformChildren(this, data)
if (aTry.catches.isEmpty()) return aTry.apply { assert(finallyExpression != null) }
if (aTry.catches.isEmpty()) return aTry.also { assert(it.finallyExpression != null) }
val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type })
val pendingExceptionDeclaration = JsIrBuilder.buildVar(commonType, data, "\$p")
val pendingExceptionDeclaration = JsIrBuilder.buildVar(context.dynamicType, data, "\$p")
val pendingException = { JsIrBuilder.buildGetValue(pendingExceptionDeclaration.symbol) }
val branches = mutableListOf<IrBranch>()
var isCaughtDynamic = false
for (catch in aTry.catches) {
assert(!catch.catchParameter.isVar) { "caught exception parameter has to be immutable" }
val type = catch.catchParameter.type
val typeSymbol = type.classifierOrNull
val castedPendingException = {
if (type !is IrDynamicType)
buildImplicitCast(pendingException(), type, typeSymbol!!)
buildImplicitCast(pendingException(), type)
else
pendingException()
}
@@ -96,15 +89,15 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
if (type is IrDynamicType) {
branches += IrElseBranchImpl(catch.startOffset, catch.endOffset, litTrue, catchBody)
isCaughtDynamic = true
break
} else {
val typeCheck = buildIsCheck(pendingException(), type, typeSymbol!!)
val typeCheck = buildIsCheck(pendingException(), type)
branches += IrBranchImpl(catch.startOffset, catch.endOffset, typeCheck, catchBody)
}
}
if (commonType !is IrDynamicType) {
if (!isCaughtDynamic) {
val throwStatement = JsIrBuilder.buildThrow(nothingType, pendingException())
branches += IrElseBranchImpl(litTrue, JsIrBuilder.buildBlock(nothingType, listOf(throwStatement)))
}
@@ -118,28 +111,11 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
return aTry.run { IrTryImpl(startOffset, endOffset, type, tryResult, listOf(newCatch), finallyExpression) }
}
private fun buildIsCheck(value: IrExpression, toType: IrType, toTypeSymbol: IrClassifierSymbol) =
JsIrBuilder.buildTypeOperator(context.irBuiltIns.booleanType, IrTypeOperator.INSTANCEOF, value, toType, toTypeSymbol)
private fun buildIsCheck(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(context.irBuiltIns.booleanType, IrTypeOperator.INSTANCEOF, value, toType)
private fun buildImplicitCast(value: IrExpression, toType: IrType, toTypeSymbol: IrClassifierSymbol) =
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType, toTypeSymbol)
private fun mergeTypes(types: List<IrType>): IrType {
return types.firstOrNull { it is IrDynamicType } ?: run {
val superClassifier =
types.map { (it as IrSimpleType).classifier }.commonSuperclass().also {
assert(it.isSubtypeOfClass(context.irBuiltIns.throwableClass))
}
val typeArguments = if (superClassifier is IrClassSymbol) {
superClassifier.owner.typeParameters.map { IrStarProjectionImpl }
} else emptyList<IrTypeArgument>()
return IrSimpleTypeImpl(superClassifier, false, typeArguments, emptyList())
}
}
private fun buildImplicitCast(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType)
}, irFile)
}