[JS IR BE] Support coroutines

* Move FinallyBlockLowering to common part
* Fix catching of dynamic exception
* Fix bridges for suspend functions
* Disable explicit cast to Unit
* Run lowering per module
* Update some test data
This commit is contained in:
Roman Artemev
2018-06-18 20:01:39 +03:00
committed by romanart
parent 37fadb8ee5
commit c62e4b4fcf
127 changed files with 3539 additions and 256 deletions
@@ -44,7 +44,7 @@ abstract class Symbols<out T : CommonBackendContext>(val context: T, private val
return initializer() return initializer()
} }
val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) } // val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) }
//abstract val areEqualByValue: List<IrFunctionSymbol> //abstract val areEqualByValue: List<IrFunctionSymbol>
@@ -0,0 +1,318 @@
/*
* 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.backend.common.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrReturnTargetSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrReturnableBlockSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
class FinallyBlocksLowering(val context: CommonBackendContext, private val throwableType: IrType): FileLoweringPass, IrElementTransformerVoidWithContext() {
private interface HighLevelJump {
fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression): IrExpression
}
private data class Return(val target: IrReturnTargetSymbol): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
=
IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, target, value)
}
private data class Break(val loop: IrLoop): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
= IrCompositeImpl(
startOffset, endOffset, context.irBuiltIns.unitType, null,
statements = listOf(
value,
IrBreakImpl(startOffset, endOffset, context.irBuiltIns.nothingType, loop)
)
)
}
private data class Continue(val loop: IrLoop): HighLevelJump {
override fun toIr(context: CommonBackendContext, startOffset: Int, endOffset: Int, value: IrExpression)
= IrCompositeImpl(
startOffset, endOffset, context.irBuiltIns.unitType, null,
statements = listOf(
value,
IrContinueImpl(startOffset, endOffset, context.irBuiltIns.nothingType, loop)
)
)
}
private abstract class Scope
private class ReturnableScope(val descriptor: CallableDescriptor): Scope()
private class LoopScope(val loop: IrLoop): Scope()
private class TryScope(var expression: IrExpression,
val finallyExpression: IrExpression,
val irBuilder: IrBuilderWithScope
): Scope() {
val jumps = mutableMapOf<HighLevelJump, IrReturnTargetSymbol>()
}
private val scopeStack = mutableListOf<Scope>()
private inline fun <S: Scope, R> using(scope: S, block: (S) -> R): R {
scopeStack.push(scope)
try {
return block(scope)
} finally {
scopeStack.pop()
}
}
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this)
}
override fun visitFunctionNew(declaration: IrFunction): IrStatement {
using(ReturnableScope(declaration.descriptor)) {
return super.visitFunctionNew(declaration)
}
}
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
if (expression !is IrReturnableBlockImpl)
return super.visitContainerExpression(expression)
using(ReturnableScope(expression.descriptor)) {
return super.visitContainerExpression(expression)
}
}
override fun visitLoop(loop: IrLoop): IrExpression {
using(LoopScope(loop)) {
return super.visitLoop(loop)
}
}
override fun visitBreak(jump: IrBreak): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Break(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitContinue(jump: IrContinue): IrExpression {
val startOffset = jump.startOffset
val endOffset = jump.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
return performHighLevelJump(
targetScopePredicate = { it is LoopScope && it.loop == jump.loop },
jump = Continue(jump.loop),
startOffset = startOffset,
endOffset = endOffset,
value = irBuilder.irGetObject(context.ir.symbols.unit)
) ?: jump
}
override fun visitReturn(expression: IrReturn): IrExpression {
expression.transformChildrenVoid(this)
return performHighLevelJump(
targetScopePredicate = { it is ReturnableScope && it.descriptor == expression.returnTarget },
jump = Return(expression.returnTargetSymbol),
startOffset = expression.startOffset,
endOffset = expression.endOffset,
value = expression.value
) ?: expression
}
private fun performHighLevelJump(targetScopePredicate: (Scope) -> Boolean,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression
): IrExpression? {
val tryScopes = scopeStack.reversed()
.takeWhile { !targetScopePredicate(it) }
.filterIsInstance<TryScope>()
.toList()
if (tryScopes.isEmpty())
return null
return performHighLevelJump(tryScopes, 0, jump, startOffset, endOffset, value)
}
private val IrReturnTarget.returnType: IrType
get() = when (this) {
is IrConstructor -> context.irBuiltIns.unitType
is IrFunction -> returnType
is IrReturnableBlock -> type
else -> error("Unknown ReturnTarget: $this")
}
private fun performHighLevelJump(tryScopes: List<TryScope>,
index: Int,
jump: HighLevelJump,
startOffset: Int,
endOffset: Int,
value: IrExpression
): IrExpression {
if (index == tryScopes.size)
return jump.toIr(context, startOffset, endOffset, value)
val currentTryScope = tryScopes[index]
currentTryScope.jumps.getOrPut(jump) {
val type = (jump as? Return)?.target?.owner?.returnType ?: value.type
val symbol = getIrReturnableBlockSymbol(jump.toString(), type)
with(currentTryScope) {
irBuilder.run {
val inlinedFinally = irInlineFinally(symbol, type, expression, finallyExpression)
expression = performHighLevelJump(
tryScopes = tryScopes,
index = index + 1,
jump = jump,
startOffset = startOffset,
endOffset = endOffset,
value = inlinedFinally)
}
}
symbol
}.let {
return IrReturnImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.nothingType,
returnTargetSymbol = it,
value = value
)
}
}
override fun visitTry(aTry: IrTry): IrExpression {
val finallyExpression = aTry.finallyExpression
?: return super.visitTry(aTry)
val startOffset = aTry.startOffset
val endOffset = aTry.endOffset
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
val transformer = this
irBuilder.run {
val transformedTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.unitType
)
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
val parameter = IrTemporaryVariableDescriptorImpl(
containingDeclaration = currentScope!!.scope.scopeOwner,
name = Name.identifier("t"),
outType = throwableType.toKotlinType()
)
val catchParameter = IrVariableImpl(
startOffset, endOffset, IrDeclarationOrigin.CATCH_PARAMETER, parameter,
throwableType
)
val syntheticTry = IrTryImpl(
startOffset = startOffset,
endOffset = endOffset,
type = context.irBuiltIns.unitType,
tryResult = transformedTry,
catches = listOf(
irCatch(catchParameter).apply {
result = irComposite {
+finallyExpression.copy()
+irThrow(irGet(catchParameter))
}
}),
finallyExpression = null
)
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
val fallThroughType = aTry.type
val fallThroughSymbol = getIrReturnableBlockSymbol("fallThrough", fallThroughType)
val transformedResult = aTry.tryResult.transform(transformer, null)
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
for (aCatch in aTry.catches) {
val transformedCatch = aCatch.transform(transformer, null)
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
transformedTry.catches.add(transformedCatch)
}
return irInlineFinally(fallThroughSymbol, fallThroughType, it.expression, it.finallyExpression)
}
}
}
private fun IrBuilderWithScope.irInlineFinally(symbol: IrReturnableBlockSymbol, type: IrType,
value: IrExpression,
finallyExpression: IrExpression
): IrExpression {
val returnType = symbol.descriptor.returnType!!
return when {
returnType.isUnit() || returnType.isNothing() -> irBlock(value, null, type) {
+irReturnableBlock(symbol, type) {
+value
}
+finallyExpression.copy()
}
else -> irComposite(value, null, type) {
val tmp = irTemporary(irReturnableBlock(symbol, type) {
+irReturn(symbol, value)
})
+finallyExpression.copy()
+irGet(tmp)
}
}
}
private fun getFakeFunctionDescriptor(name: String, returnType: KotlinType) =
SimpleFunctionDescriptorImpl.create(
currentScope!!.scope.scopeOwner,
Annotations.EMPTY,
name.synthesizedName,
CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE
).apply {
initialize(null, null, emptyList(), emptyList(), returnType,
Modality.ABSTRACT,
Visibilities.PRIVATE
)
}
private fun getIrReturnableBlockSymbol(name: String, returnType: IrType): IrReturnableBlockSymbol =
IrReturnableBlockSymbolImpl(getFakeFunctionDescriptor(name, returnType.toKotlinType()))
private inline fun <reified T : IrElement> T.copy() = this.deepCopyWithVariables()
fun IrBuilderWithScope.irReturn(target: IrReturnTargetSymbol, value: IrExpression) =
IrReturnImpl(startOffset, endOffset, context.irBuiltIns.nothingType, target, value)
inline fun IrBuilderWithScope.irReturnableBlock(symbol: IrReturnableBlockSymbol, type: IrType, body: IrBlockBuilder.() -> Unit) =
IrReturnableBlockImpl(
startOffset, endOffset, type, symbol, null,
IrBlockBuilder(context, scope, startOffset, endOffset, null, type, true)
.block(body).statements
)
}
@@ -247,6 +247,31 @@ class InnerClassConstructorCallsLowering(val context: BackendContext) : BodyLowe
return newCall return newCall
} }
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
val callee = expression.symbol as? IrConstructorSymbol ?: return expression
val parent = callee.owner.parent as? IrClass ?: return expression
if (!parent.isInner) return expression
val newCallee = context.descriptorsFactory.getInnerClassConstructorWithOuterThisParameter(callee.owner)
val newReference = expression.run { IrFunctionReferenceImpl(startOffset, endOffset, type, newCallee, newCallee.descriptor, typeArgumentsCount, origin) }
newReference.let {
it.dispatchReceiver = expression.dispatchReceiver
it.extensionReceiver = expression.extensionReceiver
for (t in 0 until expression.typeArgumentsCount) {
it.putTypeArgument(t, expression.getTypeArgument(t))
}
for (v in 0 until expression.valueArgumentsCount) {
it.putValueArgument(v, expression.getValueArgument(v))
}
}
return newReference
}
// TODO callable references? // TODO callable references?
}) })
} }
@@ -88,6 +88,13 @@ inline fun IrGeneratorWithScope.irBlock(
) = ) =
this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body) this.irBlock(expression.startOffset, expression.endOffset, origin, resultType, body)
inline fun IrGeneratorWithScope.irComposite(
expression: IrExpression, origin: IrStatementOrigin? = null,
resultType: IrType? = expression.type,
body: IrBlockBuilder.() -> Unit
) =
this.irComposite(expression.startOffset, expression.endOffset, origin, resultType, body)
inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) = inline fun IrGeneratorWithScope.irBlockBody(irElement: IrElement, body: IrBlockBodyBuilder.() -> Unit) =
this.irBlockBody(irElement.startOffset, irElement.endOffset, body) this.irBlockBody(irElement.startOffset, irElement.endOffset, body)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.common.utils
import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
@@ -36,3 +37,5 @@ fun List<IrType>.commonSupertype() = CommonSupertypes.commonSupertype(map(IrType
fun IrType.isSubtypeOf(superType: IrType) = toKotlinType().isSubtypeOf(superType.toKotlinType()) fun IrType.isSubtypeOf(superType: IrType) = toKotlinType().isSubtypeOf(superType.toKotlinType())
fun IrType.isSubtypeOfClass(superClass: IrClassSymbol) = DescriptorUtils.isSubtypeOfClass(toKotlinType(), superClass.descriptor) fun IrType.isSubtypeOfClass(superClass: IrClassSymbol) = DescriptorUtils.isSubtypeOfClass(toKotlinType(), superClass.descriptor)
fun IrType.isBuiltinFunctionalTypeOrSubtype() = toKotlinType().isBuiltinFunctionalTypeOrSubtype
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -31,3 +32,6 @@ fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? =
fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? =
this.getPropertyDeclaration(name)?.setter?.symbol this.getPropertyDeclaration(name)?.setter?.symbol
fun IrClassSymbol.getPropertyField(name: String): IrFieldSymbol? =
this.getPropertyDeclaration(name)?.backingField?.symbol
@@ -99,7 +99,6 @@ class JsIntrinsics(
val jsInstanceOf = binOpBool("jsInstanceOf") val jsInstanceOf = binOpBool("jsInstanceOf")
val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string) val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string)
// Number conversions: // Number conversions:
val jsNumberToByte = getInternalFunction("numberToByte") val jsNumberToByte = getInternalFunction("numberToByte")
@@ -132,6 +131,14 @@ class JsIntrinsics(
val jsCompareTo = getInternalFunction("compareTo") val jsCompareTo = getInternalFunction("compareTo")
val jsEquals = getInternalFunction("equals") val jsEquals = getInternalFunction("equals")
// Coroutines
val jsCoroutineContext = context.symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
val jsGetContinuation = context.run {
val f = getInternalFunctions("getContinuation")
symbolTable.referenceSimpleFunction(f.single())
}
val jsNumberRangeToNumber = getInternalFunction("numberRangeToNumber") val jsNumberRangeToNumber = getInternalFunction("numberRangeToNumber")
val jsNumberRangeToLong = getInternalFunction("numberRangeToLong") val jsNumberRangeToLong = getInternalFunction("numberRangeToLong")
@@ -13,20 +13,28 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.js.JsDescriptorsFactory import org.jetbrains.kotlin.backend.js.JsDescriptorsFactory
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ModuleIndex import org.jetbrains.kotlin.ir.backend.js.lower.inline.ModuleIndex
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol
import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.impl.IrDynamicTypeImpl
import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.createDynamicType
class JsIrBackendContext( class JsIrBackendContext(
val module: ModuleDescriptor, val module: ModuleDescriptor,
@@ -48,28 +56,75 @@ class JsIrBackendContext(
private val internalPackageName = FqName("kotlin.js") private val internalPackageName = FqName("kotlin.js")
private val internalPackage = module.getPackage(internalPackageName) private val internalPackage = module.getPackage(internalPackageName)
// TODO: replace it with appropriate package name once we migrate to 1.3 coroutines
private val coroutinePackageNameSrting = "kotlin.coroutines.experimental"
private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
private val CONTINUATION_NAME = Name.identifier("Continuation")
// TODO: what is more clear way reference this getter?
private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("<get-context>")
private val coroutinePackageName = FqName(coroutinePackageNameSrting)
private val coroutineIntrinsicsPackageName = coroutinePackageName.child(INTRINSICS_PACKAGE_NAME)
private val coroutinePackage = module.getPackage(coroutinePackageName)
private val coroutineIntrinsicsPackage = module.getPackage(coroutineIntrinsicsPackageName)
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunctionSymbol>()
val coroutineGetContext: IrFunctionSymbol
get() {
val continuation = symbolTable.referenceClass(
coroutinePackage.memberScope.getContributedClassifier(
CONTINUATION_NAME,
NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
val contextGetter = continuation.owner.declarations.single { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME } as IrFunction
return contextGetter.symbol
}
val coroutineContextProperty: PropertyDescriptor
get() {
val vars = internalPackage.memberScope.getContributedVariables(
COROUTINE_CONTEXT_NAME,
NoLookupLocation.FROM_BACKEND
)
return vars.single()
}
val intrinsics = JsIntrinsics(module, irBuiltIns, this) val intrinsics = JsIntrinsics(module, irBuiltIns, this)
private val operatorMap = referenceOperators() private val operatorMap = referenceOperators()
data class SecondaryCtorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol) val functions = (0..22).map { symbolTable.referenceClass(builtIns.getFunction(it)) }
val secondaryConstructorsMap = mutableMapOf<IrConstructorSymbol, SecondaryCtorPair>() val kFunctions by lazy {
val enumEntryToGetInstanceFunction = mutableMapOf<IrEnumEntrySymbol, IrSimpleFunctionSymbol>() (0..22).map { symbolTable.referenceClass(reflectionTypes.getKFunction(it)) }
}
fun getOperatorByName(name: Name, type: IrType) = operatorMap[name]?.get(type.toKotlinType()) val suspendFunctions = (0..22).map { symbolTable.referenceClass(builtIns.getSuspendFunction(it)) }
val dynamicType = IrDynamicTypeImpl(createDynamicType(builtIns), emptyList(), Variance.INVARIANT)
val originalModuleIndex = ModuleIndex(irModuleFragment) val originalModuleIndex = ModuleIndex(irModuleFragment)
fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type)
override val ir = object : Ir<CommonBackendContext>(this, irModuleFragment) { override val ir = object : Ir<CommonBackendContext>(this, irModuleFragment) {
override val symbols = object : Symbols<CommonBackendContext>(this@JsIrBackendContext, symbolTable.lazyWrapper) { override val symbols = object : Symbols<CommonBackendContext>(this@JsIrBackendContext, symbolTable.lazyWrapper) {
override fun calc(initializer: () -> IrClassSymbol): IrClassSymbol { override fun calc(initializer: () -> IrClassSymbol): IrClassSymbol {
val v = lazy { initializer() }
return object : IrClassSymbol { return object : IrClassSymbol {
override val owner: IrClass get() = TODO("not implemented") override val owner: IrClass get() = v.value.owner
override val isBound: Boolean get() = TODO("not implemented") override val isBound: Boolean get() = v.value.isBound
override fun bind(owner: IrClass) = TODO("not implemented") override fun bind(owner: IrClass) = v.value.bind(owner)
override val descriptor: ClassDescriptor get() = TODO("not implemented") override val descriptor: ClassDescriptor get() = v.value.descriptor
} }
} }
@@ -97,10 +152,10 @@ class JsIrBackendContext(
get() = TODO("not implemented") get() = TODO("not implemented")
override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol> override val copyRangeTo: Map<ClassDescriptor, IrSimpleFunctionSymbol>
get() = TODO("not implemented") get() = TODO("not implemented")
override val coroutineImpl: IrClassSymbol override val coroutineImpl = symbolTable.referenceClass(getInternalClass(COROUTINE_IMPL_NAME.identifier))
get() = TODO("not implemented") override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
override val coroutineSuspendedGetter: IrSimpleFunctionSymbol coroutineIntrinsicsPackage.memberScope.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND).single().getter!!
get() = TODO("not implemented") )
} }
override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true
@@ -132,6 +187,8 @@ class JsIrBackendContext(
override fun getInternalFunctions(name: String) = findFunctions(internalPackage.memberScope, name) override fun getInternalFunctions(name: String) = findFunctions(internalPackage.memberScope, name)
fun getFunctions(fqName: FqName) = findFunctions(module.getPackage(fqName.parent()).memberScope, fqName.shortName())
override fun log(message: () -> String) { override fun log(message: () -> String) {
/*TODO*/ /*TODO*/
print(message()) print(message())
@@ -6,13 +6,18 @@
package org.jetbrains.kotlin.ir.backend.js package org.jetbrains.kotlin.ir.backend.js
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.FileLoweringPass
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.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.backend.js.lower.* import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.inline.* import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.SuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.FunctionInlining
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWithReifiedTypeParametersLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.replaceUnboundSymbols
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.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
@@ -59,9 +64,7 @@ fun compile(
context.performInlining(moduleFragment) context.performInlining(moduleFragment)
context.lower(moduleFragment.files) context.lower(moduleFragment)
val transformer = SecondaryCtorLowering.CallsiteRedirectionTransformer(context)
moduleFragment.files.forEach { it.accept(transformer, null) }
val program = moduleFragment.accept(IrModuleToJsTransformer(context), null) val program = moduleFragment.accept(IrModuleToJsTransformer(context), null)
@@ -80,30 +83,32 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
} }
} }
private fun JsIrBackendContext.lower(files: List<IrFile>) { private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment) {
LateinitLowering(this, true).lower(files) moduleFragment.files.forEach(LateinitLowering(this, true)::lower)
DefaultArgumentStubGenerator(this).runOnFilePostfix(files) moduleFragment.files.forEach(DefaultArgumentStubGenerator(this)::runOnFilePostfix)
DefaultParameterInjector(this).runOnFilePostfix(files) moduleFragment.files.forEach(DefaultParameterInjector(this)::runOnFilePostfix)
SharedVariablesLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(SharedVariablesLowering(this)::runOnFilePostfix)
EnumClassLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix)
EnumUsageLowering(this).lower(files) moduleFragment.files.forEach(EnumUsageLowering(this)::lower)
ReturnableBlockLowering(this).lower(files) moduleFragment.files.forEach(ReturnableBlockLowering(this)::lower)
LocalDeclarationsLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(LocalDeclarationsLowering(this)::runOnFilePostfix)
InnerClassesLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(InnerClassesLowering(this)::runOnFilePostfix)
InnerClassConstructorCallsLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(InnerClassConstructorCallsLowering(this)::runOnFilePostfix)
PropertiesLowering().lower(files) moduleFragment.files.forEach(SuspendFunctionsLowering(this)::lower)
InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(files) moduleFragment.files.forEach(PropertiesLowering()::lower)
MultipleCatchesLowering(this).lower(files) moduleFragment.files.forEach(InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false)::runOnFilePostfix)
BridgesConstruction(this).runOnFilePostfix(files) moduleFragment.files.forEach(MultipleCatchesLowering(this)::lower)
TypeOperatorLowering(this).lower(files) moduleFragment.files.forEach(BridgesConstruction(this)::runOnFilePostfix)
BlockDecomposerLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(TypeOperatorLowering(this)::lower)
SecondaryCtorLowering(this).runOnFilePostfix(files) moduleFragment.files.forEach(BlockDecomposerLowering(this)::runOnFilePostfix)
CallableReferenceLowering(this).lower(files) val sctor = SecondaryCtorLowering(this)
IntrinsicifyCallsLowering(this).lower(files) moduleFragment.files.forEach(sctor.getConstructorProcessorLowering())
moduleFragment.files.forEach(sctor.getConstructorRedirectorLowering())
val clble = CallableReferenceLowering(this)
moduleFragment.files.forEach(clble.getReferenceCollector())
moduleFragment.files.forEach(clble.getClosureBuilder())
moduleFragment.files.forEach(clble.getReferenceReplacer())
moduleFragment.files.forEach(IntrinsicifyCallsLowering(this)::lower)
} }
private fun FileLoweringPass.lower(files: List<IrFile>) = files.forEach { lower(it) } private fun FileLoweringPass.lower(files: List<IrFile>) = files.forEach { lower(it) }
private fun DeclarationContainerLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun BodyLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun FunctionLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
private fun ClassLoweringPass.runOnFilePostfix(files: List<IrFile>) = files.forEach { runOnFilePostfix(it) }
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.ir
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
class JsIrArithBuilder(val context: JsIrBackendContext) { class JsIrArithBuilder(val context: JsIrBackendContext) {
@@ -15,15 +16,15 @@ class JsIrArithBuilder(val context: JsIrBackendContext) {
val symbols = context.ir.symbols val symbols = context.ir.symbols
private fun buildBinaryOperator(name: Name, l: IrExpression, r: IrExpression): IrExpression { private fun buildBinaryOperator(name: Name, l: IrExpression, r: IrExpression): IrExpression {
val symbol = context.getOperatorByName(name, l.type)!! val symbol = context.getOperatorByName(name, l.type.toKotlinType())
return JsIrBuilder.buildCall(symbol).apply { return JsIrBuilder.buildCall(symbol!!).apply {
dispatchReceiver = l dispatchReceiver = l
putValueArgument(0, r) putValueArgument(0, r)
} }
} }
private fun buildUnaryOperator(name: Name, v: IrExpression): IrExpression { private fun buildUnaryOperator(name: Name, v: IrExpression): IrExpression {
val symbol = context.getOperatorByName(name, v.type)!! val symbol = context.getOperatorByName(name, v.type.toKotlinType())!!
return JsIrBuilder.buildCall(symbol).apply { dispatchReceiver = v } return JsIrBuilder.buildCall(symbol).apply { dispatchReceiver = v }
} }
@@ -5,16 +5,31 @@
package org.jetbrains.kotlin.ir.backend.js.ir package org.jetbrains.kotlin.ir.backend.js.ir
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
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.*
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.ir.util.createDispatchReceiverParameter
import org.jetbrains.kotlin.ir.util.endOffset
import org.jetbrains.kotlin.ir.util.startOffset
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.resolve.OverridingStrategy
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import java.lang.reflect.Proxy
object JsIrBuilder { object JsIrBuilder {
@@ -77,8 +92,8 @@ object JsIrBuilder {
fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol) = fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol) =
IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor, 0, null) IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor, 0, null)
fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType? = null) = fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType) =
IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type ?: symbol.owner.type) IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type)
.apply { this.initializer = initializer } .apply { this.initializer = initializer }
fun buildBreak(type: IrType, loop: IrLoop) = IrBreakImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop) fun buildBreak(type: IrType, loop: IrLoop) = IrBreakImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop)
@@ -107,8 +122,8 @@ object JsIrBuilder {
return element return element
} }
fun buildWhen(type: IrType, branches: List<IrBranch>) = fun buildWhen(type: IrType, branches: List<IrBranch>, origin: IrStatementOrigin = SYNTHESIZED_STATEMENT) =
IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, SYNTHESIZED_STATEMENT, branches) IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, origin, branches)
fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) = fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) =
IrTypeOperatorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, operator, toType, symbol, argument) IrTypeOperatorCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, operator, toType, symbol, argument)
@@ -117,4 +132,276 @@ object JsIrBuilder {
fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) fun buildBoolean(type: IrType, v: Boolean) = IrConstImpl.boolean(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v) fun buildInt(type: IrType, v: Int) = IrConstImpl.int(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, v)
fun buildString(type: IrType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s) fun buildString(type: IrType, s: String) = IrConstImpl.string(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, s)
fun buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block)
} }
object SetDeclarationsParentVisitor : IrElementVisitor<Unit, IrDeclarationParent> {
override fun visitElement(element: IrElement, data: IrDeclarationParent) {
if (element !is IrDeclarationParent) {
element.acceptChildren(this, data)
}
}
override fun visitDeclaration(declaration: IrDeclaration, data: IrDeclarationParent) {
declaration.parent = data
super.visitDeclaration(declaration, data)
}
}
fun SymbolTable.translateErased(type: KotlinType): IrSimpleType {
val descriptor = TypeUtils.getClassDescriptor(type)
if (descriptor == null) return translateErased(type.immediateSupertypes().first())
val classSymbol = this.referenceClass(descriptor)
val nullable = type.isMarkedNullable
val arguments = type.arguments.map { IrStarProjectionImpl }
return classSymbol.createType(nullable, arguments)
}
fun IrDeclarationContainer.addChildren(declarations: List<IrDeclaration>) {
declarations.forEach { this.addChild(it) }
}
fun IrDeclarationContainer.addChild(declaration: IrDeclaration) {
this.declarations += declaration
declaration.accept(SetDeclarationsParentVisitor, this)
}
fun IrClass.simpleFunctions(): List<IrSimpleFunction> = this.declarations.flatMap {
when (it) {
is IrSimpleFunction -> listOf(it)
is IrProperty -> listOfNotNull(it.getter as IrSimpleFunction?, it.setter as IrSimpleFunction?)
else -> emptyList()
}
}
fun IrClass.setSuperSymbols(superTypes: List<IrType>) {
val supers = superTypes.map { it.getClass()!! }
assert(this.superDescriptors().toSet() == supers.map { it.descriptor }.toSet())
assert(this.superTypes.isEmpty())
this.superTypes += superTypes
val superMembers = supers.flatMap {
it.simpleFunctions()
}.associateBy { it.descriptor }
this.simpleFunctions().forEach {
assert(it.overriddenSymbols.isEmpty())
it.descriptor.overriddenDescriptors.mapTo(it.overriddenSymbols) {
val superMember = superMembers[it.original] ?: error(it.original)
superMember.symbol
}
}
}
fun IrSimpleFunction.setOverrides(symbolTable: SymbolTable) {
assert(this.overriddenSymbols.isEmpty())
this.descriptor.overriddenDescriptors.mapTo(this.overriddenSymbols) {
symbolTable.referenceSimpleFunction(it.original)
}
}
private fun IrClass.superDescriptors() =
this.descriptor.typeConstructor.supertypes.map { it.constructor.declarationDescriptor as ClassDescriptor }
fun IrClass.setSuperSymbols(symbolTable: SymbolTable) {
assert(this.superTypes.isEmpty())
this.descriptor.typeConstructor.supertypes.mapTo(this.superTypes) { symbolTable.translateErased(it) }
this.simpleFunctions().forEach {
it.setOverrides(symbolTable)
}
}
private fun IrElement.innerStartOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.startOffset ?: this.startOffset
private fun IrElement.innerEndOffset(descriptor: DeclarationDescriptorWithSource): Int =
descriptor.endOffset ?: this.endOffset
fun IrFunction.createParameterDeclarations(symbolTable: SymbolTable) {
fun ParameterDescriptor.irValueParameter() = IrValueParameterImpl(
innerStartOffset(this), innerEndOffset(this),
IrDeclarationOrigin.DEFINED,
this, symbolTable.translateErased(this.type),
(this as? ValueParameterDescriptor)?.varargElementType?.let { symbolTable.translateErased(it) }
).also {
it.parent = this@createParameterDeclarations
}
dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.irValueParameter()
extensionReceiverParameter = descriptor.extensionReceiverParameter?.irValueParameter()
assert(valueParameters.isEmpty())
descriptor.valueParameters.mapTo(valueParameters) { it.irValueParameter() }
assert(typeParameters.isEmpty())
descriptor.typeParameters.mapTo(typeParameters) {
IrTypeParameterImpl(
innerStartOffset(it), innerEndOffset(it),
IrDeclarationOrigin.DEFINED,
it
).also { typeParameter ->
typeParameter.parent = this
typeParameter.descriptor.upperBounds.mapTo(typeParameter.superTypes, symbolTable::translateErased)
}
}
}
private fun createFakeOverride(
descriptor: CallableMemberDescriptor,
startOffset: Int,
endOffset: Int,
symbolTable: SymbolTable
): IrDeclaration {
fun FunctionDescriptor.createFunction(): IrSimpleFunction = IrFunctionImpl(
startOffset, endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE, this
).apply {
returnType = symbolTable.translateErased(this@createFunction.returnType!!)
createParameterDeclarations(symbolTable)
}
return when (descriptor) {
is FunctionDescriptor -> descriptor.createFunction()
is PropertyDescriptor ->
IrPropertyImpl(startOffset, endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, descriptor).apply {
// TODO: add field if getter is missing?
getter = descriptor.getter?.createFunction() as IrSimpleFunction?
setter = descriptor.setter?.createFunction() as IrSimpleFunction?
}
else -> TODO(descriptor.toString())
}
}
private fun createFakeOverride(
descriptor: CallableMemberDescriptor,
overriddenDeclarations: List<IrDeclaration>,
irClass: IrClass
): IrDeclaration {
// TODO: this function doesn't substitute types.
fun IrSimpleFunction.copyFake(descriptor: FunctionDescriptor): IrSimpleFunction = IrFunctionImpl(
irClass.startOffset, irClass.endOffset, IrDeclarationOrigin.FAKE_OVERRIDE, descriptor
).also {
it.returnType = returnType
it.parent = irClass
it.createDispatchReceiverParameter()
it.extensionReceiverParameter = this.extensionReceiverParameter?.let {
IrValueParameterImpl(
it.startOffset,
it.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.extensionReceiverParameter!!,
it.type,
null
)
}
this.valueParameters.mapTo(it.valueParameters) { oldParameter ->
IrValueParameterImpl(
oldParameter.startOffset,
oldParameter.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.valueParameters[oldParameter.index],
oldParameter.type,
(oldParameter as? IrValueParameter)?.varargElementType
)
}
this.typeParameters.mapTo(it.typeParameters) { oldParameter ->
IrTypeParameterImpl(
irClass.startOffset,
irClass.endOffset,
IrDeclarationOrigin.DEFINED,
it.descriptor.typeParameters[oldParameter.index]
).apply {
superTypes += oldParameter.superTypes
}
}
}
val copiedDeclaration = overriddenDeclarations.first()
return when (copiedDeclaration) {
is IrSimpleFunction -> copiedDeclaration.copyFake(descriptor as FunctionDescriptor)
is IrProperty -> IrPropertyImpl(
irClass.startOffset,
irClass.endOffset,
IrDeclarationOrigin.FAKE_OVERRIDE,
descriptor as PropertyDescriptor
).apply {
parent = irClass
getter = copiedDeclaration.getter?.copyFake(descriptor.getter!!)
setter = copiedDeclaration.setter?.copyFake(descriptor.setter!!)
}
else -> error(copiedDeclaration)
}
}
fun IrClass.setSuperSymbolsAndAddFakeOverrides(superTypes: List<IrType>) {
val overriddenSuperMembers = this.declarations.map { it.descriptor }
.filterIsInstance<CallableMemberDescriptor>().flatMap { it.overriddenDescriptors.map { it.original } }.toSet()
val unoverriddenSuperMembers = superTypes.map { it.getClass()!! }.flatMap {
it.declarations.filter { it.descriptor !in overriddenSuperMembers }.mapNotNull {
when (it) {
is IrSimpleFunction -> it.descriptor to it
is IrProperty -> it.descriptor to it
else -> null
}
}
}.toMap()
val irClass = this
val overridingStrategy = object : OverridingStrategy() {
override fun addFakeOverride(fakeOverride: CallableMemberDescriptor) {
val overriddenDeclarations =
fakeOverride.overriddenDescriptors.map { unoverriddenSuperMembers[it]!! }
assert(overriddenDeclarations.isNotEmpty())
irClass.declarations.add(createFakeOverride(fakeOverride, overriddenDeclarations, irClass))
}
override fun inheritanceConflict(first: CallableMemberDescriptor, second: CallableMemberDescriptor) {
error("inheritance conflict in synthesized class ${irClass.descriptor}:\n $first\n $second")
}
override fun overrideConflict(fromSuper: CallableMemberDescriptor, fromCurrent: CallableMemberDescriptor) {
error("override conflict in synthesized class ${irClass.descriptor}:\n $fromSuper\n $fromCurrent")
}
}
unoverriddenSuperMembers.keys.groupBy { it.name }.forEach { (name, members) ->
OverridingUtil.generateOverridesInFunctionGroup(
name,
members,
emptyList(),
this.descriptor,
overridingStrategy
)
}
this.setSuperSymbols(superTypes)
}
inline fun <reified T> stub(name: String): T {
return Proxy.newProxyInstance(T::class.java.classLoader, arrayOf(T::class.java)) {
_ /* proxy */, method, _ /* methodArgs */ ->
if (method.name == "toString" && method.parameterCount == 0) {
"${T::class.simpleName} stub for $name"
} else {
error("${T::class.simpleName}.${method.name} is not supported for $name")
}
} as T
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
@@ -22,22 +23,10 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass { class BlockDecomposerLowering(context: JsIrBackendContext) : DeclarationContainerLoweringPass {
private lateinit var function: IrFunction
private var tmpVarCounter: Int = 0
private val statementTransformer = StatementTransformer() private val decomposerTransformer = BlockDecomposerTransformer(context)
private val expressionTransformer = ExpressionTransformer() private val nothingType = context.irBuiltIns.nothingType
private val constTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit))
private val unreachableFunction =
JsSymbolBuilder.buildSimpleFunction(context.module, Namer.UNREACHABLE_NAME).initialize(returnType = nothingType)
override fun lower(irDeclarationContainer: IrDeclarationContainer) { override fun lower(irDeclarationContainer: IrDeclarationContainer) {
irDeclarationContainer.declarations.transformFlat { declaration -> irDeclarationContainer.declarations.transformFlat { declaration ->
@@ -53,9 +42,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
} }
fun lower(irFunction: IrFunction) { fun lower(irFunction: IrFunction) {
function = irFunction irFunction.accept(decomposerTransformer, null)
tmpVarCounter = 0
irFunction.transformChildrenVoid(statementTransformer)
} }
fun lower(irField: IrField, container: IrDeclarationContainer): List<IrDeclaration> { fun lower(irField: IrField, container: IrDeclarationContainer): List<IrDeclaration> {
@@ -86,6 +73,33 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
return listOf(irField) return listOf(irField)
} }
}
class BlockDecomposerTransformer(context: JsIrBackendContext) : IrElementTransformerVoid() {
private lateinit var function: IrFunction
private var tmpVarCounter: Int = 0
private val statementTransformer = StatementTransformer()
private val expressionTransformer = ExpressionTransformer()
private val constTrue = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
private val constFalse = JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, false)
private val nothingType = context.irBuiltIns.nothingNType
private val unitType = context.irBuiltIns.unitType
private val unitValue = JsIrBuilder.buildGetObjectValue(unitType, context.symbolTable.referenceClass(context.builtIns.unit))
private val unreachableFunction =
JsSymbolBuilder.buildSimpleFunction(context.module, Namer.UNREACHABLE_NAME).initialize(returnType = nothingType)
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
override fun visitFunction(declaration: IrFunction): IrStatement {
function = declaration
tmpVarCounter = 0
return declaration.transform(statementTransformer, null)
}
override fun visitElement(element: IrElement) = element.transform(statementTransformer, null)
private fun processStatements(statements: MutableList<IrStatement>) { private fun processStatements(statements: MutableList<IrStatement>) {
statements.transformFlat { statements.transformFlat {
@@ -171,7 +185,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
val result = IrCompositeImpl(receiverResult.startOffset, expression.endOffset, unitType) val result = IrCompositeImpl(receiverResult.startOffset, expression.endOffset, unitType)
val receiverValue = receiverResult.statements.last() as IrExpression val receiverValue = receiverResult.statements.last() as IrExpression
val tmp = makeTempVar(receiverResult.type) val tmp = makeTempVar(receiverResult.type)
val irVar = JsIrBuilder.buildVar(tmp, receiverValue) val irVar = JsIrBuilder.buildVar(tmp, receiverValue, receiverResult.type)
val setValue = valueResult.statements.last() as IrExpression val setValue = valueResult.statements.last() as IrExpression
result.statements += receiverResult.statements.run { subList(0, lastIndex) } result.statements += receiverResult.statements.run { subList(0, lastIndex) }
result.statements += irVar result.statements += irVar
@@ -249,7 +263,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
val newLoopCondition = newCondition.statements.last() as IrExpression val newLoopCondition = newCondition.statements.last() as IrExpression
val breakCond = JsIrBuilder.buildCall(context.irBuiltIns.booleanNotSymbol).apply { val breakCond = JsIrBuilder.buildCall(booleanNotSymbol).apply {
putValueArgument(0, newLoopCondition) putValueArgument(0, newLoopCondition)
} }
@@ -373,7 +387,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont
return block return block
} }
override fun visitTry(aTry: IrTry) = aTry.also { it.transformChildrenVoid(this); } override fun visitTry(aTry: IrTry) = aTry.also { it.transformChildrenVoid(this) }
} }
private inner class ExpressionTransformer : IrElementTransformerVoid() { private inner class ExpressionTransformer : IrElementTransformerVoid() {
@@ -35,6 +35,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.createParameterDeclarations import org.jetbrains.kotlin.ir.util.createParameterDeclarations
import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.isInterface
@@ -48,18 +49,21 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils
// Example: for given class hierarchy // Example: for given class hierarchy
// //
// class C<T> { // class C<T> {
// fun foo(t: T) = ... // fun foo(t: T) = ... // bridge
// } // }
// //
// class D : C<Int> { // class D : C<Int> {
// override fun foo(t: Int) = impl // override fun foo(t: Int) = impl // delegate to
// }
//
// class E : D {
// <fake override> fun foo(t: Int) // function
// } // }
// //
// it adds method D that delegates generic calls to implementation: // it adds method D that delegates generic calls to implementation:
// //
// class D : C<Int> { // class E : D {
// override fun foo(t: Int) = impl // fun foo(t: Any?) = foo(t as Int) // bridgeDescriptorForIrFunction
// fun foo(t: Any?) = foo(t as Int) // Constructed bridge
// } // }
// //
class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
@@ -125,6 +129,8 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
bridge.descriptor.returnType, bridge.descriptor.modality, function.visibility bridge.descriptor.returnType, bridge.descriptor.modality, function.visibility
) )
bridgeDescriptorForIrFunction.isSuspend = bridge.descriptor.isSuspend
// TODO: Support offsets for debug info // TODO: Support offsets for debug info
val irFunction = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, bridgeDescriptorForIrFunction) val irFunction = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, bridgeDescriptorForIrFunction)
irFunction.createParameterDeclarations() irFunction.createParameterDeclarations()
@@ -136,7 +142,10 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
irFunction.extensionReceiverParameter?.let { irFunction.extensionReceiverParameter?.let {
call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type) call.extensionReceiver = irCastIfNeeded(irGet(it), delegateTo.extensionReceiverParameter!!.type)
} }
irFunction.valueParameters.mapIndexed { i, valueParameter ->
val toTake = irFunction.valueParameters.size - if (call.descriptor.isSuspend xor irFunction.descriptor.isSuspend) 1 else 0
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type)) call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
} }
+irReturn(call) +irReturn(call)
@@ -147,8 +156,9 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
return irFunction return irFunction
} }
// TODO: get rid of Unit check
private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression = private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression =
if (argument.type.classifierOrNull == type.classifierOrNull) argument else irAs(argument, type) if (argument.type.classifierOrNull == type.classifierOrNull || type.isUnit()) argument else irAs(argument, type)
} }
// Handle for common.bridges // Handle for common.bridges
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
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.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
@@ -30,7 +31,7 @@ import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
// TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface // TODO: generate $metadata$ property and fill it with corresponding KFunction/KProperty interface
class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringPass { class CallableReferenceLowering(val context: JsIrBackendContext) {
private data class CallableReferenceKey( private data class CallableReferenceKey(
val declaration: IrFunction, val declaration: IrFunction,
@@ -47,12 +48,26 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
private val newDeclarations = mutableListOf<IrDeclaration>() private val newDeclarations = mutableListOf<IrDeclaration>()
override fun lower(irFile: IrFile) { fun getReferenceCollector() = object : FileLoweringPass {
irFile.acceptVoid(CallableReferenceCollector()) private val collector = CallableReferenceCollector()
buildClosures() override fun lower(irFile: IrFile) = irFile.acceptVoid(collector)
irFile.transformChildrenVoid(CallableReferenceTransformer()) }::lower
irFile.declarations += newDeclarations
} fun getClosureBuilder() = object : FileLoweringPass {
override fun lower(irFile: IrFile) {
newDeclarations.clear()
buildClosures(irFile)
irFile.declarations += newDeclarations
}
}::lower
fun getReferenceReplacer() = object : FileLoweringPass {
private val replacer = CallableReferenceTransformer()
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(replacer)
}
}::lower
private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) = private fun makeCallableKey(declaration: IrFunction, reference: IrCallableReference) =
CallableReferenceKey(declaration, reference.dispatchReceiver != null, reference.extensionReceiver != null) CallableReferenceKey(declaration, reference.dispatchReceiver != null, reference.extensionReceiver != null)
@@ -72,15 +87,30 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP
} }
} }
private fun buildClosures() { private fun buildClosures(irFile: IrFile) {
val declarationsSet = mutableSetOf<IrFunctionSymbol>()
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) = element.acceptChildrenVoid(this)
override fun visitFunction(declaration: IrFunction) {
super.visitFunction(declaration)
declarationsSet += declaration.symbol
}
})
for (v in collectedReferenceMap.values) { for (v in collectedReferenceMap.values) {
newDeclarations += v.accept(object : IrElementVisitor<List<IrDeclaration>, Nothing?> { newDeclarations += v.accept(object : IrElementVisitor<List<IrDeclaration>, Nothing?> {
override fun visitElement(element: IrElement, data: Nothing?) = error("Unreachable execution") override fun visitElement(element: IrElement, data: Nothing?) = error("Unreachable execution")
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?) = override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?) =
lowerKFunctionReference(expression.symbol.owner, expression) if (expression.symbol in declarationsSet) lowerKFunctionReference(expression.symbol.owner, expression) else emptyList()
override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?) = override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?) =
lowerKPropertyReference(expression.getter!!.owner, expression) if (expression.getter in declarationsSet) lowerKPropertyReference(
expression.getter!!.owner,
expression
) else emptyList()
}, null) }, null)
} }
} }
@@ -67,21 +67,25 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas
val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type }) val commonType = mergeTypes(aTry.catches.map { it.catchParameter.type })
val pendingExceptionSymbol = JsSymbolBuilder.buildVar(data!!.descriptor, commonType, "\$pending\$", false) val pendingExceptionSymbol = JsSymbolBuilder.buildVar(data!!.descriptor, commonType, "\$p", false)
val pendingExceptionDeclaration = JsIrBuilder.buildVar(pendingExceptionSymbol, type = commonType) val pendingExceptionDeclaration = JsIrBuilder.buildVar(pendingExceptionSymbol, type = commonType)
val pendingException = JsIrBuilder.buildGetValue(pendingExceptionSymbol) val pendingException = JsIrBuilder.buildGetValue(pendingExceptionSymbol)
val branches = mutableListOf<IrBranch>() val branches = mutableListOf<IrBranch>()
for (catch in aTry.catches) { for (catch in aTry.catches) {
assert(!catch.catchParameter.isVar) { "caught exception parameter has to immutable" } assert(!catch.catchParameter.isVar) { "caught exception parameter has to be immutable" }
val type = catch.catchParameter.type val type = catch.catchParameter.type
val typeSymbol = type.classifierOrNull val typeSymbol = type.classifierOrNull
val castedPendingException = if (type !is IrDynamicType)
buildImplicitCast(pendingException, type, typeSymbol!!)
else pendingException
val catchBody = catch.result.transform(object : IrElementTransformer<VariableDescriptor> { val catchBody = catch.result.transform(object : IrElementTransformer<VariableDescriptor> {
override fun visitGetValue(expression: IrGetValue, data: VariableDescriptor) = override fun visitGetValue(expression: IrGetValue, data: VariableDescriptor) =
if (typeSymbol != null && expression.descriptor == data) if (expression.descriptor == data)
// TODO how is it good to generate implicit cast for each access? castedPendingException
buildImplicitCast(pendingException, type, typeSymbol)
else else
expression expression
}, catch.parameter) }, catch.parameter)
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -14,7 +15,6 @@ import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.symbols.JsSymbolBuilder import org.jetbrains.kotlin.ir.backend.js.symbols.JsSymbolBuilder
import org.jetbrains.kotlin.ir.backend.js.symbols.initialize import org.jetbrains.kotlin.ir.backend.js.symbols.initialize
import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
@@ -24,7 +24,7 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
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.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.defaultType
@@ -33,19 +33,29 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransformerVoid(), DeclarationContainerLoweringPass { class SecondaryCtorLowering(val context: JsIrBackendContext) {
private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, JsIrBackendContext.SecondaryCtorPair>() data class ConstructorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol)
override fun lower(irDeclarationContainer: IrDeclarationContainer) { private val oldCtorToNewMap = mutableMapOf<IrConstructorSymbol, ConstructorPair>()
irDeclarationContainer.declarations.transformFlat {
if (it is IrClass) { fun getConstructorProcessorLowering() = object : DeclarationContainerLoweringPass {
listOf(it) + lowerClass(it) override fun lower(irDeclarationContainer: IrDeclarationContainer) {
} else null irDeclarationContainer.declarations.transformFlat {
if (it is IrClass) {
listOf(it) + lowerClass(it)
} else null
}
} }
}::runOnFilePostfix
context.secondaryConstructorsMap.putAll(oldCtorToNewMap) fun getConstructorRedirectorLowering() = object : DeclarationContainerLoweringPass {
} override fun lower(irDeclarationContainer: IrDeclarationContainer) {
for (it in irDeclarationContainer.declarations) {
it.accept(CallsiteRedirectionTransformer(), null)
}
}
}::runOnFilePostfix
private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> { private fun lowerClass(irClass: IrClass): List<IrSimpleFunction> {
val className = irClass.name.asString() val className = irClass.name.asString()
@@ -74,12 +84,10 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
// val t = Object.create(Foo.prototype); // val t = Object.create(Foo.prototype);
// return Foo_init_$Init$(..., t) // return Foo_init_$Init$(..., t)
// } // }
val newInitConstructor = createInitConstructor(declaration, irClass, constructorName, irClass.defaultType)
val newInitConstructor = createInitConstructor(declaration, constructorName, irClass.defaultType)
val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName, irClass.defaultType) val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName, irClass.defaultType)
oldCtorToNewMap[declaration.symbol] = oldCtorToNewMap[declaration.symbol] = ConstructorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
JsIrBackendContext.SecondaryCtorPair(newInitConstructor.symbol, newCreateConstructor.symbol)
oldConstructors += declaration oldConstructors += declaration
newConstructors += newInitConstructor newConstructors += newInitConstructor
@@ -92,7 +100,11 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return newConstructors return newConstructors
} }
private class ThisUsageReplaceTransformer(val function: IrFunctionSymbol, val thisSymbol: IrValueParameterSymbol) : private class ThisUsageReplaceTransformer(
val function: IrFunctionSymbol,
val newThisSymbol: IrValueSymbol,
val oldThisSymbol: IrValueSymbol?
) :
IrElementTransformerVoid() { IrElementTransformerVoid() {
override fun visitReturn(expression: IrReturn): IrExpression = IrReturnImpl( override fun visitReturn(expression: IrReturn): IrExpression = IrReturnImpl(
@@ -100,15 +112,15 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
expression.endOffset, expression.endOffset,
expression.type, expression.type,
function, function,
IrGetValueImpl(expression.startOffset, expression.endOffset, thisSymbol.owner.type, thisSymbol) IrGetValueImpl(expression.startOffset, expression.endOffset, newThisSymbol.owner.type, newThisSymbol)
) )
override fun visitGetValue(expression: IrGetValue): IrExpression = override fun visitGetValue(expression: IrGetValue): IrExpression =
if (expression.descriptor.name.isSpecial && expression.descriptor.name.asString() == Namer.THIS_SPECIAL_NAME) IrGetValueImpl( if (expression.symbol == oldThisSymbol) IrGetValueImpl(
expression.startOffset, expression.startOffset,
expression.endOffset, expression.endOffset,
expression.type, expression.type,
thisSymbol, newThisSymbol,
expression.origin expression.origin
) else { ) else {
expression expression
@@ -117,6 +129,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
private fun createInitConstructor( private fun createInitConstructor(
declaration: IrConstructor, declaration: IrConstructor,
klass: IrClass,
name: String, name: String,
type: IrType type: IrType
): IrSimpleFunction = ): IrSimpleFunction =
@@ -135,6 +148,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
) )
val thisParam = JsIrBuilder.buildValueParameter(thisSymbol, type) val thisParam = JsIrBuilder.buildValueParameter(thisSymbol, type)
val oldThisReceiver = klass.thisReceiver?.symbol
return IrFunctionImpl( return IrFunctionImpl(
declaration.startOffset, declaration.endOffset, declaration.startOffset, declaration.endOffset,
@@ -147,7 +161,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
typeParameters += declaration.typeParameters typeParameters += declaration.typeParameters
// parent = declaration.parent // parent = declaration.parent
body = JsIrBuilder.buildBlockBody(statements + retStmt).apply { body = JsIrBuilder.buildBlockBody(statements + retStmt).apply {
transformChildrenVoid(ThisUsageReplaceTransformer(it, thisSymbol)) transformChildrenVoid(ThisUsageReplaceTransformer(it, thisSymbol, oldThisReceiver))
} }
} }
@@ -198,23 +212,22 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
} }
class CallsiteRedirectionTransformer(val context: JsIrBackendContext) : IrElementTransformer<IrFunction?> { inner class CallsiteRedirectionTransformer : IrElementTransformer<IrFunction?> {
override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration) override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration)
override fun visitCall(expression: IrCall, ownerFunc: IrFunction?): IrElement { override fun visitCall(expression: IrCall, data: IrFunction?): IrElement {
super.visitCall(expression, ownerFunc) super.visitCall(expression, data)
// TODO: figure out the reason why symbol is not bound // TODO: figure out the reason why symbol is not bound
if (expression.symbol.isBound) { if (expression.symbol.isBound) {
val target = expression.symbol.owner as IrFunction val target = expression.symbol.owner
if (target is IrConstructor) { if (target is IrConstructor) {
if (!target.descriptor.isPrimary) { if (!target.descriptor.isPrimary) {
val ctor = context.secondaryConstructorsMap[target.symbol] val ctor = oldCtorToNewMap[target.symbol]
if (ctor != null) { if (ctor != null) {
return redirectCall(expression, ctor.stub) return redirectCall(expression, ctor.stub)
} }
} }
@@ -224,8 +237,8 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return expression return expression
} }
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, ownerFunc: IrFunction?): IrElement { override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement {
super.visitDelegatingConstructorCall(expression, ownerFunc) super.visitDelegatingConstructorCall(expression, data)
val target = expression.symbol val target = expression.symbol
if (target.owner.isPrimary) { if (target.owner.isPrimary) {
@@ -233,9 +246,9 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
return expression return expression
} }
val fromPrimary = ownerFunc!! is IrConstructor val fromPrimary = data!! is IrConstructor
// TODO: what is `deserialized` constructor? // TODO: what is `deserialized` constructor?
val ctor = context.secondaryConstructorsMap[target] ?: return expression val ctor = oldCtorToNewMap[target] ?: return expression
val newCall = redirectCall(expression, ctor.delegate) val newCall = redirectCall(expression, ctor.delegate)
val readThis = if (fromPrimary) { val readThis = if (fromPrimary) {
@@ -246,7 +259,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor
IrValueParameterSymbolImpl(LazyClassReceiverParameterDescriptor(target.descriptor.containingDeclaration)) IrValueParameterSymbolImpl(LazyClassReceiverParameterDescriptor(target.descriptor.containingDeclaration))
) )
} else { } else {
IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, ownerFunc.valueParameters.last().symbol) IrGetValueImpl(expression.startOffset, expression.endOffset, expression.type, data.valueParameters.last().symbol)
} }
newCall.putValueArgument(expression.valueArgumentsCount, readThis) newCall.putValueArgument(expression.valueArgumentsCount, readThis)
@@ -0,0 +1,735 @@
/*
* 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.coroutines
import org.jetbrains.kotlin.backend.common.peek
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
import org.jetbrains.kotlin.ir.backend.js.symbols.JsSymbolBuilder
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.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.visitors.*
class SuspendState(type: IrType) {
val entryBlock: IrContainerExpression = JsIrBuilder.buildComposite(type)
val successors = mutableSetOf<SuspendState>()
var id = -1
}
data class LoopBounds(val headState: SuspendState, val exitState: SuspendState)
data class FinallyTargets(val normal: SuspendState, val fromThrow: SuspendState)
data class TryState(val tryState: SuspendState, val catchState: SuspendState, val finallyState: FinallyTargets?)
class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.entryBlock.type) {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D) = visitor.visitExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {}
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {}
}
class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrElementTransformerVoid() {
override fun visitExpression(expression: IrExpression): IrExpression {
val dispatchPoint = expression as? IrDispatchPoint
?: return super.visitExpression(expression)
return action(dispatchPoint.target)
}
}
class StateMachineBuilder(
private val suspendableNodes: MutableSet<IrElement>,
val context: JsIrBackendContext,
val function: IrFunctionSymbol,
private val rootLoop: IrLoop,
private val exceptionSymbol: IrFieldSymbol,
private val exStateSymbol: IrFieldSymbol,
private val stateSymbol: IrFieldSymbol,
thisSymbol: IrValueParameterSymbol,
private val suspendResult: IrVariableSymbol
) : IrElementVisitorVoid {
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
private val unit = context.irBuiltIns.unitType
private val nothing = context.irBuiltIns.nothingType
private val int = context.irBuiltIns.intType
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
private val thisReceiver = JsIrBuilder.buildGetValue(thisSymbol)
private var hasExceptions = false
val entryState = SuspendState(unit)
val rootExceptionTrap = buildExceptionTrapState()
private val globalExceptionSymbol =
JsSymbolBuilder.buildTempVar(function, exceptionSymbol.owner.type, "e")
lateinit var globalCatch: IrCatch
fun finalizeStateMachine() {
val unitValue = JsIrBuilder.buildGetObjectValue(
unit,
context.symbolTable.referenceClass(context.builtIns.unit)
)
globalCatch = buildGlobalCatch()
if (currentBlock.statements.lastOrNull() !is IrReturn) {
addStatement(JsIrBuilder.buildReturn(function, unitValue, nothing))
}
if (!hasExceptions) entryState.successors += rootExceptionTrap
}
private fun buildGlobalCatch(): IrCatch {
val catchVariable =
JsIrBuilder.buildVar(globalExceptionSymbol, type = exceptionSymbol.owner.type)
val block = JsIrBuilder.buildBlock(unit)
if (hasExceptions) {
val thenBlock = JsIrBuilder.buildBlock(unit)
val elseBlock = JsIrBuilder.buildBlock(unit)
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
putValueArgument(0, exceptionState())
putValueArgument(1, IrDispatchPoint(rootExceptionTrap))
}
block.statements += JsIrBuilder.buildIfElse(unit, check, thenBlock, elseBlock)
thenBlock.statements += JsIrBuilder.buildThrow(
nothing,
JsIrBuilder.buildGetValue(globalExceptionSymbol)
)
// TODO: exception table
elseBlock.statements += JsIrBuilder.buildSetField(
stateSymbol,
thisReceiver,
exceptionState(),
unit
)
elseBlock.statements += JsIrBuilder.buildSetField(
exceptionSymbol,
thisReceiver,
JsIrBuilder.buildGetValue(globalExceptionSymbol),
unit
)
} else {
block.statements += JsIrBuilder.buildThrow(
nothing,
JsIrBuilder.buildGetValue(globalExceptionSymbol)
)
}
return JsIrBuilder.buildCatch(catchVariable, block)
}
private var currentState = entryState
private var currentBlock = entryState.entryBlock
private val returnableBlockMap = mutableMapOf<IrReturnableBlockSymbol, Pair<SuspendState, IrVariableSymbol?>>()
private val catchBlockStack = mutableListOf(rootExceptionTrap)
private fun buildExceptionTrapState(): SuspendState {
val state = SuspendState(unit)
state.entryBlock.statements += JsIrBuilder.buildThrow(nothing, pendingException())
return state
}
private fun newState() {
val newState = SuspendState(unit)
doDispatch(newState)
updateState(newState)
}
private fun updateState(newState: SuspendState) {
currentState = newState
currentBlock = newState.entryBlock
}
private fun lastExpression() = currentBlock.statements.lastOrNull() as? IrExpression ?: unitValue
private fun IrContainerExpression.addStatement(statement: IrStatement) {
statements.add(statement)
}
private fun addStatement(statement: IrStatement) = currentBlock.addStatement(statement)
private fun maybeDoDispatch(target: SuspendState) {
val lastStatement = currentBlock.statements.lastOrNull()
if (lastStatement !is IrReturn && lastStatement !is IrContinue && lastStatement !is IrThrow) {
doDispatch(target)
}
}
private fun doDispatch(target: SuspendState, andContinue: Boolean = true) = doDispatchImpl(target, currentBlock, andContinue)
private fun doDispatchImpl(target: SuspendState, block: IrContainerExpression, andContinue: Boolean) {
val irDispatch = IrDispatchPoint(target)
currentState.successors.add(target)
block.addStatement(JsIrBuilder.buildSetField(stateSymbol, thisReceiver, irDispatch, unit))
if (andContinue) doContinue(block)
}
private fun doContinue(block: IrContainerExpression = currentBlock) {
block.addStatement(JsIrBuilder.buildContinue(nothing, rootLoop))
}
private fun transformLastExpression(transformer: (IrExpression) -> IrStatement) {
val expression = lastExpression()
val newStatement = transformer(expression)
currentBlock.statements.let { if (it.isNotEmpty()) it[it.lastIndex] = newStatement else it += newStatement }
}
private fun buildDispatchBlock(target: SuspendState) = JsIrBuilder.buildComposite(unit)
.also { doDispatchImpl(target, it, true) }
override fun visitElement(element: IrElement) {
if (element in suspendableNodes) {
element.acceptChildrenVoid(this)
} else {
addStatement(element as IrStatement)
}
}
private fun transformLoop(loop: IrLoop, transformer: (IrLoop, SuspendState /*head*/, SuspendState /*exit*/) -> Unit) {
if (loop !in suspendableNodes) return addStatement(loop)
newState()
val loopHeadState = currentState
val loopExitState = SuspendState(unit)
loopMap[loop] = LoopBounds(loopHeadState, loopExitState)
transformer(loop, loopHeadState, loopExitState)
loopMap.remove(loop)
updateState(loopExitState)
}
override fun visitWhileLoop(loop: IrWhileLoop) = transformLoop(loop) { l, head, exit ->
l.condition.acceptVoid(this)
transformLastExpression {
val exitCond = JsIrBuilder.buildCall(booleanNotSymbol).apply { putValueArgument(0, it) }
val irBreak = buildDispatchBlock(exit)
JsIrBuilder.buildIfElse(unit, exitCond, irBreak)
}
l.body?.acceptVoid(this)
doDispatch(head)
}
override fun visitDoWhileLoop(loop: IrDoWhileLoop) = transformLoop(loop) { l, head, exit ->
l.body?.acceptVoid(this)
l.condition.acceptVoid(this)
transformLastExpression {
val irContinue = buildDispatchBlock(head)
JsIrBuilder.buildIfElse(unit, it, irContinue)
}
doDispatch(exit)
}
private fun processReturnableBlock(expression: IrReturnableBlock) {
if (expression !in suspendableNodes) return super.visitBlock(expression)
val exitState = SuspendState(unit)
val resultVariable = if (hasResultingValue(expression)) {
val symbol = tempVar(expression.type, "RETURNABLE_BLOCK")
addStatement(JsIrBuilder.buildVar(symbol, null, expression.type))
symbol
} else null
returnableBlockMap[expression.symbol] = Pair(exitState, resultVariable)
super.visitBlock(expression)
returnableBlockMap.remove(expression.symbol)
maybeDoDispatch(exitState)
updateState(exitState)
if (resultVariable != null) {
addStatement(JsIrBuilder.buildGetValue(resultVariable))
}
}
override fun visitBlock(expression: IrBlock) =
if (expression is IrReturnableBlock) processReturnableBlock(expression) else super.visitBlock(expression)
private fun implicitCast(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(toType, IrTypeOperator.IMPLICIT_CAST, value, toType, toType.classifierOrFail)
override fun visitCall(expression: IrCall) {
super.visitCall(expression)
if (expression.descriptor.isSuspend) {
val result = lastExpression()
val continueState = SuspendState(unit)
val dispatch = IrDispatchPoint(continueState)
currentState.successors += continueState
transformLastExpression { JsIrBuilder.buildSetField(stateSymbol, thisReceiver, dispatch, unit) }
addStatement(JsIrBuilder.buildSetVariable(suspendResult, result, unit))
val irReturn = JsIrBuilder.buildReturn(function, JsIrBuilder.buildGetValue(suspendResult), nothing)
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
putValueArgument(0, JsIrBuilder.buildGetValue(suspendResult))
putValueArgument(1, JsIrBuilder.buildCall(context.ir.symbols.coroutineSuspendedGetter))
}
val suspensionBlock = JsIrBuilder.buildBlock(unit, listOf(irReturn))
addStatement(JsIrBuilder.buildIfElse(unit, check, suspensionBlock))
doContinue()
updateState(continueState)
addStatement(implicitCast(JsIrBuilder.buildGetValue(suspendResult), expression.type))
}
}
override fun visitBreak(jump: IrBreak) {
val exitState = loopMap[jump.loop]!!.exitState
doDispatch(exitState)
}
override fun visitContinue(jump: IrContinue) {
val headState = loopMap[jump.loop]!!.headState
doDispatch(headState)
}
private fun wrap(expression: IrExpression, variable: IrVariableSymbol) =
JsIrBuilder.buildSetVariable(variable, expression, unit)
override fun visitWhen(expression: IrWhen) {
if (expression !in suspendableNodes) return addStatement(expression)
val exitState = SuspendState(expression.type)
val varSymbol: IrVariableSymbol?
val branches: List<IrBranch>
if (hasResultingValue(expression)) {
varSymbol = tempVar(expression.type, "WHEN_RESULT")
addStatement(JsIrBuilder.buildVar(varSymbol, type = expression.type))
branches = expression.branches.map {
val wrapped = wrap(it.result, varSymbol)
if (it.result in suspendableNodes) {
suspendableNodes += wrapped
}
when (it) {
is IrElseBranch -> IrElseBranchImpl(
it.startOffset,
it.endOffset,
it.condition,
wrapped
)
else /* IrBranch */ -> IrBranchImpl(
it.startOffset,
it.endOffset,
it.condition,
wrapped
)
}
}
} else {
varSymbol = null
branches = expression.branches
}
val rootState = currentState
val rootBlock = currentBlock
for (branch in branches) {
if (branch !is IrElseBranch) {
branch.condition.acceptVoid(this)
val branchBlock = JsIrBuilder.buildComposite(branch.result.type)
val elseBlock = JsIrBuilder.buildComposite(expression.type)
val dispatchState = currentState
transformLastExpression {
// TODO: make sure elseBlock is added iff it really needs
JsIrBuilder.buildIfElse(unit, it, branchBlock, elseBlock)
}
currentBlock = branchBlock
branch.result.acceptVoid(this)
if (currentBlock.statements.last() !is IrContinue) {
if (currentState !== rootState) {
doDispatch(exitState)
}
}
currentState = dispatchState
currentBlock = elseBlock
} else {
branch.result.acceptVoid(this)
if (currentBlock.statements.last() !is IrContinue) {
if (currentState !== rootState) {
doDispatch(exitState)
}
}
break
}
}
currentState = rootState
currentBlock = rootBlock
maybeDoDispatch(exitState)
updateState(exitState)
if (varSymbol != null) {
addStatement(JsIrBuilder.buildGetValue(varSymbol))
}
}
override fun visitSetVariable(expression: IrSetVariable) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { value = it } }
}
override fun visitVariable(declaration: IrVariable) {
if (declaration !in suspendableNodes) return addStatement(declaration)
declaration.acceptChildrenVoid(this)
transformLastExpression { declaration.apply { initializer = it } }
}
override fun visitGetField(expression: IrGetField) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { receiver = it } }
}
override fun visitGetClass(expression: IrGetClass) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { argument = it } }
}
private fun transformArguments(arguments: Array<IrExpression?>): Array<IrExpression?> {
var suspendableCount = arguments.fold(0) { r, n -> if (n in suspendableNodes) r + 1 else r }
val newArguments = arrayOfNulls<IrExpression>(arguments.size)
for ((i, arg) in arguments.withIndex()) {
newArguments[i] = if (arg != null && suspendableCount > 0) {
if (arg in suspendableNodes) suspendableCount--
arg.acceptVoid(this)
val tmp = tempVar(arg.type, "ARGUMENT")
transformLastExpression { JsIrBuilder.buildVar(tmp, it, it.type) }
JsIrBuilder.buildGetValue(tmp)
} else arg
}
return newArguments
}
override fun visitMemberAccess(expression: IrMemberAccessExpression) {
if (expression !in suspendableNodes) {
addExceptionEdge()
return addStatement(expression)
}
val arguments = arrayOfNulls<IrExpression>(expression.valueArgumentsCount + 2)
arguments[0] = expression.dispatchReceiver
arguments[1] = expression.extensionReceiver
for (i in 0 until expression.valueArgumentsCount) {
arguments[i + 2] = expression.getValueArgument(i)
}
val newArguments = transformArguments(arguments)
expression.dispatchReceiver = newArguments[0]
expression.extensionReceiver = newArguments[1]
for (i in 0 until expression.valueArgumentsCount) {
expression.putValueArgument(i, newArguments[i + 2])
}
addExceptionEdge()
addStatement(expression)
}
override fun visitSetField(expression: IrSetField) {
if (expression !in suspendableNodes) return addStatement(expression)
val newArguments = transformArguments(arrayOf(expression.receiver, expression.value))
val receiver = newArguments[0]
val value = newArguments[1] as IrExpression
addStatement(expression.run {
IrSetFieldImpl(
startOffset,
endOffset,
symbol,
receiver,
value,
unit,
origin,
superQualifierSymbol
)
})
}
// TODO: should it be lowered before?
override fun visitStringConcatenation(expression: IrStringConcatenation) {
assert(expression in suspendableNodes)
val arguments = arrayOfNulls<IrExpression>(expression.arguments.size)
expression.arguments.forEachIndexed { i, a -> arguments[i] = a }
val newArguments = transformArguments(arguments)
addStatement(expression.run {
IrStringConcatenationImpl(
startOffset,
endOffset,
type,
newArguments.map { it!! })
})
}
private val unitValue = JsIrBuilder.buildGetObjectValue(
unit,
context.symbolTable.referenceClass(context.builtIns.unit)
)
override fun visitReturn(expression: IrReturn) {
expression.acceptChildrenVoid(this)
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
val (exitState, varSymbol) = returnableBlockMap[expression.returnTargetSymbol]!!
if (varSymbol != null) {
transformLastExpression { JsIrBuilder.buildSetVariable(varSymbol, it, it.type) }
}
doDispatch(exitState)
} else {
transformLastExpression { expression.apply { value = it } }
}
}
private fun addExceptionEdge() {
hasExceptions = true
currentState.successors += catchBlockStack.peek()!!
}
private fun hasResultingValue(expression: IrExpression) = expression.type.run { !isUnit() && !isNothing() }
override fun visitThrow(expression: IrThrow) {
expression.acceptChildrenVoid(this)
addExceptionEdge()
transformLastExpression { expression.apply { value = it } }
}
override fun visitTry(aTry: IrTry) {
val tryState = buildTryState(aTry)
val enclosingCatch = catchBlockStack.peek()!!
catchBlockStack.push(tryState.catchState)
val finallyStateVarSymbol = tempVar(int, "FINALLY_STATE")
val exitState = SuspendState(unit)
val varSymbol = if (hasResultingValue(aTry)) tempVar(aTry.type, "TRY_RESULT") else null
if (aTry.finallyExpression != null) {
addStatement(
JsIrBuilder.buildVar(
finallyStateVarSymbol,
IrDispatchPoint(exitState), int
)
)
}
if (varSymbol != null) {
addStatement(JsIrBuilder.buildVar(varSymbol, type = aTry.type))
}
// TODO: refact it with exception table, see coroutinesInternal.kt
setupExceptionState(tryState.catchState)
val tryResult = if (varSymbol != null) {
JsIrBuilder.buildSetVariable(varSymbol, aTry.tryResult, unit).also {
if (it.value in suspendableNodes) suspendableNodes += it
}
} else aTry.tryResult
tryResult.acceptVoid(this)
if (tryState.finallyState != null) {
doDispatch(tryState.finallyState.normal)
} else {
setupExceptionState(enclosingCatch)
doDispatch(exitState)
}
addExceptionEdge()
catchBlockStack.pop()
updateState(tryState.catchState)
if (tryState.finallyState != null) {
setupExceptionState(tryState.finallyState.fromThrow)
} else {
setupExceptionState(enclosingCatch)
}
val ex = pendingException()
var rethrowNeeded = true
for (catch in aTry.catches) {
val type = catch.catchParameter.type
val initializer = if (type !is IrDynamicType) implicitCast(ex, type) else ex
val irVar = catch.catchParameter.also {
it.initializer = initializer
}
val catchResult = if (varSymbol != null) {
JsIrBuilder.buildSetVariable(varSymbol, catch.result, unit).also {
if (it.value in suspendableNodes) suspendableNodes += it
}
} else catch.result
if (type is IrDynamicType) {
rethrowNeeded = false
addStatement(irVar)
catchResult.acceptVoid(this)
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
maybeDoDispatch(exitDispatch)
} else {
val check = buildIsCheck(ex, type)
val branchBlock = JsIrBuilder.buildComposite(catchResult.type)
val elseBlock = JsIrBuilder.buildComposite(catchResult.type)
val irIf = JsIrBuilder.buildIfElse(catchResult.type, check, branchBlock, elseBlock)
val ifBlock = currentBlock
currentBlock = branchBlock
addStatement(irVar)
catchResult.acceptVoid(this)
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
maybeDoDispatch(exitDispatch)
currentBlock = ifBlock
addStatement(irIf)
currentBlock = elseBlock
}
}
if (rethrowNeeded) {
addExceptionEdge()
addStatement(JsIrBuilder.buildThrow(nothing, ex))
}
if (tryState.finallyState == null) {
currentState.successors += enclosingCatch
}
val finallyState = tryState.finallyState
if (finallyState != null) {
val throwExitState = SuspendState(unit)
updateState(finallyState.fromThrow)
tryState.tryState.successors += finallyState.fromThrow
addStatement(
JsIrBuilder.buildSetVariable(
finallyStateVarSymbol,
IrDispatchPoint(throwExitState), int
)
)
doDispatch(finallyState.normal)
updateState(finallyState.normal)
tryState.tryState.successors += finallyState.normal
setupExceptionState(enclosingCatch)
aTry.finallyExpression?.acceptVoid(this)
currentState.successors += listOf(throwExitState, exitState)
addStatement(
JsIrBuilder.buildSetField(
stateSymbol,
thisReceiver,
JsIrBuilder.buildGetValue(
finallyStateVarSymbol
),
unit
)
)
doContinue()
updateState(throwExitState)
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
addExceptionEdge()
}
updateState(exitState)
if (varSymbol != null) {
addStatement(JsIrBuilder.buildGetValue(varSymbol))
}
}
private fun setupExceptionState(target: SuspendState) {
addStatement(
JsIrBuilder.buildSetField(
exStateSymbol, thisReceiver,
IrDispatchPoint(target), unit
)
)
}
private fun exceptionState() = JsIrBuilder.buildGetField(exStateSymbol, thisReceiver)
private fun pendingException() = JsIrBuilder.buildGetField(exceptionSymbol, thisReceiver)
private fun buildTryState(aTry: IrTry) =
TryState(
currentState,
SuspendState(unit),
aTry.finallyExpression?.run {
FinallyTargets(
SuspendState(
unit
), SuspendState(unit)
)
}
)
private fun buildIsCheck(value: IrExpression, toType: IrType) =
JsIrBuilder.buildTypeOperator(
context.irBuiltIns.booleanType,
IrTypeOperator.INSTANCEOF,
value,
toType,
toType.classifierOrNull!!
)
private fun tempVar(type: IrType, name: String? = null) =
JsSymbolBuilder.buildTempVar(function, type, name)
}
@@ -0,0 +1,943 @@
/*
* 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.coroutines
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.getFunction
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.ir.createOverriddenDescriptor
import org.jetbrains.kotlin.backend.common.lower.SymbolWithIrBuilder
import org.jetbrains.kotlin.backend.common.lower.copyAsValueParameter
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.lower.irBlockBody
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.*
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrConstructorSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.DFS
internal class SuspendFunctionsLowering(val context: JsIrBackendContext): FileLoweringPass {
private object STATEMENT_ORIGIN_COROUTINE_IMPL : IrStatementOriginImpl("COROUTINE_IMPL")
private object DECLARATION_ORIGIN_COROUTINE_IMPL : IrDeclarationOriginImpl("COROUTINE_IMPL")
private val builtCoroutines = mutableMapOf<FunctionDescriptor, BuiltCoroutine>()
private val suspendLambdas = mutableMapOf<FunctionDescriptor, IrFunctionReference>()
override fun lower(irFile: IrFile) {
markSuspendLambdas(irFile)
buildCoroutines(irFile)
transformCallableReferencesToSuspendLambdas(irFile)
}
private fun buildCoroutines(irFile: IrFile) {
irFile.declarations.transformFlat(::tryTransformSuspendFunction)
irFile.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.declarations.transformFlat(::tryTransformSuspendFunction)
}
})
}
private fun tryTransformSuspendFunction(element: IrElement) =
if (element is IrFunction && element.descriptor.isSuspend && element.descriptor.modality != Modality.ABSTRACT)
transformSuspendFunction(element, suspendLambdas[element.descriptor])
else null
private fun markSuspendLambdas(irElement: IrElement) {
irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
expression.acceptChildrenVoid(this)
val descriptor = expression.descriptor
if (descriptor.isSuspend)
suspendLambdas.put(descriptor, expression)
}
})
}
private fun transformCallableReferencesToSuspendLambdas(irElement: IrElement) {
irElement.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
expression.transformChildrenVoid(this)
val descriptor = expression.descriptor
if (!descriptor.isSuspend)
return expression
val coroutine = builtCoroutines[descriptor]
?: throw Error("Non-local callable reference to suspend lambda: $descriptor")
val constructorParameters = coroutine.coroutineConstructor.valueParameters
val expressionArguments = expression.getArguments().map { it.second }
assert(constructorParameters.size == expressionArguments.size,
{ "Inconsistency between callable reference to suspend lambda and the corresponding coroutine" })
val irBuilder = context.createIrBuilder(expression.symbol, expression.startOffset, expression.endOffset)
irBuilder.run {
return irCall(coroutine.coroutineConstructor.symbol).apply {
expressionArguments.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
}
}
}
})
}
private sealed class SuspendFunctionKind {
object NO_SUSPEND_CALLS : SuspendFunctionKind()
class DELEGATING(val delegatingCall: IrCall) : SuspendFunctionKind()
object NEEDS_STATE_MACHINE : SuspendFunctionKind()
}
private fun transformSuspendFunction(irFunction: IrFunction, functionReference: IrFunctionReference?): List<IrDeclaration>? {
val suspendFunctionKind = getSuspendFunctionKind(irFunction)
return when (suspendFunctionKind) {
is SuspendFunctionKind.NO_SUSPEND_CALLS -> {
null // No suspend function calls - just an ordinary function.
}
is SuspendFunctionKind.DELEGATING -> { // Calls another suspend function at the end.
removeReturnIfSuspendedCallAndSimplifyDelegatingCall(
irFunction, suspendFunctionKind.delegatingCall)
null // No need in state machine.
}
is SuspendFunctionKind.NEEDS_STATE_MACHINE -> {
val coroutine = buildCoroutine(irFunction, functionReference) // Coroutine implementation.
if (suspendLambdas.contains(irFunction.descriptor)) // Suspend lambdas are called through factory method <create>,
listOf(coroutine) // thus we can eliminate original body.
else
listOf<IrDeclaration>(
coroutine,
irFunction
)
}
}
}
private fun getSuspendFunctionKind(irFunction: IrFunction): SuspendFunctionKind {
if (suspendLambdas.contains(irFunction.descriptor))
return SuspendFunctionKind.NEEDS_STATE_MACHINE // Suspend lambdas always need coroutine implementation.
val body = irFunction.body
?: return SuspendFunctionKind.NO_SUSPEND_CALLS
var numberOfSuspendCalls = 0
body.acceptVoid(object: IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitCall(expression: IrCall) {
expression.acceptChildrenVoid(this)
if (expression.descriptor.isSuspend)
++numberOfSuspendCalls
}
})
// It is important to optimize the case where there is only one suspend call and it is the last statement
// because we don't need to build a fat coroutine class in that case.
// This happens a lot in practise because of suspend functions with default arguments.
// TODO: use TailRecursionCallsCollector.
val lastStatement = (body as IrBlockBody).statements.lastOrNull()
val lastCall = when (lastStatement) {
is IrCall -> lastStatement
is IrReturn -> {
var value: IrElement = lastStatement
/*
* Check if matches this pattern:
* block/return {
* block/return {
* .. suspendCall()
* }
* }
*/
loop@while (true) {
when {
value is IrBlock && value.statements.size == 1 -> value = value.statements.first()
value is IrReturn -> value = value.value
else -> break@loop
}
}
value as? IrCall
}
else -> null
}
val suspendCallAtEnd = lastCall != null && lastCall.descriptor.isSuspend // Suspend call.
return when {
numberOfSuspendCalls == 0 -> SuspendFunctionKind.NO_SUSPEND_CALLS
numberOfSuspendCalls == 1
&& suspendCallAtEnd -> SuspendFunctionKind.DELEGATING(
lastCall!!
)
else -> SuspendFunctionKind.NEEDS_STATE_MACHINE
}
}
private val symbols = context.ir.symbols
private val unit = context.run { symbolTable.referenceClass(builtIns.unit) }
private val getContinuationSymbol =
context.run {
val f = getInternalFunctions("getContinuation")
symbolTable.referenceSimpleFunction(f.single())
}
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
private val returnIfSuspendedDescriptor = context.getInternalFunctions("returnIfSuspended").single()
private fun removeReturnIfSuspendedCallAndSimplifyDelegatingCall(irFunction: IrFunction, delegatingCall: IrCall) {
val returnValue =
if (delegatingCall.descriptor.original == returnIfSuspendedDescriptor)
delegatingCall.getValueArgument(0)!!
else delegatingCall
context.createIrBuilder(irFunction.symbol).run {
val statements = (irFunction.body as IrBlockBody).statements
val lastStatement = statements.last()
assert (lastStatement == delegatingCall || lastStatement is IrReturn) { "Unexpected statement $lastStatement" }
statements[statements.size - 1] = irReturn(returnValue)
}
}
private fun buildCoroutine(irFunction: IrFunction, functionReference: IrFunctionReference?): IrClass {
val descriptor = irFunction.descriptor
val coroutine = CoroutineBuilder(irFunction, functionReference).build()
builtCoroutines.put(descriptor, coroutine)
if (functionReference == null) {
// It is not a lambda - replace original function with a call to constructor of the built coroutine.
val irBuilder = context.createIrBuilder(irFunction.symbol, irFunction.startOffset, irFunction.endOffset)
irFunction.body = irBuilder.irBlockBody(irFunction) {
+irReturn(
irCall(coroutine.doResumeFunction.symbol).apply {
dispatchReceiver = irCall(coroutine.coroutineConstructor.symbol).apply {
val functionParameters = irFunction.explicitParameters
functionParameters.forEachIndexed { index, argument ->
putValueArgument(index, irGet(argument))
}
putValueArgument(functionParameters.size,
irCall(getContinuationSymbol, getContinuationSymbol.owner.returnType, listOf(irFunction.returnType)))
}
putValueArgument(0, irGetObject(unit)) // value
putValueArgument(1, irNull()) // exception
})
}
}
return coroutine.coroutineClass
}
private class BuiltCoroutine(val coroutineClass: IrClass,
val coroutineConstructor: IrConstructor,
val doResumeFunction: IrFunction)
private var coroutineId = 0
private inner class CoroutineBuilder(val irFunction: IrFunction, val functionReference: IrFunctionReference?) {
private val functionParameters = irFunction.explicitParameters
private val boundFunctionParameters = functionReference?.getArgumentsWithIr()?.map { it.first }
private val unboundFunctionParameters = boundFunctionParameters?.let { functionParameters - it }
private lateinit var suspendResult: IrVariable
private lateinit var suspendState: IrVariable
private lateinit var dataArgument: IrValueParameter
private lateinit var exceptionArgument: IrValueParameter
private lateinit var coroutineClassDescriptor: ClassDescriptorImpl
private lateinit var coroutineClass: IrClassImpl
private lateinit var coroutineClassThis: IrValueParameter
private lateinit var argumentToPropertiesMap: Map<ParameterDescriptor, IrField>
private val coroutineImplSymbol = symbols.coroutineImpl
private val coroutineImplConstructorSymbol = coroutineImplSymbol.constructors.single()
private val coroutineImplClassDescriptor = coroutineImplSymbol.descriptor
private val create1Function = coroutineImplSymbol.owner.simpleFunctions()
.single { it.name.asString() == "create" && it.valueParameters.size == 1 }
private val create1CompletionParameter = create1Function.valueParameters[0]
private val coroutineImplLabelFieldSymbol = coroutineImplSymbol.getPropertyField("label")!!
// private val coroutineImplResultFieldSymbol = coroutineImplSymbol.getPropertyField("pendingResult")!!
private val coroutineImplExceptionFieldSymbol = coroutineImplSymbol.getPropertyField("pendingException")!!
private val coroutineImplExceptionStateFieldSymbol = coroutineImplSymbol.getPropertyField("exceptionState")!!
private val coroutineConstructors = mutableListOf<IrConstructor>()
private var exceptionTrapId = -1
fun build(): BuiltCoroutine {
val superTypes = mutableListOf<IrType>(coroutineImplSymbol.owner.defaultType)
var suspendFunctionClass: IrClass? = null
var functionClass: IrClass? = null
var suspendFunctionClassTypeArguments: List<IrType>? = null
var functionClassTypeArguments: List<IrType>? = null
if (unboundFunctionParameters != null) {
// Suspend lambda inherits SuspendFunction.
val numberOfParameters = unboundFunctionParameters.size
suspendFunctionClass = context.suspendFunctions[numberOfParameters].owner
val unboundParameterTypes = unboundFunctionParameters.map { it.type }
suspendFunctionClassTypeArguments = unboundParameterTypes + irFunction.returnType
superTypes += suspendFunctionClass.typeWith(suspendFunctionClassTypeArguments)
functionClass = context.functions[numberOfParameters + 1].owner
val continuationType = continuationClassSymbol.typeWith(irFunction.returnType)
functionClassTypeArguments = unboundParameterTypes + continuationType + context.irBuiltIns.anyNType
superTypes += functionClass.typeWith(functionClassTypeArguments)
}
coroutineClassDescriptor = ClassDescriptorImpl(
/* containingDeclaration = */ irFunction.descriptor.containingDeclaration,
/* name = */ "${irFunction.descriptor.name}\$${coroutineId++}".synthesizedName,
/* modality = */ Modality.FINAL,
/* kind = */ ClassKind.CLASS,
/* superTypes = */ superTypes.map { it.toKotlinType() },
/* source = */ SourceElement.NO_SOURCE,
/* isExternal = */ false,
/* storageManager = */ LockBasedStorageManager.NO_LOCKS
).also {
it.initialize(stub("coroutine class"), stub("coroutine class constructors"), null)
}
coroutineClass = IrClassImpl(
startOffset = irFunction.startOffset,
endOffset = irFunction.endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
descriptor = coroutineClassDescriptor
)
coroutineClass.parent = irFunction.parent
coroutineClass.createParameterDeclarations()
coroutineClassThis = coroutineClass.thisReceiver!!
val overriddenMap = mutableMapOf<CallableMemberDescriptor, CallableMemberDescriptor>()
val constructors = mutableSetOf<ClassConstructorDescriptor>()
val coroutineConstructorBuilder = createConstructorBuilder()
constructors.add(coroutineConstructorBuilder.symbol.descriptor)
coroutineConstructorBuilder.initialize()
val doResumeFunction = coroutineImplSymbol.owner.simpleFunctions()
.single { it.name.asString() == "doResume" }
val doResumeMethodBuilder = createDoResumeMethodBuilder(doResumeFunction, coroutineClass)
doResumeMethodBuilder.initialize()
overriddenMap += doResumeFunction.descriptor to doResumeMethodBuilder.symbol.descriptor
var coroutineFactoryConstructorBuilder: SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>? = null
var createMethodBuilder: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>? = null
var invokeMethodBuilder: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>? = null
if (functionReference != null) {
// Suspend lambda - create factory methods.
coroutineFactoryConstructorBuilder = createFactoryConstructorBuilder(boundFunctionParameters!!)
constructors.add(coroutineFactoryConstructorBuilder.symbol.descriptor)
val createFunctionDescriptor = coroutineImplClassDescriptor.unsubstitutedMemberScope
.getContributedFunctions(Name.identifier("create"), NoLookupLocation.FROM_BACKEND)
.atMostOne { it.valueParameters.size == unboundFunctionParameters!!.size + 1 }
createMethodBuilder = createCreateMethodBuilder(
unboundArgs = unboundFunctionParameters!!,
superFunctionDescriptor = createFunctionDescriptor,
coroutineConstructor = coroutineConstructorBuilder.ir,
coroutineClass = coroutineClass)
createMethodBuilder.initialize()
if (createFunctionDescriptor != null)
overriddenMap += createFunctionDescriptor to createMethodBuilder.symbol.descriptor
val invokeFunctionDescriptor = functionClass!!.descriptor
.getFunction("invoke", functionClassTypeArguments!!.map { it.toKotlinType() })
val suspendInvokeFunctionDescriptor = suspendFunctionClass!!.descriptor
.getFunction("invoke", suspendFunctionClassTypeArguments!!.map { it.toKotlinType() })
invokeMethodBuilder = createInvokeMethodBuilder(
suspendFunctionInvokeFunctionDescriptor = suspendInvokeFunctionDescriptor,
functionInvokeFunctionDescriptor = invokeFunctionDescriptor,
createFunction = createMethodBuilder.ir,
doResumeFunction = doResumeMethodBuilder.ir,
coroutineClass = coroutineClass)
}
coroutineClass.addChild(coroutineConstructorBuilder.ir)
coroutineConstructors += coroutineConstructorBuilder.ir
coroutineFactoryConstructorBuilder?.let {
it.initialize()
coroutineClass.addChild(it.ir)
coroutineConstructors += it.ir
}
createMethodBuilder?.let {
coroutineClass.addChild(it.ir)
}
invokeMethodBuilder?.let {
it.initialize()
coroutineClass.addChild(it.ir)
}
coroutineClass.addChild(doResumeMethodBuilder.ir)
coroutineClass.setSuperSymbolsAndAddFakeOverrides(superTypes)
setupExceptionState()
return BuiltCoroutine(
coroutineClass = coroutineClass,
coroutineConstructor = coroutineFactoryConstructorBuilder?.ir
?: coroutineConstructorBuilder.ir,
doResumeFunction = doResumeMethodBuilder.ir
)
}
private fun createConstructorBuilder()
= object : SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
override fun buildSymbol() = IrConstructorSymbolImpl(
ClassConstructorDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* isPrimary = */ false,
/* source = */ SourceElement.NO_SOURCE
)
)
private lateinit var constructorParameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
constructorParameters = (
functionParameters
+ coroutineImplConstructorSymbol.owner.valueParameters[0] // completion.
).mapIndexed { index, parameter ->
val parameterDescriptor = parameter.descriptor.copyAsValueParameter(descriptor, index)
parameter.copy(parameterDescriptor)
}
descriptor.initialize(
constructorParameters.map { it.descriptor as ValueParameterDescriptor },
Visibilities.PUBLIC
)
descriptor.returnType = coroutineClassDescriptor.defaultType
}
override fun buildIr(): IrConstructor {
// Save all arguments to fields.
argumentToPropertiesMap = functionParameters.associate {
it.descriptor to addField(it.name, it.type, false)
}
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrConstructorImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
this.valueParameters += constructorParameters
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody {
val completionParameter = valueParameters.last()
+IrDelegatingConstructorCallImpl(startOffset, endOffset,
context.irBuiltIns.unitType,
coroutineImplConstructorSymbol, coroutineImplConstructorSymbol.descriptor).apply {
putValueArgument(0, irGet(completionParameter))
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClass.symbol, context.irBuiltIns.unitType)
functionParameters.forEachIndexed { index, parameter ->
+irSetField(
irGet(coroutineClassThis),
argumentToPropertiesMap[parameter.descriptor]!!,
irGet(valueParameters[index])
)
}
}
}
}
}
private fun createFactoryConstructorBuilder(boundParams: List<IrValueParameter>)
= object : SymbolWithIrBuilder<IrConstructorSymbol, IrConstructor>() {
override fun buildSymbol() = IrConstructorSymbolImpl(
ClassConstructorDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* isPrimary = */ false,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var constructorParameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as ClassConstructorDescriptorImpl
constructorParameters = boundParams.mapIndexed { index, parameter ->
val parameterDescriptor = parameter.descriptor.copyAsValueParameter(descriptor, index)
parameter.copy(parameterDescriptor)
}
descriptor.initialize(
constructorParameters.map { it.descriptor as ValueParameterDescriptor },
Visibilities.PUBLIC
)
descriptor.returnType = coroutineClassDescriptor.defaultType
}
override fun buildIr(): IrConstructor {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrConstructorImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
this.valueParameters += constructorParameters
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody {
+IrDelegatingConstructorCallImpl(startOffset, endOffset, context.irBuiltIns.unitType,
coroutineImplConstructorSymbol, coroutineImplConstructorSymbol.descriptor).apply {
putValueArgument(0, irNull()) // Completion.
}
+IrInstanceInitializerCallImpl(startOffset, endOffset, coroutineClass.symbol,
context.irBuiltIns.unitType)
// Save all arguments to fields.
boundParams.forEachIndexed { index, parameter ->
+irSetField(irGet(coroutineClassThis), argumentToPropertiesMap[parameter.descriptor]!!,
irGet(valueParameters[index]))
}
}
}
}
}
private fun createCreateMethodBuilder(unboundArgs: List<IrValueParameter>,
superFunctionDescriptor: FunctionDescriptor?,
coroutineConstructor: IrConstructor,
coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* name = */ Name.identifier("create"),
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var parameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
parameters = (
unboundArgs + create1CompletionParameter
).mapIndexed { index, parameter ->
parameter.copy(parameter.descriptor.copyAsValueParameter(descriptor, index))
}
descriptor.initialize(
/* receiverParameterType = */ null,
/* dispatchReceiverParameter = */ coroutineClassDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
/* unsubstitutedValueParameters = */ parameters.map { it.descriptor as ValueParameterDescriptor },
/* unsubstitutedReturnType = */ coroutineClassDescriptor.defaultType,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE).apply {
if (superFunctionDescriptor != null) {
overriddenDescriptors += superFunctionDescriptor.overriddenDescriptors
overriddenDescriptors += superFunctionDescriptor
}
}
}
override fun buildIr(): IrSimpleFunction {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = coroutineClass.defaultType
parent = coroutineClass
this.valueParameters += parameters
this.createDispatchReceiverParameter()
val thisReceiver = this.dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
irCall(coroutineConstructor).apply {
var unboundIndex = 0
val unboundArgsSet = unboundArgs.toSet()
functionParameters.map {
if (unboundArgsSet.contains(it))
irGet(valueParameters[unboundIndex++])
else
irGetField(irGet(thisReceiver), argumentToPropertiesMap[it.descriptor]!!)
}.forEachIndexed { index, argument ->
putValueArgument(index, argument)
}
putValueArgument(functionParameters.size, irGet(valueParameters[unboundIndex]))
assert(unboundIndex == valueParameters.size - 1,
{ "Not all arguments of <create> are used" })
})
}
}
}
}
private fun createInvokeMethodBuilder(suspendFunctionInvokeFunctionDescriptor: FunctionDescriptor,
functionInvokeFunctionDescriptor: FunctionDescriptor,
createFunction: IrFunction,
doResumeFunction: IrFunction,
coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
SimpleFunctionDescriptorImpl.create(
/* containingDeclaration = */ coroutineClassDescriptor,
/* annotations = */ Annotations.EMPTY,
/* name = */ Name.identifier("invoke"),
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE
)
)
lateinit var parameters: List<IrValueParameter>
override fun doInitialize() {
val descriptor = symbol.descriptor as SimpleFunctionDescriptorImpl
parameters = createFunction.valueParameters
// Skip completion - invoke() already has it implicitly as a suspend function.
.take(createFunction.valueParameters.size - 1)
.map { it.copy(it.descriptor.copyAsValueParameter(descriptor, it.index)) }
descriptor.initialize(
/* receiverParameterType = */ null,
/* dispatchReceiverParameter = */ coroutineClassDescriptor.thisAsReceiverParameter,
/* typeParameters = */ emptyList(),
/* unsubstitutedValueParameters = */ parameters.map { it.descriptor as ValueParameterDescriptor },
/* unsubstitutedReturnType = */ irFunction.descriptor.returnType,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE).apply {
overriddenDescriptors += suspendFunctionInvokeFunctionDescriptor
overriddenDescriptors += functionInvokeFunctionDescriptor
isSuspend = true
}
}
override fun buildIr(): IrSimpleFunction {
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
return IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = irFunction.returnType
parent = coroutineClass
valueParameters += parameters
this.createDispatchReceiverParameter()
val thisReceiver = this.dispatchReceiverParameter!!
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
body = irBuilder.irBlockBody(startOffset, endOffset) {
+irReturn(
irCall(doResumeFunction).apply {
dispatchReceiver = irCall(createFunction).apply {
dispatchReceiver = irGet(thisReceiver)
valueParameters.forEachIndexed { index, parameter ->
putValueArgument(index, irGet(parameter))
}
putValueArgument(valueParameters.size,
irCall(getContinuationSymbol, getContinuationSymbol.owner.returnType, listOf(returnType)))
}
putValueArgument(0, irGetObject(symbols.unit)) // value
putValueArgument(1, irNull()) // exception
}
)
}
}
}
}
private fun addField(name: Name, type: IrType, isMutable: Boolean): IrField = createField(
irFunction.startOffset,
irFunction.endOffset,
type,
name,
isMutable,
DECLARATION_ORIGIN_COROUTINE_IMPL,
coroutineClassDescriptor
).also {
coroutineClass.addChild(it)
}
private fun createDoResumeMethodBuilder(doResumeFunction: IrFunction, coroutineClass: IrClass)
= object: SymbolWithIrBuilder<IrSimpleFunctionSymbol, IrSimpleFunction>() {
override fun buildSymbol() = IrSimpleFunctionSymbolImpl(
doResumeFunction.descriptor.createOverriddenDescriptor(coroutineClassDescriptor)
)
override fun doInitialize() { }
override fun buildIr(): IrSimpleFunction {
val originalBody = irFunction.body!!
val startOffset = irFunction.startOffset
val endOffset = irFunction.endOffset
val function = IrFunctionImpl(
startOffset = startOffset,
endOffset = endOffset,
origin = DECLARATION_ORIGIN_COROUTINE_IMPL,
symbol = symbol).apply {
returnType = context.irBuiltIns.anyNType
parent = coroutineClass
this.createDispatchReceiverParameter()
doResumeFunction.valueParameters.mapIndexedTo(this.valueParameters) { index, it ->
it.copy(descriptor.valueParameters[index])
}
}
dataArgument = function.valueParameters[0]
exceptionArgument = function.valueParameters[1]
suspendResult = JsIrBuilder.buildVar(IrVariableSymbolImpl(
IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor,
name = "suspendResult".synthesizedName,
outType = context.builtIns.nullableAnyType,
isMutable = true
)
), JsIrBuilder.buildGetValue(dataArgument.symbol), context.irBuiltIns.anyNType)
suspendState = JsIrBuilder.buildVar(IrVariableSymbolImpl(
IrTemporaryVariableDescriptorImpl(
containingDeclaration = irFunction.descriptor,
name = "suspendState".synthesizedName,
outType = coroutineImplLabelFieldSymbol.owner.type.toKotlinType(),
isMutable = true
)
), type = coroutineImplLabelFieldSymbol.owner.type)
val body =
(originalBody as IrBlockBody).run {
IrBlockImpl(
startOffset,
endOffset,
context.irBuiltIns.unitType,
STATEMENT_ORIGIN_COROUTINE_IMPL,
statements
)
}
buildStateMachine(body, function)
return function
}
}
private fun setupExceptionState() {
for (it in coroutineConstructors) {
(it.body as? IrBlockBody)?.run {
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
statements += JsIrBuilder.buildSetField(coroutineImplExceptionStateFieldSymbol, receiver, id, context.irBuiltIns.unitType)
}
}
}
private fun buildStateMachine(body: IrBlock, function: IrFunction) {
val unit = context.irBuiltIns.unitType
val switch = IrWhenImpl(body.startOffset, body.endOffset, unit,
COROUTINE_SWITCH
)
val rootTry = IrTryImpl(body.startOffset, body.endOffset, unit).apply {
tryResult = switch
}
val rootLoop = IrDoWhileLoopImpl(
body.startOffset,
body.endOffset,
unit,
COROUTINE_ROOT_LOOP,
rootTry,
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
)
val suspendableNodes = mutableSetOf<IrElement>()
val loweredBody =
collectSuspendableNodes(body, suspendableNodes, context, function)
val thisReceiver = (function.dispatchReceiverParameter as IrValueParameter).symbol
val stateMachineBuilder = StateMachineBuilder(
suspendableNodes,
context,
function.symbol,
rootLoop,
coroutineImplExceptionFieldSymbol,
coroutineImplExceptionStateFieldSymbol,
coroutineImplLabelFieldSymbol,
thisReceiver,
suspendResult.symbol
)
loweredBody.acceptVoid(stateMachineBuilder)
stateMachineBuilder.finalizeStateMachine()
rootTry.catches += stateMachineBuilder.globalCatch
val visited = mutableSetOf<SuspendState>()
val sortedStates = DFS.topologicalOrder(listOf(stateMachineBuilder.entryState), { it.successors }, { visited.add(it) })
sortedStates.withIndex().forEach { it.value.id = it.index }
fun buildDispatch(target: SuspendState) = target.run {
assert(id >= 0)
JsIrBuilder.buildInt(context.irBuiltIns.intType, id)
}
val eqeqeqInt = context.irBuiltIns.eqeqeqSymbol
for (state in sortedStates) {
val condition = JsIrBuilder.buildCall(eqeqeqInt).apply {
putValueArgument(0, JsIrBuilder.buildGetField(coroutineImplLabelFieldSymbol, JsIrBuilder.buildGetValue(thisReceiver)))
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id))
}
switch.branches += IrBranchImpl(state.entryBlock.startOffset, state.entryBlock.endOffset, condition, state.entryBlock)
}
val irResultDeclaration = suspendResult
rootLoop.transform(DispatchPointTransformer(::buildDispatch), null)
exceptionTrapId = stateMachineBuilder.rootExceptionTrap.id
val functionBody = IrBlockBodyImpl(function.startOffset, function.endOffset, listOf(irResultDeclaration, rootLoop))
function.body = functionBody
val liveLocals = computeLivenessAtSuspensionPoints(functionBody).values.flatten().toSet()
val localToPropertyMap = mutableMapOf<IrValueSymbol, IrFieldSymbol>()
var localCounter = 0
// TODO: optimize by using the same property for different locals.
liveLocals.forEach {
if (it != suspendState && it != suspendResult) {
localToPropertyMap.getOrPut(it.symbol) {
addField(Name.identifier("${it.name}${localCounter++}"), it.type, (it as? IrVariable)?.isVar ?: false).symbol
}
}
}
irFunction.explicitParameters.forEach {
localToPropertyMap.getOrPut(it.symbol) {
argumentToPropertiesMap.getValue(it.descriptor).symbol
}
}
function.transform(
LiveLocalsTransformer(
localToPropertyMap,
JsIrBuilder.buildGetValue(
thisReceiver
),
unit
), null)
}
private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> {
// TODO: data flow analysis.
// Just save all visible for now.
val result = mutableMapOf<IrCall, List<IrValueDeclaration>>()
body.acceptChildrenVoid(object : VariablesScopeTracker() {
override fun visitCall(expression: IrCall) {
if (!expression.descriptor.isSuspend) return super.visitCall(expression)
expression.acceptChildrenVoid(this)
val visibleVariables = mutableListOf<IrValueDeclaration>()
scopeStack.forEach { visibleVariables += it }
result.put(expression, visibleVariables)
}
})
return result
}
}
private open class VariablesScopeTracker: IrElementVisitorVoid {
protected val scopeStack = mutableListOf<MutableSet<IrVariable>>(mutableSetOf())
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitContainerExpression(expression: IrContainerExpression) {
if (!expression.isTransparentScope)
scopeStack.push(mutableSetOf())
super.visitContainerExpression(expression)
if (!expression.isTransparentScope)
scopeStack.pop()
}
override fun visitCatch(aCatch: IrCatch) {
scopeStack.push(mutableSetOf())
super.visitCatch(aCatch)
scopeStack.pop()
}
override fun visitVariable(declaration: IrVariable) {
super.visitVariable(declaration)
scopeStack.peek()!!.add(declaration)
}
}
}
@@ -0,0 +1,174 @@
/*
* 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.coroutines
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.common.push
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.ir.JsIrBuilder
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.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.visitors.*
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
object COROUTINE_SWITCH : IrStatementOriginImpl("COROUTINE_SWITCH")
open class SuspendableNodesCollector(protected val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
protected var hasSuspendableChildren = false
override fun visitElement(element: IrElement) {
val current = hasSuspendableChildren
hasSuspendableChildren = false
element.acceptChildrenVoid(this)
if (hasSuspendableChildren) {
suspendableNodes += element
}
hasSuspendableChildren = hasSuspendableChildren || current
}
override fun visitCall(expression: IrCall) {
super.visitCall(expression)
if (expression.descriptor.isSuspend) {
suspendableNodes += expression
hasSuspendableChildren = true
}
}
}
fun collectSuspendableNodes(
body: IrBlock,
suspendableNodes: MutableSet<IrElement>,
context: JsIrBackendContext,
function: IrFunction
): IrBlock {
// 1st: mark suspendable loops and tries
body.acceptVoid(SuspendableNodesCollector(suspendableNodes))
// 2nd: mark inner terminators
val terminatorsCollector = SuspendedTerminatorsCollector(suspendableNodes)
body.acceptVoid(terminatorsCollector)
if (terminatorsCollector.shouldFinalliesBeLowered) {
val finallyLower = FinallyBlocksLowering(context, context.dynamicType)
function.body = IrBlockBodyImpl(body.startOffset, body.endOffset, body.statements)
function.transform(finallyLower, null)
val newBody = function.body as IrBlockBody
function.body = null
suspendableNodes.clear()
val newBlock = JsIrBuilder.buildBlock(body.type, newBody.statements)
return collectSuspendableNodes(newBlock, suspendableNodes, context, function)
}
return body
}
class SuspendedTerminatorsCollector(suspendableNodes: MutableSet<IrElement>) : SuspendableNodesCollector(suspendableNodes) {
var shouldFinalliesBeLowered = false
override fun visitBreakContinue(jump: IrBreakContinue) {
if (jump.loop in suspendableNodes) {
suspendableNodes.add(jump)
hasSuspendableChildren = true
}
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
}
private val tryStack = mutableListOf<IrTry>()
private val tryLoopStack = mutableListOf<IrStatement>()
private fun pushTry(aTry: IrTry) {
tryStack.push(aTry)
tryLoopStack.push(aTry)
}
private fun popTry() {
tryLoopStack.pop()
tryStack.pop()
}
private fun pushLoop(loop: IrLoop) {
tryLoopStack.push(loop)
}
private fun popLoop() {
tryLoopStack.pop()
}
override fun visitLoop(loop: IrLoop) {
pushLoop(loop)
super.visitLoop(loop)
popLoop()
}
override fun visitTry(aTry: IrTry) {
pushTry(aTry)
super.visitTry(aTry)
popTry()
}
override fun visitReturn(expression: IrReturn) {
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
super.visitReturn(expression)
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
suspendableNodes.add(expression)
hasSuspendableChildren = true
}
}
}
class LiveLocalsTransformer(
private val localMap: Map<IrValueSymbol, IrFieldSymbol>,
private val receiver: IrExpression,
private val unitType: IrType
) :
IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val field = localMap[expression.symbol] ?: return expression
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver, origin) }
}
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
expression.transformChildrenVoid(this)
val field = localMap[expression.symbol] ?: return expression
return expression.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, value, unitType, origin) }
}
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid(this)
val field = localMap[declaration.symbol] ?: return declaration
val initializer = declaration.initializer
return if (initializer != null) {
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver, initializer, unitType) }
} else {
JsIrBuilder.buildComposite(declaration.type)
}
}
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> { class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsExpression, JsGenerationContext> {
override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression { override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression {
@@ -158,7 +159,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
val arguments = translateCallArguments(expression, context) val arguments = translateCallArguments(expression, context)
if (dispatchReceiver != null && if (dispatchReceiver != null &&
dispatchReceiver.type.isFunctionTypeOrSubtype() && symbol.owner.name == OperatorNameConventions.INVOKE dispatchReceiver.type.isFunctionTypeOrSubtype() && symbol.descriptor.name == OperatorNameConventions.INVOKE && !symbol.descriptor.isSuspend
) { ) {
return JsInvocation(jsDispatchReceiver!!, arguments) return JsInvocation(jsDispatchReceiver!!, arguments)
} }
@@ -6,12 +6,14 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.COROUTINE_SWITCH
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.constructedClass import org.jetbrains.kotlin.ir.backend.js.utils.constructedClass
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> { class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsStatement, JsGenerationContext> {
override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement { override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement {
@@ -81,9 +83,40 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
} }
override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsStatement { override fun visitWhen(expression: IrWhen, context: JsGenerationContext): JsStatement {
if (expression.origin == COROUTINE_SWITCH) return toSwitch(expression, context)
return expression.toJsNode(this, context, ::JsIf) ?: JsEmpty return expression.toJsNode(this, context, ::JsIf) ?: JsEmpty
} }
private fun toSwitch(expression: IrWhen, context: JsGenerationContext): JsStatement {
var expr: IrExpression? = null
val cases = expression.branches.map {
val body = it.result
val id = if (it is IrElseBranch) null else {
val call = it.condition as IrCall
expr = call.getValueArgument(0) as IrExpression
call.getValueArgument(1)
}
Pair(id, body)
}
val exprTransformer = IrElementToJsExpressionTransformer()
val jsExpr = expr!!.accept(exprTransformer, context)
return JsSwitch(jsExpr, cases.map { (id, body) ->
val jsId = id?.accept(exprTransformer, context)
val jsBody = body.accept(this, context).asBlock()
val case: JsSwitchMember
if (jsId == null) {
case = JsDefault()
} else {
case = JsCase().also { it.caseExpression = jsId }
}
case.also { it.statements += jsBody.statements }
})
}
override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement { override fun visitWhileLoop(loop: IrWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null? //TODO what if body null?
val label = context.getNameForLoop(loop) val label = context.getNameForLoop(loop)
@@ -94,7 +127,8 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer<JsSta
override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement { override fun visitDoWhileLoop(loop: IrDoWhileLoop, context: JsGenerationContext): JsStatement {
//TODO what if body null? //TODO what if body null?
val label = context.getNameForLoop(loop) val label = context.getNameForLoop(loop)
val loopStatement = JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context)) val loopStatement =
JsDoWhile(loop.condition.accept(IrElementToJsExpressionTransformer(), context), loop.body?.accept(this, context))
return label?.let { JsLabel(it, loopStatement) } ?: loopStatement return label?.let { JsLabel(it, loopStatement) } ?: loopStatement
} }
} }
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.onlyIf import org.jetbrains.kotlin.backend.common.onlyIf
import org.jetbrains.kotlin.backend.common.utils.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isAny import org.jetbrains.kotlin.ir.types.isAny
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.isReal import org.jetbrains.kotlin.ir.util.isReal
import org.jetbrains.kotlin.ir.util.resolveFakeOverride import org.jetbrains.kotlin.ir.util.resolveFakeOverride
@@ -166,7 +168,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
irClass.superTypes.mapNotNull { irClass.superTypes.mapNotNull {
val symbol = it.classifierOrFail val symbol = it.classifierOrFail
// TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal // TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal
if (symbol.isInterface && !symbol.isEffectivelyExternal()) JsNameRef(context.getNameForSymbol(symbol)) else null if (symbol.isInterface && !irClass.defaultType.isBuiltinFunctionalTypeOrSubtype() && !symbol.isEffectivelyExternal()) JsNameRef(context.getNameForSymbol(symbol)) else null
} }
) )
) )
@@ -132,6 +132,17 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
val value = args[2] val value = args[2]
JsInvocation(JsNameRef(Namer.KPROPERTY_SET, reference), listOf(receiver, value)) JsInvocation(JsNameRef(Namer.KPROPERTY_SET, reference), listOf(receiver, value))
} }
add(intrinsics.jsGetContinuation) { _, context: JsGenerationContext ->
context.continuation
}
add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext ->
val contextGetter = backendContext.coroutineGetContext
val getterName = context.getNameForSymbol(contextGetter)
val continuation = context.continuation
JsInvocation(JsNameRef(getterName, continuation))
}
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs package org.jetbrains.kotlin.ir.backend.js.transformers.irToJs
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext import org.jetbrains.kotlin.ir.backend.js.utils.JsGenerationContext
import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.Namer
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
@@ -49,6 +50,9 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForSymbol(it.symbol)) } declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForSymbol(it.symbol)) }
functionParams.forEach { function.addParameter(it) } functionParams.forEach { function.addParameter(it) }
if (declaration.descriptor.isSuspend) {
function.addParameter(context.currentScope.declareName(Namer.CONTINUATION))
}
return function return function
} }
@@ -57,11 +61,15 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene
val transformer = IrElementToJsExpressionTransformer() val transformer = IrElementToJsExpressionTransformer()
val size = expression.valueArgumentsCount val size = expression.valueArgumentsCount
return (0 until size).mapTo(ArrayList(size)) { val arguments = (0 until size).mapTo(ArrayList(size)) {
val argument = expression.getValueArgument(it) val argument = expression.getValueArgument(it)
val result = argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) val result = argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1))
result result
} }
return if (expression.descriptor.isSuspend) {
arguments + context.continuation
} else arguments
} }
val IrFunction.isStatic: Boolean get() = this.dispatchReceiverParameter == null val IrFunction.isStatic: Boolean get() = this.dispatchReceiverParameter == null
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
@@ -44,4 +45,20 @@ class JsGenerationContext {
fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, this) fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, this)
fun getNameForLoop(loop: IrLoop): JsName? = staticContext.getNameForLoop(loop, this) fun getNameForLoop(loop: IrLoop): JsName? = staticContext.getNameForLoop(loop, this)
val continuation
get() = if (isCoroutineDoResume()) {
JsThisRef()
} else {
if (currentFunction!!.descriptor.isSuspend) {
JsNameRef(currentScope.declareName(Namer.CONTINUATION))
} else {
getNameForSymbol(currentFunction.valueParameters.last().symbol).makeRef()
}
}
private fun isCoroutineDoResume(): Boolean {
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
return staticContext.doResumeFunctionSymbol in overriddenSymbols
}
} }
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.ir.backend.js.utils
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIntrinsicTransformers
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.expressions.IrLoop
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.js.backend.ast.JsClassModel import org.jetbrains.kotlin.js.backend.ast.JsClassModel
@@ -24,6 +25,9 @@ class JsStaticContext(
val intrinsics = JsIntrinsicTransformers(backendContext) val intrinsics = JsIntrinsicTransformers(backendContext)
// TODO: use IrSymbol instead of JsName // TODO: use IrSymbol instead of JsName
val classModels = mutableMapOf<JsName, JsClassModel>() val classModels = mutableMapOf<JsName, JsClassModel>()
val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner
val doResumeFunctionSymbol = coroutineImplDeclaration.declarations
.filterIsInstance<IrSimpleFunction>().single { it.name.asString() == "doResume" }.symbol
val initializerBlock = JsGlobalBlock() val initializerBlock = JsGlobalBlock()
@@ -60,6 +60,8 @@ object Namer {
val DEFINE_INLINE_FUNCTION = "defineInlineFunction" val DEFINE_INLINE_FUNCTION = "defineInlineFunction"
val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default" val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default"
val CONTINUATION = "\$cont"
val JS_ERROR = JsNameRef("Error") val JS_ERROR = JsNameRef("Error")
val JS_OBJECT = JsNameRef("Object") val JS_OBJECT = JsNameRef("Object")
@@ -60,7 +60,7 @@ class SimpleNameGenerator : NameGenerator {
nameDeclarator = context.currentScope::declareFreshName nameDeclarator = context.currentScope::declareFreshName
} }
is PropertyDescriptor -> { is PropertyDescriptor -> {
nameBuilder.append(descriptor.name.identifier) nameBuilder.append(descriptor.name.asString())
if (descriptor.visibility == Visibilities.PRIVATE || descriptor.modality != Modality.FINAL) { if (descriptor.visibility == Visibilities.PRIVATE || descriptor.modality != Modality.FINAL) {
nameBuilder.append('$') nameBuilder.append('$')
nameBuilder.append(getNameForDescriptor(descriptor.containingDeclaration, context)) nameBuilder.append(getNameForDescriptor(descriptor.containingDeclaration, context))
@@ -183,8 +183,12 @@ fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression, getterSymbol:
origin = IrStatementOrigin.GET_PROPERTY origin = IrStatementOrigin.GET_PROPERTY
) )
fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType): IrCall = fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType, typeArguments: List<IrType> = emptyList()): IrCall =
IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor) IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor).apply {
typeArguments.forEachIndexed { index, irType ->
this.putTypeArgument(index, irType)
}
}
fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrCall = fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrCall =
IrCallImpl(startOffset, endOffset, callee.owner.returnType, callee, callee.descriptor) IrCallImpl(startOffset, endOffset, callee.owner.returnType, callee, callee.descriptor)
@@ -237,3 +241,14 @@ fun IrBuilderWithScope.irString(value: String) =
fun IrBuilderWithScope.irConcat() = fun IrBuilderWithScope.irConcat() =
IrStringConcatenationImpl(startOffset, endOffset, context.irBuiltIns.stringType) IrStringConcatenationImpl(startOffset, endOffset, context.irBuiltIns.stringType)
fun IrBuilderWithScope.irSetField(receiver: IrExpression, irField: IrField, value: IrExpression): IrExpression =
IrSetFieldImpl(
startOffset,
endOffset,
irField.symbol,
receiver = receiver,
value = value,
type = context.irBuiltIns.unitType
)
@@ -19,12 +19,13 @@ package org.jetbrains.kotlin.ir.builders
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.expressions.IrBlock
import org.jetbrains.kotlin.ir.expressions.IrBlockBody import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrContainerExpression
import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import java.util.* import java.util.*
@@ -84,12 +85,13 @@ class IrBlockBuilder(
startOffset: Int, startOffset: Int,
endOffset: Int, endOffset: Int,
val origin: IrStatementOrigin? = null, val origin: IrStatementOrigin? = null,
var resultType: IrType? = null var resultType: IrType? = null,
) : IrStatementsBuilder<IrBlock>(context, scope, startOffset, endOffset) { val isTransparent:Boolean = false
) : IrStatementsBuilder<IrContainerExpression>(context, scope, startOffset, endOffset) {
private val statements = ArrayList<IrStatement>() private val statements = ArrayList<IrStatement>()
inline fun block(body: IrBlockBuilder.() -> Unit): IrBlock { inline fun block(body: IrBlockBuilder.() -> Unit): IrContainerExpression {
body() body()
return doBuild() return doBuild()
} }
@@ -98,11 +100,11 @@ class IrBlockBuilder(
statements.add(irStatement) statements.add(irStatement)
} }
override fun doBuild(): IrBlock { override fun doBuild(): IrContainerExpression {
val resultType = this.resultType val resultType = this.resultType
?: statements.lastOrNull().safeAs<IrExpression>()?.type ?: statements.lastOrNull().safeAs<IrExpression>()?.type
?: context.irBuiltIns.unitType ?: context.irBuiltIns.unitType
val irBlock = IrBlockImpl(startOffset, endOffset, resultType, origin) val irBlock = if (isTransparent) IrCompositeImpl(startOffset, endOffset, resultType, origin) else IrBlockImpl(startOffset, endOffset, resultType, origin)
irBlock.statements.addAll(statements) irBlock.statements.addAll(statements)
return irBlock return irBlock
} }
@@ -154,6 +156,20 @@ inline fun IrGeneratorWithScope.irBlock(
origin, resultType origin, resultType
).block(body) ).block(body)
inline fun IrGeneratorWithScope.irComposite(
startOffset: Int = UNDEFINED_OFFSET,
endOffset: Int = UNDEFINED_OFFSET,
origin: IrStatementOrigin? = null,
resultType: IrType? = null,
body: IrBlockBuilder.() -> Unit
): IrExpression =
IrBlockBuilder(
context, scope,
startOffset,
endOffset,
origin, resultType, true
).block(body)
inline fun IrGeneratorWithScope.irBlockBody( inline fun IrGeneratorWithScope.irBlockBody(
startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET, startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET,
body: IrBlockBodyBuilder.() -> Unit body: IrBlockBodyBuilder.() -> Unit
@@ -8,6 +8,9 @@ package org.jetbrains.kotlin.ir.types
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl
@@ -75,6 +78,17 @@ fun IrType.toKotlinType(): KotlinType {
} }
} }
fun IrType.getClass(): IrClass? =
(this.classifierOrNull as? IrClassSymbol)?.owner
fun IrClassSymbol.createType(hasQuestionMark: Boolean, arguments: List<IrTypeArgument>): IrSimpleType =
IrSimpleTypeImpl(
this,
hasQuestionMark,
arguments,
emptyList()
)
private fun makeKotlinType( private fun makeKotlinType(
classifier: IrClassifierSymbol, classifier: IrClassifierSymbol,
arguments: List<IrTypeArgument>, arguments: List<IrTypeArgument>,
@@ -95,6 +109,20 @@ fun ClassifierDescriptor.toIrType(hasQuestionMark: Boolean = false): IrType {
return IrSimpleTypeImpl(defaultType, symbol, hasQuestionMark, listOf(), listOf()) return IrSimpleTypeImpl(defaultType, symbol, hasQuestionMark, listOf(), listOf())
} }
val IrTypeParameter.defaultType: IrType get() = symbol.owner.defaultType
fun IrClassifierSymbol.typeWith(vararg arguments: IrType): IrSimpleType = typeWith(arguments.toList())
fun IrClassifierSymbol.typeWith(arguments: List<IrType>): IrSimpleType =
IrSimpleTypeImpl(
this,
false,
arguments.map { makeTypeProjection(it, Variance.INVARIANT) },
emptyList()
)
fun IrClass.typeWith(arguments: List<IrType>) = this.symbol.typeWith(arguments)
fun KotlinType.toIrType(): IrType? { fun KotlinType.toIrType(): IrType? {
if (isDynamic()) return IrDynamicTypeImpl(this, listOf(), Variance.INVARIANT) if (isDynamic()) return IrDynamicTypeImpl(this, listOf(), Variance.INVARIANT)
@@ -113,6 +141,7 @@ fun KotlinType.toIrType(): IrType? {
return IrSimpleTypeImpl(this, symbol, isMarkedNullable, arguments, annotations) return IrSimpleTypeImpl(this, symbol, isMarkedNullable, arguments, annotations)
} }
// TODO: this function creates unbound symbol which is the great source of problems
private fun ClassifierDescriptor.getSymbol(): IrClassifierSymbol = when (this) { private fun ClassifierDescriptor.getSymbol(): IrClassifierSymbol = when (this) {
is ClassDescriptor -> IrClassSymbolImpl(this) is ClassDescriptor -> IrClassSymbolImpl(this)
is TypeParameterDescriptor -> IrTypeParameterSymbolImpl(this) is TypeParameterDescriptor -> IrTypeParameterSymbolImpl(this)
@@ -6,20 +6,30 @@
package org.jetbrains.kotlin.ir.util package org.jetbrains.kotlin.ir.util
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrTypeParameterImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toIrType import org.jetbrains.kotlin.ir.types.toIrType
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.types.KotlinType
/** /**
* Binds the arguments explicitly represented in the IR to the parameters of the accessed function. * Binds the arguments explicitly represented in the IR to the parameters of the accessed function.
@@ -168,6 +178,21 @@ fun IrFunction.createParameterDeclarations() {
} }
} }
fun IrClass.createParameterDeclarations() {
thisReceiver = IrValueParameterImpl(
startOffset, endOffset,
IrDeclarationOrigin.INSTANCE_RECEIVER,
descriptor.thisAsReceiverParameter,
this.symbol.typeWith(this.typeParameters.map { it.defaultType }),
null
).also { valueParameter ->
valueParameter.parent = this
}
assert(typeParameters.isEmpty())
assert(descriptor.declaredTypeParameters.isEmpty())
}
//fun IrClass.createParameterDeclarations() { //fun IrClass.createParameterDeclarations() {
// descriptor.thisAsReceiverParameter.let { // descriptor.thisAsReceiverParameter.let {
// thisReceiver = IrValueParameterImpl( // thisReceiver = IrValueParameterImpl(
@@ -311,6 +336,40 @@ fun IrValueParameter.copy(newDescriptor: ParameterDescriptor): IrValueParameter
) )
} }
fun createField(
startOffset: Int,
endOffset: Int,
type: IrType,
name: Name,
isMutable: Boolean,
origin: IrDeclarationOrigin,
owner: ClassDescriptor
): IrField {
val descriptor = PropertyDescriptorImpl.create(
/* containingDeclaration = */ owner,
/* annotations = */ Annotations.EMPTY,
/* modality = */ Modality.FINAL,
/* visibility = */ Visibilities.PRIVATE,
/* isVar = */ isMutable,
/* name = */ name,
/* kind = */ CallableMemberDescriptor.Kind.DECLARATION,
/* source = */ SourceElement.NO_SOURCE,
/* lateInit = */ false,
/* isConst = */ false,
/* isExpect = */ false,
/* isActual = */ false,
/* isExternal = */ false,
/* isDelegated = */ false
).apply {
initialize(null, null)
val receiverType: KotlinType? = null
setType(type.toKotlinType(), emptyList(), owner.thisAsReceiverParameter, receiverType)
}
return IrFieldImpl(startOffset, endOffset, origin, descriptor, type)
}
fun IrFunction.createDispatchReceiverParameter() { fun IrFunction.createDispatchReceiverParameter() {
assert(this.dispatchReceiverParameter == null) assert(this.dispatchReceiverParameter == null)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
class A { class A {
inner class Inner(val result: Int) inner class Inner(val result: Int)
} }
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
private fun <T> upcast(value: T): T = value private fun <T> upcast(value: T): T = value
fun box(): String { fun box(): String {
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_COROUTINES // WITH_COROUTINES
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -6,7 +6,6 @@
import helpers.* import helpers.*
import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.* import COROUTINES_PACKAGE.intrinsics.*
import kotlin.test.assertEquals
class A(val w: String) { class A(val w: String) {
suspend fun String.ext(): String = suspendCoroutineUninterceptedOrReturn { suspend fun String.ext(): String = suspendCoroutineUninterceptedOrReturn {
@@ -6,7 +6,6 @@
import helpers.* import helpers.*
import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.* import COROUTINES_PACKAGE.intrinsics.*
import kotlin.test.assertEquals
class A(val w: String) { class A(val w: String) {
suspend fun Long.ext(): String = suspendCoroutineUninterceptedOrReturn { suspend fun Long.ext(): String = suspendCoroutineUninterceptedOrReturn {
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS_IR, JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_COROUTINES // WITH_COROUTINES
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -3,6 +3,7 @@
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
// DONT_RUN_GENERATED_CODE: JS_IR
import helpers.* import helpers.*
import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.*
import COROUTINES_PACKAGE.intrinsics.* import COROUTINES_PACKAGE.intrinsics.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.assertEquals import kotlin.test.assertEquals
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
import kotlin.test.* import kotlin.test.*
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects // !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: common.kt // FILE: common.kt
@@ -1,6 +1,5 @@
// !LANGUAGE: +MultiPlatformProjects // !LANGUAGE: +MultiPlatformProjects
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
// FILE: common.kt // FILE: common.kt

Some files were not shown because too many files have changed in this diff Show More