diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt index d3536131f86..c032d33f113 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/Ir.kt @@ -44,7 +44,7 @@ abstract class Symbols(val context: T, private val return initializer() } - val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) } +// val refClass = calc { symbolTable.referenceClass(context.getInternalClass("Ref")) } //abstract val areEqualByValue: List diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FinallyBlocksLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FinallyBlocksLowering.kt new file mode 100644 index 00000000000..047cb1e0f89 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/FinallyBlocksLowering.kt @@ -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() + } + + private val scopeStack = mutableListOf() + + private inline fun 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() + .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, + 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 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 + ) +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt index 11dc51f2bf9..b70621b9a29 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt @@ -247,6 +247,31 @@ class InnerClassConstructorCallsLowering(val context: BackendContext) : BodyLowe 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? }) } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt index fde30783c33..5cceb311f03 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LowerUtils.kt @@ -88,6 +88,13 @@ inline fun IrGeneratorWithScope.irBlock( ) = 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) = this.irBlockBody(irElement.startOffset, irElement.endOffset, body) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/utils/kotlinTypeBasedUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/utils/kotlinTypeBasedUtils.kt index a77cc0d630e..b832970d9f7 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/utils/kotlinTypeBasedUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/utils/kotlinTypeBasedUtils.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.common.utils import org.jetbrains.kotlin.backend.common.descriptors.isFunctionOrKFunctionType import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.IrType @@ -36,3 +37,5 @@ fun List.commonSupertype() = CommonSupertypes.commonSupertype(map(IrType fun IrType.isSubtypeOf(superType: IrType) = toKotlinType().isSubtypeOf(superType.toKotlinType()) fun IrType.isSubtypeOfClass(superClass: IrClassSymbol) = DescriptorUtils.isSubtypeOfClass(toKotlinType(), superClass.descriptor) + +fun IrType.isBuiltinFunctionalTypeOrSubtype() = toKotlinType().isBuiltinFunctionalTypeOrSubtype \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrBackendUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrBackendUtils.kt index 7c07ea88404..a2f464d92a9 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrBackendUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrBackendUtils.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.ir.util import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.ir.declarations.IrProperty 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.name.Name @@ -31,3 +32,6 @@ fun IrClassSymbol.getPropertyGetter(name: String): IrFunctionSymbol? = fun IrClassSymbol.getPropertySetter(name: String): IrFunctionSymbol? = this.getPropertyDeclaration(name)?.setter?.symbol + +fun IrClassSymbol.getPropertyField(name: String): IrFieldSymbol? = + this.getPropertyDeclaration(name)?.backingField?.symbol diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 4fd5e9e88e7..9994275ce83 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -99,7 +99,6 @@ class JsIntrinsics( val jsInstanceOf = binOpBool("jsInstanceOf") val jsTypeOf = unOp("jsTypeOf", irBuiltIns.string) - // Number conversions: val jsNumberToByte = getInternalFunction("numberToByte") @@ -132,6 +131,14 @@ class JsIntrinsics( val jsCompareTo = getInternalFunction("compareTo") 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 jsNumberRangeToLong = getInternalFunction("numberRangeToLong") diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index a9d66f755df..1b1e448ad2a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -13,20 +13,28 @@ import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.js.JsDescriptorsFactory import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.backend.js.lower.inline.ModuleIndex 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.symbols.* -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.toKotlinType +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrEnumEntrySymbol +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.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.createDynamicType class JsIrBackendContext( val module: ModuleDescriptor, @@ -48,28 +56,75 @@ class JsIrBackendContext( private val internalPackageName = FqName("kotlin.js") 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("") + + 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() + + 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) 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() - val enumEntryToGetInstanceFunction = mutableMapOf() + val kFunctions by lazy { + (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) + fun getOperatorByName(name: Name, type: KotlinType) = operatorMap[name]?.get(type) + override val ir = object : Ir(this, irModuleFragment) { override val symbols = object : Symbols(this@JsIrBackendContext, symbolTable.lazyWrapper) { override fun calc(initializer: () -> IrClassSymbol): IrClassSymbol { + val v = lazy { initializer() } return object : IrClassSymbol { - override val owner: IrClass get() = TODO("not implemented") - override val isBound: Boolean get() = TODO("not implemented") - override fun bind(owner: IrClass) = TODO("not implemented") - override val descriptor: ClassDescriptor get() = TODO("not implemented") + override val owner: IrClass get() = v.value.owner + override val isBound: Boolean get() = v.value.isBound + override fun bind(owner: IrClass) = v.value.bind(owner) + override val descriptor: ClassDescriptor get() = v.value.descriptor } } @@ -97,10 +152,10 @@ class JsIrBackendContext( get() = TODO("not implemented") override val copyRangeTo: Map get() = TODO("not implemented") - override val coroutineImpl: IrClassSymbol - get() = TODO("not implemented") - override val coroutineSuspendedGetter: IrSimpleFunctionSymbol - get() = TODO("not implemented") + override val coroutineImpl = symbolTable.referenceClass(getInternalClass(COROUTINE_IMPL_NAME.identifier)) + override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction( + coroutineIntrinsicsPackage.memberScope.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND).single().getter!! + ) } override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true @@ -132,6 +187,8 @@ class JsIrBackendContext( 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) { /*TODO*/ print(message()) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index c3ed48a827a..0e487e7cdbe 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -6,13 +6,18 @@ package org.jetbrains.kotlin.ir.backend.js 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.runOnFilePostfix import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.descriptors.ModuleDescriptor 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.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment @@ -59,9 +64,7 @@ fun compile( context.performInlining(moduleFragment) - context.lower(moduleFragment.files) - val transformer = SecondaryCtorLowering.CallsiteRedirectionTransformer(context) - moduleFragment.files.forEach { it.accept(transformer, null) } + context.lower(moduleFragment) val program = moduleFragment.accept(IrModuleToJsTransformer(context), null) @@ -80,30 +83,32 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment) } } -private fun JsIrBackendContext.lower(files: List) { - LateinitLowering(this, true).lower(files) - DefaultArgumentStubGenerator(this).runOnFilePostfix(files) - DefaultParameterInjector(this).runOnFilePostfix(files) - SharedVariablesLowering(this).runOnFilePostfix(files) - EnumClassLowering(this).runOnFilePostfix(files) - EnumUsageLowering(this).lower(files) - ReturnableBlockLowering(this).lower(files) - LocalDeclarationsLowering(this).runOnFilePostfix(files) - InnerClassesLowering(this).runOnFilePostfix(files) - InnerClassConstructorCallsLowering(this).runOnFilePostfix(files) - PropertiesLowering().lower(files) - InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false).runOnFilePostfix(files) - MultipleCatchesLowering(this).lower(files) - BridgesConstruction(this).runOnFilePostfix(files) - TypeOperatorLowering(this).lower(files) - BlockDecomposerLowering(this).runOnFilePostfix(files) - SecondaryCtorLowering(this).runOnFilePostfix(files) - CallableReferenceLowering(this).lower(files) - IntrinsicifyCallsLowering(this).lower(files) +private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment) { + moduleFragment.files.forEach(LateinitLowering(this, true)::lower) + moduleFragment.files.forEach(DefaultArgumentStubGenerator(this)::runOnFilePostfix) + moduleFragment.files.forEach(DefaultParameterInjector(this)::runOnFilePostfix) + moduleFragment.files.forEach(SharedVariablesLowering(this)::runOnFilePostfix) + moduleFragment.files.forEach(EnumClassLowering(this)::runOnFilePostfix) + moduleFragment.files.forEach(EnumUsageLowering(this)::lower) + moduleFragment.files.forEach(ReturnableBlockLowering(this)::lower) + moduleFragment.files.forEach(LocalDeclarationsLowering(this)::runOnFilePostfix) + moduleFragment.files.forEach(InnerClassesLowering(this)::runOnFilePostfix) + moduleFragment.files.forEach(InnerClassConstructorCallsLowering(this)::runOnFilePostfix) + moduleFragment.files.forEach(SuspendFunctionsLowering(this)::lower) + moduleFragment.files.forEach(PropertiesLowering()::lower) + moduleFragment.files.forEach(InitializersLowering(this, JsLoweredDeclarationOrigin.CLASS_STATIC_INITIALIZER, false)::runOnFilePostfix) + moduleFragment.files.forEach(MultipleCatchesLowering(this)::lower) + moduleFragment.files.forEach(BridgesConstruction(this)::runOnFilePostfix) + moduleFragment.files.forEach(TypeOperatorLowering(this)::lower) + moduleFragment.files.forEach(BlockDecomposerLowering(this)::runOnFilePostfix) + val sctor = SecondaryCtorLowering(this) + 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) = files.forEach { lower(it) } -private fun DeclarationContainerLoweringPass.runOnFilePostfix(files: List) = files.forEach { runOnFilePostfix(it) } -private fun BodyLoweringPass.runOnFilePostfix(files: List) = files.forEach { runOnFilePostfix(it) } -private fun FunctionLoweringPass.runOnFilePostfix(files: List) = files.forEach { runOnFilePostfix(it) } -private fun ClassLoweringPass.runOnFilePostfix(files: List) = files.forEach { runOnFilePostfix(it) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt index c289d7f1fc3..94e8624ca5b 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrArithBuilder.kt @@ -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.utils.OperatorNames import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.name.Name class JsIrArithBuilder(val context: JsIrBackendContext) { @@ -15,15 +16,15 @@ class JsIrArithBuilder(val context: JsIrBackendContext) { val symbols = context.ir.symbols private fun buildBinaryOperator(name: Name, l: IrExpression, r: IrExpression): IrExpression { - val symbol = context.getOperatorByName(name, l.type)!! - return JsIrBuilder.buildCall(symbol).apply { + val symbol = context.getOperatorByName(name, l.type.toKotlinType()) + return JsIrBuilder.buildCall(symbol!!).apply { dispatchReceiver = l putValueArgument(0, r) } } 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 } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt index d1fa1c08699..6b63fd17a89 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/ir/IrBuilder.kt @@ -5,16 +5,31 @@ 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.UNDEFINED_OFFSET -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.* 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.IrSimpleType 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 { @@ -77,8 +92,8 @@ object JsIrBuilder { fun buildFunctionReference(type: IrType, symbol: IrFunctionSymbol) = IrFunctionReferenceImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, symbol, symbol.descriptor, 0, null) - fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType? = null) = - IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type ?: symbol.owner.type) + fun buildVar(symbol: IrVariableSymbol, initializer: IrExpression? = null, type: IrType) = + IrVariableImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, SYNTHESIZED_DECLARATION, symbol, type) .apply { this.initializer = initializer } fun buildBreak(type: IrType, loop: IrLoop) = IrBreakImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, loop) @@ -107,8 +122,8 @@ object JsIrBuilder { return element } - fun buildWhen(type: IrType, branches: List) = - IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, SYNTHESIZED_STATEMENT, branches) + fun buildWhen(type: IrType, branches: List, origin: IrStatementOrigin = SYNTHESIZED_STATEMENT) = + IrWhenImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, type, origin, branches) fun buildTypeOperator(type: IrType, operator: IrTypeOperator, argument: IrExpression, toType: IrType, symbol: IrClassifierSymbol) = 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 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 buildCatch(ex: IrVariable, block: IrBlockImpl) = IrCatchImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, ex, block) } + + +object SetDeclarationsParentVisitor : IrElementVisitor { + 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) { + declarations.forEach { this.addChild(it) } +} + +fun IrDeclarationContainer.addChild(declaration: IrDeclaration) { + this.declarations += declaration + declaration.accept(SetDeclarationsParentVisitor, this) +} + +fun IrClass.simpleFunctions(): List = 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) { + 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, + 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) { + val overriddenSuperMembers = this.declarations.map { it.descriptor } + .filterIsInstance().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 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 +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt index caf2dde9fbd..73dc54c3101 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BlockDecomposerLowering.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower import org.jetbrains.kotlin.backend.common.DeclarationContainerLoweringPass +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 @@ -22,22 +23,10 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationContainerLoweringPass { - private lateinit var function: IrFunction - private var tmpVarCounter: Int = 0 +class BlockDecomposerLowering(context: JsIrBackendContext) : DeclarationContainerLoweringPass { - 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 decomposerTransformer = BlockDecomposerTransformer(context) + private val nothingType = context.irBuiltIns.nothingType override fun lower(irDeclarationContainer: IrDeclarationContainer) { irDeclarationContainer.declarations.transformFlat { declaration -> @@ -53,9 +42,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont } fun lower(irFunction: IrFunction) { - function = irFunction - tmpVarCounter = 0 - irFunction.transformChildrenVoid(statementTransformer) + irFunction.accept(decomposerTransformer, null) } fun lower(irField: IrField, container: IrDeclarationContainer): List { @@ -86,6 +73,33 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont 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) { statements.transformFlat { @@ -171,7 +185,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont val result = IrCompositeImpl(receiverResult.startOffset, expression.endOffset, unitType) val receiverValue = receiverResult.statements.last() as IrExpression 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 result.statements += receiverResult.statements.run { subList(0, lastIndex) } result.statements += irVar @@ -249,7 +263,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont val newLoopCondition = newCondition.statements.last() as IrExpression - val breakCond = JsIrBuilder.buildCall(context.irBuiltIns.booleanNotSymbol).apply { + val breakCond = JsIrBuilder.buildCall(booleanNotSymbol).apply { putValueArgument(0, newLoopCondition) } @@ -373,7 +387,7 @@ class BlockDecomposerLowering(val context: JsIrBackendContext) : DeclarationCont 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() { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt index e40d8df931e..4d51136b429 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/BridgesConstruction.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.types.IrType 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.util.createParameterDeclarations import org.jetbrains.kotlin.ir.util.isInterface @@ -48,18 +49,21 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils // Example: for given class hierarchy // // class C { -// fun foo(t: T) = ... +// fun foo(t: T) = ... // bridge // } // // class D : C { -// override fun foo(t: Int) = impl +// override fun foo(t: Int) = impl // delegate to +// } +// +// class E : D { +// fun foo(t: Int) // function // } // // it adds method D that delegates generic calls to implementation: // -// class D : C { -// override fun foo(t: Int) = impl -// fun foo(t: Any?) = foo(t as Int) // Constructed bridge +// class E : D { +// fun foo(t: Any?) = foo(t as Int) // bridgeDescriptorForIrFunction // } // class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { @@ -125,6 +129,8 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { bridge.descriptor.returnType, bridge.descriptor.modality, function.visibility ) + bridgeDescriptorForIrFunction.isSuspend = bridge.descriptor.isSuspend + // TODO: Support offsets for debug info val irFunction = IrFunctionImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.DEFINED, bridgeDescriptorForIrFunction) irFunction.createParameterDeclarations() @@ -136,7 +142,10 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { irFunction.extensionReceiverParameter?.let { 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)) } +irReturn(call) @@ -147,8 +156,9 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass { return irFunction } + // TODO: get rid of Unit check 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 diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt index 7927f36172e..49a44e75c21 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/CallableReferenceLowering.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.expressions.* 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.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol @@ -30,7 +31,7 @@ import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe // 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( val declaration: IrFunction, @@ -47,12 +48,26 @@ class CallableReferenceLowering(val context: JsIrBackendContext) : FileLoweringP private val newDeclarations = mutableListOf() - override fun lower(irFile: IrFile) { - irFile.acceptVoid(CallableReferenceCollector()) - buildClosures() - irFile.transformChildrenVoid(CallableReferenceTransformer()) - irFile.declarations += newDeclarations - } + fun getReferenceCollector() = object : FileLoweringPass { + private val collector = CallableReferenceCollector() + override fun lower(irFile: IrFile) = irFile.acceptVoid(collector) + }::lower + + 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) = 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() + 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) { newDeclarations += v.accept(object : IrElementVisitor, Nothing?> { override fun visitElement(element: IrElement, data: Nothing?) = error("Unreachable execution") 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?) = - lowerKPropertyReference(expression.getter!!.owner, expression) + if (expression.getter in declarationsSet) lowerKPropertyReference( + expression.getter!!.owner, + expression + ) else emptyList() }, null) } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MultipleCatchesLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MultipleCatchesLowering.kt index 6c22c74451c..b5530b1c497 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MultipleCatchesLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/MultipleCatchesLowering.kt @@ -67,21 +67,25 @@ class MultipleCatchesLowering(val context: JsIrBackendContext) : FileLoweringPas 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 pendingException = JsIrBuilder.buildGetValue(pendingExceptionSymbol) val branches = mutableListOf() 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 typeSymbol = type.classifierOrNull + val castedPendingException = if (type !is IrDynamicType) + buildImplicitCast(pendingException, type, typeSymbol!!) + else pendingException + val catchBody = catch.result.transform(object : IrElementTransformer { override fun visitGetValue(expression: IrGetValue, data: VariableDescriptor) = - if (typeSymbol != null && expression.descriptor == data) - // TODO how is it good to generate implicit cast for each access? - buildImplicitCast(pendingException, type, typeSymbol) + if (expression.descriptor == data) + castedPendingException else expression }, catch.parameter) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt index 28010499503..5b772650fae 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/SecondaryCtorLowering.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower 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.impl.LazyClassReceiverParameterDescriptor 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.symbols.JsSymbolBuilder 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.impl.IrFunctionImpl 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.IrFunctionSymbol 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.types.IrType 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.transformChildrenVoid -class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransformerVoid(), DeclarationContainerLoweringPass { +class SecondaryCtorLowering(val context: JsIrBackendContext) { - private val oldCtorToNewMap = mutableMapOf() + data class ConstructorPair(val delegate: IrSimpleFunctionSymbol, val stub: IrSimpleFunctionSymbol) - override fun lower(irDeclarationContainer: IrDeclarationContainer) { - irDeclarationContainer.declarations.transformFlat { - if (it is IrClass) { - listOf(it) + lowerClass(it) - } else null + private val oldCtorToNewMap = mutableMapOf() + + fun getConstructorProcessorLowering() = object : DeclarationContainerLoweringPass { + override fun lower(irDeclarationContainer: IrDeclarationContainer) { + 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 { val className = irClass.name.asString() @@ -74,12 +84,10 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor // val t = Object.create(Foo.prototype); // return Foo_init_$Init$(..., t) // } - - val newInitConstructor = createInitConstructor(declaration, constructorName, irClass.defaultType) + val newInitConstructor = createInitConstructor(declaration, irClass, constructorName, irClass.defaultType) val newCreateConstructor = createCreateConstructor(declaration, newInitConstructor, constructorName, irClass.defaultType) - oldCtorToNewMap[declaration.symbol] = - JsIrBackendContext.SecondaryCtorPair(newInitConstructor.symbol, newCreateConstructor.symbol) + oldCtorToNewMap[declaration.symbol] = ConstructorPair(newInitConstructor.symbol, newCreateConstructor.symbol) oldConstructors += declaration newConstructors += newInitConstructor @@ -92,7 +100,11 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor return newConstructors } - private class ThisUsageReplaceTransformer(val function: IrFunctionSymbol, val thisSymbol: IrValueParameterSymbol) : + private class ThisUsageReplaceTransformer( + val function: IrFunctionSymbol, + val newThisSymbol: IrValueSymbol, + val oldThisSymbol: IrValueSymbol? + ) : IrElementTransformerVoid() { override fun visitReturn(expression: IrReturn): IrExpression = IrReturnImpl( @@ -100,15 +112,15 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor expression.endOffset, expression.type, 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 = - if (expression.descriptor.name.isSpecial && expression.descriptor.name.asString() == Namer.THIS_SPECIAL_NAME) IrGetValueImpl( + if (expression.symbol == oldThisSymbol) IrGetValueImpl( expression.startOffset, expression.endOffset, expression.type, - thisSymbol, + newThisSymbol, expression.origin ) else { expression @@ -117,6 +129,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor private fun createInitConstructor( declaration: IrConstructor, + klass: IrClass, name: String, type: IrType ): IrSimpleFunction = @@ -135,6 +148,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor ) val thisParam = JsIrBuilder.buildValueParameter(thisSymbol, type) + val oldThisReceiver = klass.thisReceiver?.symbol return IrFunctionImpl( declaration.startOffset, declaration.endOffset, @@ -147,7 +161,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor typeParameters += declaration.typeParameters // parent = declaration.parent 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 { + inner class CallsiteRedirectionTransformer : IrElementTransformer { override fun visitFunction(declaration: IrFunction, data: IrFunction?): IrStatement = super.visitFunction(declaration, declaration) - override fun visitCall(expression: IrCall, ownerFunc: IrFunction?): IrElement { - super.visitCall(expression, ownerFunc) + override fun visitCall(expression: IrCall, data: IrFunction?): IrElement { + super.visitCall(expression, data) // TODO: figure out the reason why symbol is not bound if (expression.symbol.isBound) { - val target = expression.symbol.owner as IrFunction + val target = expression.symbol.owner if (target is IrConstructor) { if (!target.descriptor.isPrimary) { - val ctor = context.secondaryConstructorsMap[target.symbol] + val ctor = oldCtorToNewMap[target.symbol] if (ctor != null) { - return redirectCall(expression, ctor.stub) } } @@ -224,8 +237,8 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor return expression } - override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, ownerFunc: IrFunction?): IrElement { - super.visitDelegatingConstructorCall(expression, ownerFunc) + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall, data: IrFunction?): IrElement { + super.visitDelegatingConstructorCall(expression, data) val target = expression.symbol if (target.owner.isPrimary) { @@ -233,9 +246,9 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor return expression } - val fromPrimary = ownerFunc!! is IrConstructor + val fromPrimary = data!! is IrConstructor // 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 readThis = if (fromPrimary) { @@ -246,7 +259,7 @@ class SecondaryCtorLowering(val context: JsIrBackendContext) : IrElementTransfor IrValueParameterSymbolImpl(LazyClassReceiverParameterDescriptor(target.descriptor.containingDeclaration)) ) } 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) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt new file mode 100644 index 00000000000..d949c1c8353 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/StateMachineBuilder.kt @@ -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() + 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 accept(visitor: IrElementVisitor, data: D) = visitor.visitExpression(this, data) + + override fun acceptChildren(visitor: IrElementVisitor, data: D) {} + + override fun transformChildren(transformer: IrElementTransformer, 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, + 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() + 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>() + + 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 + + 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): Array { + + var suspendableCount = arguments.fold(0) { r, n -> if (n in suspendableNodes) r + 1 else r } + + val newArguments = arrayOfNulls(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(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(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) +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendFunctionsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendFunctionsLowering.kt new file mode 100644 index 00000000000..83f2019de59 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendFunctionsLowering.kt @@ -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() + private val suspendLambdas = mutableMapOf() + + 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? { + 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 , + listOf(coroutine) // thus we can eliminate original body. + else + listOf( + 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 + + 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() + private var exceptionTrapId = -1 + + fun build(): BuiltCoroutine { + val superTypes = mutableListOf(coroutineImplSymbol.owner.defaultType) + var suspendFunctionClass: IrClass? = null + var functionClass: IrClass? = null + var suspendFunctionClassTypeArguments: List? = null + var functionClassTypeArguments: List? = 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() + val constructors = mutableSetOf() + 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? = null + var createMethodBuilder: SymbolWithIrBuilder? = null + var invokeMethodBuilder: SymbolWithIrBuilder? = 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() { + + override fun buildSymbol() = IrConstructorSymbolImpl( + ClassConstructorDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* isPrimary = */ false, + /* source = */ SourceElement.NO_SOURCE + ) + ) + + private lateinit var constructorParameters: List + + 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) + = object : SymbolWithIrBuilder() { + + override fun buildSymbol() = IrConstructorSymbolImpl( + ClassConstructorDescriptorImpl.create( + /* containingDeclaration = */ coroutineClassDescriptor, + /* annotations = */ Annotations.EMPTY, + /* isPrimary = */ false, + /* source = */ SourceElement.NO_SOURCE + ) + ) + + lateinit var constructorParameters: List + + 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, + superFunctionDescriptor: FunctionDescriptor?, + coroutineConstructor: IrConstructor, + coroutineClass: IrClass) + = object: SymbolWithIrBuilder() { + + 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 + + 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 are used" }) + }) + } + } + } + } + + private fun createInvokeMethodBuilder(suspendFunctionInvokeFunctionDescriptor: FunctionDescriptor, + functionInvokeFunctionDescriptor: FunctionDescriptor, + createFunction: IrFunction, + doResumeFunction: IrFunction, + coroutineClass: IrClass) + = object: SymbolWithIrBuilder() { + + 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 + + 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() { + + 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() + 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() + + 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() + 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> { + // TODO: data flow analysis. + // Just save all visible for now. + val result = mutableMapOf>() + body.acceptChildrenVoid(object : VariablesScopeTracker() { + override fun visitCall(expression: IrCall) { + if (!expression.descriptor.isSuspend) return super.visitCall(expression) + + expression.acceptChildrenVoid(this) + val visibleVariables = mutableListOf() + scopeStack.forEach { visibleVariables += it } + result.put(expression, visibleVariables) + } + }) + + return result + } + } + + private open class VariablesScopeTracker: IrElementVisitorVoid { + + protected val scopeStack = mutableListOf>(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) + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt new file mode 100644 index 00000000000..0d2ee1b9ca9 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/SuspendLoweringUtils.kt @@ -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) : 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, + 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) : 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() + private val tryLoopStack = mutableListOf() + + 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, + 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) + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt index 51a577a7411..a4e8a68c46c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/IrElementToJsExpressionTransformer.kt @@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.types.isUnit import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.util.OperatorNameConventions +@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { override fun visitVararg(expression: IrVararg, context: JsGenerationContext): JsExpression { @@ -158,7 +159,7 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer { override fun visitBlockBody(body: IrBlockBody, context: JsGenerationContext): JsStatement { @@ -81,9 +83,40 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer + + 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 { //TODO what if body null? val label = context.getNameForLoop(loop) @@ -94,7 +127,8 @@ class IrElementToJsStatementTransformer : BaseIrElementToJsNodeTransformer + context.continuation + } + + add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext -> + val contextGetter = backendContext.coroutineGetContext + val getterName = context.getNameForSymbol(contextGetter) + val continuation = context.continuation + JsInvocation(JsNameRef(getterName, continuation)) + } } } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index a0862ee6ba9..be1a63e28ed 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -5,6 +5,7 @@ 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.Namer 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)) } functionParams.forEach { function.addParameter(it) } + if (declaration.descriptor.isSuspend) { + function.addParameter(context.currentScope.declareName(Namer.CONTINUATION)) + } return function } @@ -57,11 +61,15 @@ fun translateCallArguments(expression: IrMemberAccessExpression, context: JsGene val transformer = IrElementToJsExpressionTransformer() 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 result = argument?.accept(transformer, context) ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) result } + + return if (expression.descriptor.isSuspend) { + arguments + context.continuation + } else arguments } val IrFunction.isStatic: Boolean get() = this.dispatchReceiverParameter == null diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt index cd3733359d4..9bdf4059b71 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsGenerationContext.kt @@ -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.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.js.backend.ast.* @@ -44,4 +45,20 @@ class JsGenerationContext { fun getNameForSymbol(symbol: IrSymbol): JsName = staticContext.getNameForSymbol(symbol, 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 + } } \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt index c5c144649a1..22055b03c8e 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/JsStaticContext.kt @@ -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.transformers.irToJs.JsIntrinsicTransformers +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrLoop import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.js.backend.ast.JsClassModel @@ -24,6 +25,9 @@ class JsStaticContext( val intrinsics = JsIntrinsicTransformers(backendContext) // TODO: use IrSymbol instead of JsName val classModels = mutableMapOf() + val coroutineImplDeclaration = backendContext.ir.symbols.coroutineImpl.owner + val doResumeFunctionSymbol = coroutineImplDeclaration.declarations + .filterIsInstance().single { it.name.asString() == "doResume" }.symbol val initializerBlock = JsGlobalBlock() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index 812b3fb14e6..dae1b2216eb 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -60,6 +60,8 @@ object Namer { val DEFINE_INLINE_FUNCTION = "defineInlineFunction" val DEFAULT_PARAMETER_IMPLEMENTOR_SUFFIX = "\$default" + val CONTINUATION = "\$cont" + val JS_ERROR = JsNameRef("Error") val JS_OBJECT = JsNameRef("Object") diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt index 7ab2aed9fb7..66707b229dd 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/SimpleNameGenerator.kt @@ -60,7 +60,7 @@ class SimpleNameGenerator : NameGenerator { nameDeclarator = context.currentScope::declareFreshName } is PropertyDescriptor -> { - nameBuilder.append(descriptor.name.identifier) + nameBuilder.append(descriptor.name.asString()) if (descriptor.visibility == Visibilities.PRIVATE || descriptor.modality != Modality.FINAL) { nameBuilder.append('$') nameBuilder.append(getNameForDescriptor(descriptor.containingDeclaration, context)) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt index f7bf2f74e2c..e46e7c7fede 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/ExpressionHelpers.kt @@ -183,8 +183,12 @@ fun IrBuilderWithScope.irGet(type: IrType, receiver: IrExpression, getterSymbol: origin = IrStatementOrigin.GET_PROPERTY ) -fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType): IrCall = - IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor) +fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol, type: IrType, typeArguments: List = emptyList()): IrCall = + IrCallImpl(startOffset, endOffset, type, callee, callee.descriptor).apply { + typeArguments.forEachIndexed { index, irType -> + this.putTypeArgument(index, irType) + } + } fun IrBuilderWithScope.irCall(callee: IrFunctionSymbol): IrCall = IrCallImpl(startOffset, endOffset, callee.owner.returnType, callee, callee.descriptor) @@ -237,3 +241,14 @@ fun IrBuilderWithScope.irString(value: String) = fun IrBuilderWithScope.irConcat() = 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 + ) \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt index f89b1dbcad7..33a2e3a6cbf 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/builders/IrBuilder.kt @@ -19,12 +19,13 @@ package org.jetbrains.kotlin.ir.builders import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement 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.IrContainerExpression import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl 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.utils.addToStdlib.safeAs import java.util.* @@ -84,12 +85,13 @@ class IrBlockBuilder( startOffset: Int, endOffset: Int, val origin: IrStatementOrigin? = null, - var resultType: IrType? = null -) : IrStatementsBuilder(context, scope, startOffset, endOffset) { + var resultType: IrType? = null, + val isTransparent:Boolean = false +) : IrStatementsBuilder(context, scope, startOffset, endOffset) { private val statements = ArrayList() - inline fun block(body: IrBlockBuilder.() -> Unit): IrBlock { + inline fun block(body: IrBlockBuilder.() -> Unit): IrContainerExpression { body() return doBuild() } @@ -98,11 +100,11 @@ class IrBlockBuilder( statements.add(irStatement) } - override fun doBuild(): IrBlock { + override fun doBuild(): IrContainerExpression { val resultType = this.resultType ?: statements.lastOrNull().safeAs()?.type ?: 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) return irBlock } @@ -154,6 +156,20 @@ inline fun IrGeneratorWithScope.irBlock( origin, resultType ).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( startOffset: Int = UNDEFINED_OFFSET, endOffset: Int = UNDEFINED_OFFSET, body: IrBlockBodyBuilder.() -> Unit diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt index 35837148522..5620b7406b8 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/irTypes.kt @@ -8,6 +8,9 @@ package org.jetbrains.kotlin.ir.types import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor 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.impl.IrClassSymbolImpl 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): IrSimpleType = + IrSimpleTypeImpl( + this, + hasQuestionMark, + arguments, + emptyList() + ) + private fun makeKotlinType( classifier: IrClassifierSymbol, arguments: List, @@ -95,6 +109,20 @@ fun ClassifierDescriptor.toIrType(hasQuestionMark: Boolean = false): IrType { 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): IrSimpleType = + IrSimpleTypeImpl( + this, + false, + arguments.map { makeTypeProjection(it, Variance.INVARIANT) }, + emptyList() + ) + +fun IrClass.typeWith(arguments: List) = this.symbol.typeWith(arguments) + fun KotlinType.toIrType(): IrType? { if (isDynamic()) return IrDynamicTypeImpl(this, listOf(), Variance.INVARIANT) @@ -113,6 +141,7 @@ fun KotlinType.toIrType(): IrType? { 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) { is ClassDescriptor -> IrClassSymbolImpl(this) is TypeParameterDescriptor -> IrTypeParameterSymbolImpl(this) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt index 17c94929da2..f16b4d8dc17 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrUtils.kt @@ -6,20 +6,30 @@ package org.jetbrains.kotlin.ir.util 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.UNDEFINED_OFFSET 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.IrValueParameterImpl 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.types.IrType import org.jetbrains.kotlin.ir.types.toIrType 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.startOffset import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe 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. @@ -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() { // descriptor.thisAsReceiverParameter.let { // 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() { assert(this.dispatchReceiverParameter == null) diff --git a/compiler/testData/codegen/box/boxingOptimization/nullCheck.kt b/compiler/testData/codegen/box/boxingOptimization/nullCheck.kt index 6062a6811c2..4ba74af4165 100644 --- a/compiler/testData/codegen/box/boxingOptimization/nullCheck.kt +++ b/compiler/testData/codegen/box/boxingOptimization/nullCheck.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt index df35c32e978..1ffec309237 100644 --- a/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt +++ b/compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/boxingOptimization/simple.kt b/compiler/testData/codegen/box/boxingOptimization/simple.kt index 44b00e42991..fbe93a5233a 100644 --- a/compiler/testData/codegen/box/boxingOptimization/simple.kt +++ b/compiler/testData/codegen/box/boxingOptimization/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/boxingOptimization/simpleUninitializedMerge.kt b/compiler/testData/codegen/box/boxingOptimization/simpleUninitializedMerge.kt index c09fe8bec1b..9612abbe1bb 100644 --- a/compiler/testData/codegen/box/boxingOptimization/simpleUninitializedMerge.kt +++ b/compiler/testData/codegen/box/boxingOptimization/simpleUninitializedMerge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/callableReference/function/innerConstructorFromTopLevelOneStringArg.kt b/compiler/testData/codegen/box/callableReference/function/innerConstructorFromTopLevelOneStringArg.kt index 7e4782a9e4c..6ed017518ef 100644 --- a/compiler/testData/codegen/box/callableReference/function/innerConstructorFromTopLevelOneStringArg.kt +++ b/compiler/testData/codegen/box/callableReference/function/innerConstructorFromTopLevelOneStringArg.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR class A { inner class Inner(val result: Int) } diff --git a/compiler/testData/codegen/box/callableReference/function/newArray.kt b/compiler/testData/codegen/box/callableReference/function/newArray.kt index 3d846e4b441..ccce9aa6312 100644 --- a/compiler/testData/codegen/box/callableReference/function/newArray.kt +++ b/compiler/testData/codegen/box/callableReference/function/newArray.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR private fun upcast(value: T): T = value fun box(): String { diff --git a/compiler/testData/codegen/box/controlStructures/factorialTest.kt b/compiler/testData/codegen/box/controlStructures/factorialTest.kt index 0fda24b8efd..fdb108f8bdc 100644 --- a/compiler/testData/codegen/box/controlStructures/factorialTest.kt +++ b/compiler/testData/codegen/box/controlStructures/factorialTest.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt b/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt index 6334d67b038..d3ec2d5a5ab 100644 --- a/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/32defaultParametersInSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt b/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt index 5dc82ebfb03..a3b36b8fa77 100644 --- a/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/accessorForSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/asyncIterator.kt b/compiler/testData/codegen/box/coroutines/asyncIterator.kt index c87482e406e..25e4ac9672e 100644 --- a/compiler/testData/codegen/box/coroutines/asyncIterator.kt +++ b/compiler/testData/codegen/box/coroutines/asyncIterator.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt b/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt index ed6708530f1..59251a2cf05 100644 --- a/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt +++ b/compiler/testData/codegen/box/coroutines/asyncIteratorNullMerge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/beginWithException.kt b/compiler/testData/codegen/box/coroutines/beginWithException.kt index 594dd0cd91d..5c085b6ed0f 100644 --- a/compiler/testData/codegen/box/coroutines/beginWithException.kt +++ b/compiler/testData/codegen/box/coroutines/beginWithException.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/beginWithExceptionNoHandleException.kt b/compiler/testData/codegen/box/coroutines/beginWithExceptionNoHandleException.kt index 193610d01e7..0205e5e6052 100644 --- a/compiler/testData/codegen/box/coroutines/beginWithExceptionNoHandleException.kt +++ b/compiler/testData/codegen/box/coroutines/beginWithExceptionNoHandleException.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/coercionToUnit.kt b/compiler/testData/codegen/box/coroutines/coercionToUnit.kt index e3757646af7..141f020f7fd 100644 --- a/compiler/testData/codegen/box/coroutines/coercionToUnit.kt +++ b/compiler/testData/codegen/box/coroutines/coercionToUnit.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/controlFlow/doWhileStatement.kt b/compiler/testData/codegen/box/coroutines/controlFlow/doWhileStatement.kt index af9cb656d1b..146ecc7293f 100644 --- a/compiler/testData/codegen/box/coroutines/controlFlow/doWhileStatement.kt +++ b/compiler/testData/codegen/box/coroutines/controlFlow/doWhileStatement.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt b/compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt index 6281fc3f783..ac4e4840228 100644 --- a/compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt +++ b/compiler/testData/codegen/box/coroutines/controlFlow/returnFromFinally.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/controlFlow/whileStatement.kt b/compiler/testData/codegen/box/coroutines/controlFlow/whileStatement.kt index 6fae0c98f7d..3247b59b53c 100644 --- a/compiler/testData/codegen/box/coroutines/controlFlow/whileStatement.kt +++ b/compiler/testData/codegen/box/coroutines/controlFlow/whileStatement.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/controllerAccessFromInnerLambda.kt b/compiler/testData/codegen/box/coroutines/controllerAccessFromInnerLambda.kt index 4910c26df5a..329347403c2 100644 --- a/compiler/testData/codegen/box/coroutines/controllerAccessFromInnerLambda.kt +++ b/compiler/testData/codegen/box/coroutines/controllerAccessFromInnerLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt b/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt index 2f16e1b6d97..6587f831cd6 100644 --- a/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt +++ b/compiler/testData/codegen/box/coroutines/coroutineContextInInlinedLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt b/compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt index 31cc7a49b24..c404c0acf76 100644 --- a/compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt +++ b/compiler/testData/codegen/box/coroutines/createCoroutineSafe.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt b/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt index 98fc0320117..96459e600c5 100644 --- a/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt +++ b/compiler/testData/codegen/box/coroutines/crossInlineWithCapturedOuterReceiver.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt b/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt index 339593532a7..c8b35e16b24 100644 --- a/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt +++ b/compiler/testData/codegen/box/coroutines/defaultParametersInSuspend.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/delegatedSuspendMember.kt b/compiler/testData/codegen/box/coroutines/delegatedSuspendMember.kt index 92ced65b4da..1e267b5d04e 100644 --- a/compiler/testData/codegen/box/coroutines/delegatedSuspendMember.kt +++ b/compiler/testData/codegen/box/coroutines/delegatedSuspendMember.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/dispatchResume.kt b/compiler/testData/codegen/box/coroutines/dispatchResume.kt index 2fd19636969..674a075d5f2 100644 --- a/compiler/testData/codegen/box/coroutines/dispatchResume.kt +++ b/compiler/testData/codegen/box/coroutines/dispatchResume.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/falseUnitCoercion.kt b/compiler/testData/codegen/box/coroutines/falseUnitCoercion.kt index d707a66e99b..4d3f0224abc 100644 --- a/compiler/testData/codegen/box/coroutines/falseUnitCoercion.kt +++ b/compiler/testData/codegen/box/coroutines/falseUnitCoercion.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt index 220d3bd2df2..c4a4f126565 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceivers.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceivers.kt index e18a899e9ca..bccf7cfbe8b 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceivers.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceivers.kt @@ -6,7 +6,6 @@ import helpers.* import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.intrinsics.* -import kotlin.test.assertEquals class A(val w: String) { suspend fun String.ext(): String = suspendCoroutineUninterceptedOrReturn { diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt index 45dc3572017..0c19e8d9abb 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt @@ -6,7 +6,6 @@ import helpers.* import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.intrinsics.* -import kotlin.test.assertEquals class A(val w: String) { suspend fun Long.ext(): String = suspendCoroutineUninterceptedOrReturn { diff --git a/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt b/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt index 2162db6c40d..efbeb8bcfd3 100644 --- a/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt +++ b/compiler/testData/codegen/box/coroutines/inlineClasses/bridgeGenerationNonInline.kt @@ -1,4 +1,4 @@ -// IGNORE_BACKEND: JS_IR, JVM_IR +// IGNORE_BACKEND: JVM_IR // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt index 5c94a5a99ca..054590cd2e0 100644 --- a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt index a8a5a7d1731..b1a6bba271f 100644 --- a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt index e5509dc825e..00c52e6ea8c 100644 --- a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt index ca214c479cb..d280b4f7846 100644 --- a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt index edd01e22398..d9f1ad105fd 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContext.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt index e0f523c6646..4145296ddb5 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiver.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt index 7ffc56636ec..acfcc24d319 100644 --- a/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt +++ b/compiler/testData/codegen/box/coroutines/intrinsicSemantics/coroutineContextReceiverNotIntrinsic.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedParameters.kt b/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedParameters.kt index c7305aceba4..756f6a74982 100644 --- a/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedParameters.kt +++ b/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedParameters.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedVariables.kt b/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedVariables.kt index f3d37ee3a89..34566eff187 100644 --- a/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedVariables.kt +++ b/compiler/testData/codegen/box/coroutines/localFunctions/named/capturedVariables.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/localFunctions/named/extension.kt b/compiler/testData/codegen/box/coroutines/localFunctions/named/extension.kt index 5b10356f86e..82431c2f346 100644 --- a/compiler/testData/codegen/box/coroutines/localFunctions/named/extension.kt +++ b/compiler/testData/codegen/box/coroutines/localFunctions/named/extension.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/localFunctions/named/nestedLocals.kt b/compiler/testData/codegen/box/coroutines/localFunctions/named/nestedLocals.kt index a372f620c79..aa1baaea219 100644 --- a/compiler/testData/codegen/box/coroutines/localFunctions/named/nestedLocals.kt +++ b/compiler/testData/codegen/box/coroutines/localFunctions/named/nestedLocals.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/localFunctions/named/withArguments.kt b/compiler/testData/codegen/box/coroutines/localFunctions/named/withArguments.kt index 248eee38639..5392e0dcc5c 100644 --- a/compiler/testData/codegen/box/coroutines/localFunctions/named/withArguments.kt +++ b/compiler/testData/codegen/box/coroutines/localFunctions/named/withArguments.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda1.kt b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda1.kt index e12cb2296a3..4e3ed325872 100644 --- a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda1.kt +++ b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda1.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda2.kt b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda2.kt index f72a1085c4f..a03b773e87d 100644 --- a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda2.kt +++ b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda3.kt b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda3.kt index f10d25d9d4b..32c25fe6ee7 100644 --- a/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda3.kt +++ b/compiler/testData/codegen/box/coroutines/multipleInvokeCallsInsideInlineLambda3.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt b/compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt index d20d7db73bf..5a885a9d9ff 100644 --- a/compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt +++ b/compiler/testData/codegen/box/coroutines/noSuspensionPoints.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt b/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt index d646d020064..886f34dff7f 100644 --- a/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt +++ b/compiler/testData/codegen/box/coroutines/overrideDefaultArgument.kt @@ -3,6 +3,7 @@ // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST +// DONT_RUN_GENERATED_CODE: JS_IR import helpers.* import COROUTINES_PACKAGE.* import COROUTINES_PACKAGE.intrinsics.* diff --git a/compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt b/compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt index 5c98bfa49d8..64722f01abd 100644 --- a/compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt +++ b/compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt index 2f88fc13704..e34a3c78eea 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/dispatchResume.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateFunctions.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateFunctions.kt index 30fc9708372..2506947a3aa 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateFunctions.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/privateFunctions.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt index a39e8a36f55..04239e645ee 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/withVariables.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/suspendInTheMiddleOfObjectConstructionWithJumpOut.kt b/compiler/testData/codegen/box/coroutines/suspendInTheMiddleOfObjectConstructionWithJumpOut.kt index 0d618fcc946..26429c2d07c 100644 --- a/compiler/testData/codegen/box/coroutines/suspendInTheMiddleOfObjectConstructionWithJumpOut.kt +++ b/compiler/testData/codegen/box/coroutines/suspendInTheMiddleOfObjectConstructionWithJumpOut.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineNonLocalReturn.kt b/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineNonLocalReturn.kt index ad377fca5b6..8c0bc7056ce 100644 --- a/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineNonLocalReturn.kt +++ b/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineNonLocalReturn.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineReturn.kt b/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineReturn.kt index 18432687322..ca5c26d8532 100644 --- a/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineReturn.kt +++ b/compiler/testData/codegen/box/coroutines/unitTypeReturn/coroutineReturn.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/coroutines/varValueConflictsWithTable.kt b/compiler/testData/codegen/box/coroutines/varValueConflictsWithTable.kt index e949a863529..10fca405a93 100644 --- a/compiler/testData/codegen/box/coroutines/varValueConflictsWithTable.kt +++ b/compiler/testData/codegen/box/coroutines/varValueConflictsWithTable.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/compiler/testData/codegen/box/deadCodeElimination/emptyVariableRange.kt b/compiler/testData/codegen/box/deadCodeElimination/emptyVariableRange.kt index df8ddc19692..c162fbeec34 100644 --- a/compiler/testData/codegen/box/deadCodeElimination/emptyVariableRange.kt +++ b/compiler/testData/codegen/box/deadCodeElimination/emptyVariableRange.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/differentReceivers.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/differentReceivers.kt index 3e4a436ad2c..5f8eebb7641 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/differentReceivers.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/differentReceivers.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/evaluationOrder.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/evaluationOrder.kt index d3535114ded..049a5ed01fc 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/evaluationOrder.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/evaluationOrder.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/generic.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/generic.kt index 119c5a5ece1..8bb42b15283 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/generic.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/generic.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inClass.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inClass.kt index 987f8aef9fa..634604c10f1 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inClass.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inClass.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inlineProvideDelegate.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inlineProvideDelegate.kt index 4b787cf7402..de9e887dc8a 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inlineProvideDelegate.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/inlineProvideDelegate.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/propertyMetadata.kt b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/propertyMetadata.kt index 4a3287a5875..f2251a23205 100644 --- a/compiler/testData/codegen/box/delegatedProperty/provideDelegate/propertyMetadata.kt +++ b/compiler/testData/codegen/box/delegatedProperty/provideDelegate/propertyMetadata.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt b/compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt index 688835ecd0a..a59a7507800 100644 --- a/compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt +++ b/compiler/testData/codegen/box/functions/localFunctions/callInlineLocalInLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambda.kt b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambda.kt index 25246ee1dac..935d8b4963c 100644 --- a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambda.kt +++ b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage1.kt b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage1.kt index ec45fe84ab8..6609be78133 100644 --- a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage1.kt +++ b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage1.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage2.kt b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage2.kt index 8924ebdd108..166dfedccb8 100644 --- a/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage2.kt +++ b/compiler/testData/codegen/box/functions/localFunctions/definedWithinLambdaInnerUsage2.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/increment/augmentedAssignmentWithComplexRhs.kt b/compiler/testData/codegen/box/increment/augmentedAssignmentWithComplexRhs.kt index 11d36b423d9..cc9dccf660b 100644 --- a/compiler/testData/codegen/box/increment/augmentedAssignmentWithComplexRhs.kt +++ b/compiler/testData/codegen/box/increment/augmentedAssignmentWithComplexRhs.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.* diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt index 80e03c9394a..3d18adacce5 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +MultiPlatformProjects // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt index 388b3ecb52e..53f7f987aea 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +MultiPlatformProjects // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromCommonClass.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromCommonClass.kt index 8b59c67573a..658cd7d94b2 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromCommonClass.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromCommonClass.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +MultiPlatformProjects // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt index 896cf21f71a..23aae686691 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +MultiPlatformProjects // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt index 2567df20cd5..c4ea69ccf78 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt index 3c1a684d0be..abe6f5ca198 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt index f3b2c59a0f3..55334ec1fda 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt diff --git a/compiler/testData/codegen/box/reflection/functions/simpleNames.kt b/compiler/testData/codegen/box/reflection/functions/simpleNames.kt index 6e617fb09e8..9a725a5999f 100644 --- a/compiler/testData/codegen/box/reflection/functions/simpleNames.kt +++ b/compiler/testData/codegen/box/reflection/functions/simpleNames.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_REFLECT import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/reified/checkcast.kt b/compiler/testData/codegen/box/reified/checkcast.kt index d6753ce8b27..3b10a4bd5f8 100644 --- a/compiler/testData/codegen/box/reified/checkcast.kt +++ b/compiler/testData/codegen/box/reified/checkcast.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/reified/safecast.kt b/compiler/testData/codegen/box/reified/safecast.kt index d7d9be254da..6977288fc30 100644 --- a/compiler/testData/codegen/box/reified/safecast.kt +++ b/compiler/testData/codegen/box/reified/safecast.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/storeStackBeforeInline/primitiveMerge.kt b/compiler/testData/codegen/box/storeStackBeforeInline/primitiveMerge.kt index 382c0fd0c4a..7a0c44138c9 100644 --- a/compiler/testData/codegen/box/storeStackBeforeInline/primitiveMerge.kt +++ b/compiler/testData/codegen/box/storeStackBeforeInline/primitiveMerge.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/storeStackBeforeInline/simple.kt b/compiler/testData/codegen/box/storeStackBeforeInline/simple.kt index d117a59354c..02c77ef3f87 100644 --- a/compiler/testData/codegen/box/storeStackBeforeInline/simple.kt +++ b/compiler/testData/codegen/box/storeStackBeforeInline/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/storeStackBeforeInline/unreachableMarker.kt b/compiler/testData/codegen/box/storeStackBeforeInline/unreachableMarker.kt index 5bfa9dafebb..800314e0a51 100644 --- a/compiler/testData/codegen/box/storeStackBeforeInline/unreachableMarker.kt +++ b/compiler/testData/codegen/box/storeStackBeforeInline/unreachableMarker.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/storeStackBeforeInline/withLambda.kt b/compiler/testData/codegen/box/storeStackBeforeInline/withLambda.kt index 892ff730148..44e64d5a9bc 100644 --- a/compiler/testData/codegen/box/storeStackBeforeInline/withLambda.kt +++ b/compiler/testData/codegen/box/storeStackBeforeInline/withLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/strings/nestedConcat.kt b/compiler/testData/codegen/box/strings/nestedConcat.kt index c4a57c7dee9..6c27ecec29e 100644 --- a/compiler/testData/codegen/box/strings/nestedConcat.kt +++ b/compiler/testData/codegen/box/strings/nestedConcat.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME import kotlin.test.assertEquals diff --git a/compiler/testData/codegen/box/syntheticAccessors/accessorForProtectedInvokeVirtual.kt b/compiler/testData/codegen/box/syntheticAccessors/accessorForProtectedInvokeVirtual.kt index c275e8e0847..5a2cc2d3ab5 100644 --- a/compiler/testData/codegen/box/syntheticAccessors/accessorForProtectedInvokeVirtual.kt +++ b/compiler/testData/codegen/box/syntheticAccessors/accessorForProtectedInvokeVirtual.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: 1.kt diff --git a/compiler/testData/codegen/box/when/stringOptimization/duplicatingItems.kt b/compiler/testData/codegen/box/when/stringOptimization/duplicatingItems.kt index bf14d9f91e7..1d400ddcb4d 100644 --- a/compiler/testData/codegen/box/when/stringOptimization/duplicatingItems.kt +++ b/compiler/testData/codegen/box/when/stringOptimization/duplicatingItems.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // CHECK_CASES_COUNT: function=foo count=3 // CHECK_IF_COUNT: function=foo count=0 diff --git a/compiler/testData/codegen/box/when/stringOptimization/expression.kt b/compiler/testData/codegen/box/when/stringOptimization/expression.kt index efbd9a0f3e8..f3ad4b24b80 100644 --- a/compiler/testData/codegen/box/when/stringOptimization/expression.kt +++ b/compiler/testData/codegen/box/when/stringOptimization/expression.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // CHECK_CASES_COUNT: function=foo count=4 // CHECK_IF_COUNT: function=foo count=0 diff --git a/compiler/testData/codegen/box/when/stringOptimization/nullability.kt b/compiler/testData/codegen/box/when/stringOptimization/nullability.kt index 70c45d8a002..c325a7bef61 100644 --- a/compiler/testData/codegen/box/when/stringOptimization/nullability.kt +++ b/compiler/testData/codegen/box/when/stringOptimization/nullability.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // CHECK_CASES_COUNT: function=foo1 count=2 // CHECK_IF_COUNT: function=foo1 count=1 diff --git a/compiler/testData/codegen/box/when/stringOptimization/statement.kt b/compiler/testData/codegen/box/when/stringOptimization/statement.kt index 89897017f74..1a06c2f0148 100644 --- a/compiler/testData/codegen/box/when/stringOptimization/statement.kt +++ b/compiler/testData/codegen/box/when/stringOptimization/statement.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // CHECK_CASES_COUNT: function=foo1 count=4 // CHECK_IF_COUNT: function=foo1 count=0 diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java index f071565e6f4..2c1b27e0596 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsName.java @@ -4,9 +4,9 @@ package org.jetbrains.kotlin.js.backend.ast; +import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.js.backend.ast.metadata.HasMetadata; import org.jetbrains.kotlin.js.common.Symbol; -import org.jetbrains.annotations.NotNull; /** * An abstract base class for named JavaScript objects. diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index 6f11d3d6d52..885a4ae5155 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -124,7 +124,7 @@ abstract class BasicBoxTest( generateJavaScriptFile( file.parent, module, outputFileName, dependencies, friends, modules.size > 1, !SKIP_SOURCEMAP_REMAPPING.matcher(fileContent).find(), - outputPrefixFile, outputPostfixFile, mainCallParameters, testPackage, testFunction + outputPrefixFile, outputPostfixFile, mainCallParameters, testPackage, testFunction, coroutinesPackage.isNotEmpty() ) if (!module.name.endsWith(OLD_MODULE_SUFFIX)) Pair(outputFileName, module) else null @@ -291,18 +291,19 @@ abstract class BasicBoxTest( private fun TestModule.outputFileName(directory: File) = directory.absolutePath + "/" + outputFileSimpleName() + "_v5" private fun generateJavaScriptFile( - directory: String, - module: TestModule, - outputFileName: String, - dependencies: List, - friends: List, - multiModule: Boolean, - remap: Boolean, - outputPrefixFile: File?, - outputPostfixFile: File?, - mainCallParameters: MainCallParameters, - testPackage: String?, - testFunction: String + directory: String, + module: TestModule, + outputFileName: String, + dependencies: List, + friends: List, + multiModule: Boolean, + remap: Boolean, + outputPrefixFile: File?, + outputPostfixFile: File?, + mainCallParameters: MainCallParameters, + testPackage: String?, + testFunction: String, + doNotCache: Boolean ) { val kotlinFiles = module.files.filter { it.fileName.endsWith(".kt") } val testFiles = kotlinFiles.map { it.fileName } @@ -324,7 +325,7 @@ abstract class BasicBoxTest( val incrementalData = IncrementalData() translateFiles( psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, - mainCallParameters, incrementalData, remap, testPackage, testFunction + mainCallParameters, incrementalData, remap, testPackage, testFunction, doNotCache ) if (module.hasFilesToRecompile) { @@ -371,7 +372,7 @@ abstract class BasicBoxTest( translateFiles( translationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, - mainCallParameters, incrementalData, remap, testPackage, testFunction + mainCallParameters, incrementalData, remap, testPackage, testFunction, false ) val originalOutput = FileUtil.loadFile(outputFile) @@ -435,7 +436,8 @@ abstract class BasicBoxTest( incrementalData: IncrementalData, remap: Boolean, testPackage: String?, - testFunction: String + testFunction: String, + doNotCache: Boolean ) { val translator = K2JSTranslator(config) val translationResult = translator.translateUnits(ExceptionThrowingReporter, units, mainCallParameters) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt index b61ee084684..d3057b6cb1f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicIrBoxTest.kt @@ -77,7 +77,8 @@ abstract class BasicIrBoxTest( incrementalData: IncrementalData, remap: Boolean, testPackage: String?, - testFunction: String + testFunction: String, + doNotCache: Boolean ) { val filesToCompile = units .map { (it as TranslationUnit.SourceFile).file } @@ -94,12 +95,22 @@ abstract class BasicIrBoxTest( runtimeFile.write(runtimeResult!!.generatedCode) } - val result = compile( - config.project, - filesToCompile, - config.configuration, - FqName((testPackage?.let { "$it." } ?: "") + testFunction), - listOf(runtimeResult!!.moduleDescriptor)) + val result = if (doNotCache) { + val runtimeFiles = runtimeSources.map(::createPsiFile) + val allFiles = runtimeFiles + filesToCompile + compile( + config.project, + allFiles, + config.configuration, + FqName((testPackage?.let { "$it." } ?: "") + testFunction)) + } else { + compile( + config.project, + filesToCompile, + config.configuration, + FqName((testPackage?.let { "$it." } ?: "") + testFunction), + listOf(runtimeResult!!.moduleDescriptor)) + } outputFile.write(result.generatedCode) } diff --git a/js/js.translator/testData/box/coroutines/localVarOptimization.kt b/js/js.translator/testData/box/coroutines/localVarOptimization.kt index ff89f207007..4f386c0fd21 100644 --- a/js/js.translator/testData/box/coroutines/localVarOptimization.kt +++ b/js/js.translator/testData/box/coroutines/localVarOptimization.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // EXPECTED_REACHABLE_NODES: 1251 // DECLARES_VARIABLE: function=doResume name=k // PROPERTY_READ_COUNT: name=local$o count=1 diff --git a/libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt b/libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt index 6051121bf33..5237ef39718 100644 --- a/libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt +++ b/libraries/stdlib/js/irRuntime/DefaultConstructorMarker.kt @@ -5,4 +5,4 @@ package kotlin.js -internal object DefaultConstructorMarker +internal object DefaultConstructorMarker \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/coroutineIntrinsics.kt b/libraries/stdlib/js/irRuntime/coroutineIntrinsics.kt new file mode 100644 index 00000000000..a4c3fa1529f --- /dev/null +++ b/libraries/stdlib/js/irRuntime/coroutineIntrinsics.kt @@ -0,0 +1,115 @@ +/* + * 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 kotlin.coroutines.experimental.intrinsics + +import kotlin.coroutines.experimental.* + +@SinceKotlin("1.1") +@kotlin.internal.InlineOnly +@Suppress("UNUSED_PARAMETER") +public suspend inline fun suspendCoroutineOrReturn(crossinline block: (Continuation) -> Any?): T = + suspendCoroutineUninterceptedOrReturn { cont -> block(cont.intercepted()) } + +/** + * Obtains the current continuation instance inside suspend functions and either suspends + * currently running coroutine or returns result immediately without suspension. + * + * Unlike [suspendCoroutineOrReturn] it does not intercept continuation. + */ +@SinceKotlin("1.2") +public suspend fun suspendCoroutineUninterceptedOrReturn(block: (Continuation) -> Any?): T = + returnIfSuspended(block(getContinuation())) + +/** + * Intercept continuation with [ContinuationInterceptor]. + */ +@SinceKotlin("1.2") +@kotlin.internal.InlineOnly +public inline fun Continuation.intercepted() = normalizeContinuation(this) + +/** + * This value is used as a return value of [suspendCoroutineOrReturn] `block` argument to state that + * the execution was suspended and will not return any result immediately. + */ +@SinceKotlin("1.1") +public val COROUTINE_SUSPENDED: Any = Any() + + +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +@kotlin.internal.InlineOnly +public inline fun (suspend () -> T).startCoroutineUninterceptedOrReturn( + completion: Continuation +): Any? { + val self_0 = this + val cmpt_0 = completion + return js("self_0.invoke(cmpt_0)") + // TODO: use clean version once function references is fixed +// return (this as Function1, Any?>).invoke(completion) +} + +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +@kotlin.internal.InlineOnly +public inline fun (suspend R.() -> T).startCoroutineUninterceptedOrReturn( + receiver: R, + completion: Continuation +): Any? { + val self_0 = this + val rec_0 = receiver + val cmpt_0 = completion + return js("self_0.invoke(rec_0, cmpt_0)") + // TODO: use clean version once function references is fixed +// return (this as Function2, Any?>).invoke(receiver, completion) +} + +@SinceKotlin("1.1") +public fun (suspend R.() -> T).createCoroutineUnchecked( + receiver: R, + completion: Continuation +): Continuation { + return if (this !is CoroutineImpl) { + buildContinuationByInvokeCall(completion) { + @Suppress("UNCHECKED_CAST") (this as Function2, Any?>).invoke(receiver, completion) + } + } else { + (create(receiver, completion) as CoroutineImpl).facade + } +} + +@SinceKotlin("1.1") +public fun (suspend () -> T).createCoroutineUnchecked( + completion: Continuation +): Continuation { + return if (this !is CoroutineImpl) { + buildContinuationByInvokeCall(completion) { + @Suppress("UNCHECKED_CAST") (this as Function1, Any?>).invoke(completion) + } + } else { + (create(completion) as CoroutineImpl).facade + } +} + +private inline fun buildContinuationByInvokeCall( + completion: Continuation, + crossinline block: () -> Any? +): Continuation { + val continuation = + object : Continuation { + override val context: CoroutineContext + get() = completion.context + + override fun resume(value: Unit) { + processBareContinuationResume(completion, block) + } + + override fun resumeWithException(exception: Throwable) { + completion.resumeWithException(exception) + } + } + + return interceptContinuationIfNeeded(completion.context, continuation) +} \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/coroutines.kt b/libraries/stdlib/js/irRuntime/coroutines.kt new file mode 100644 index 00000000000..2289bb6c328 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/coroutines.kt @@ -0,0 +1,323 @@ +/* + * 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 kotlin.coroutines.experimental + +import kotlin.coroutines.experimental.intrinsics.* + +/** + * Starts coroutine with receiver type [R] and result type [T]. + * This function creates and start a new, fresh instance of suspendable computation every time it is invoked. + * The [completion] continuation is invoked when coroutine completes with result or exception. + */ +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +public fun (suspend R.() -> T).startCoroutine( + receiver: R, + completion: Continuation +) { + createCoroutineUnchecked(receiver, completion).resume(Unit) +} + +/** + * Starts coroutine without receiver and with result type [T]. + * This function creates and start a new, fresh instance of suspendable computation every time it is invoked. + * The [completion] continuation is invoked when coroutine completes with result or exception. + */ +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +public fun (suspend () -> T).startCoroutine( + completion: Continuation +) { + createCoroutineUnchecked(completion).resume(Unit) +} + +/** + * Creates a coroutine with receiver type [R] and result type [T]. + * This function creates a new, fresh instance of suspendable computation every time it is invoked. + * + * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance. + * The [completion] continuation is invoked when coroutine completes with result or exception. + * Repeated invocation of any resume function on the resulting continuation produces [IllegalStateException]. + */ +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +public fun (suspend R.() -> T).createCoroutine( + receiver: R, + completion: Continuation +): Continuation = SafeContinuation(createCoroutineUnchecked(receiver, completion), COROUTINE_SUSPENDED) + +/** + * Creates a coroutine without receiver and with result type [T]. + * This function creates a new, fresh instance of suspendable computation every time it is invoked. + * + * To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance. + * The [completion] continuation is invoked when coroutine completes with result or exception. + * Repeated invocation of any resume function on the resulting continuation produces [IllegalStateException]. + */ +@SinceKotlin("1.1") +@Suppress("UNCHECKED_CAST") +public fun (suspend () -> T).createCoroutine( + completion: Continuation +): Continuation = SafeContinuation(createCoroutineUnchecked(completion), COROUTINE_SUSPENDED) + +// TODO: remove this once implemented in stdlib +inline fun T.let(f: (T) -> R) = f(this) +inline fun run(f: () -> R) = f() + +public interface CoroutineContext { + /** + * Returns the element with the given [key] from this context or `null`. + * Keys are compared _by reference_, that is to get an element from the context the reference to its actual key + * object must be presented to this function. + */ + public operator fun get(key: Key): E? + + /** + * Accumulates entries of this context starting with [initial] value and applying [operation] + * from left to right to current accumulator value and each element of this context. + */ + public fun fold(initial: R, operation: (R, Element) -> R): R + + /** + * Returns a context containing elements from this context and elements from other [context]. + * The elements from this context with the same key as in the other one are dropped. + */ + public operator fun plus(context: CoroutineContext): CoroutineContext = + if (context === EmptyCoroutineContext) this else // fast path -- avoid lambda creation + context.fold(this) { acc, element -> + val removed = acc.minusKey(element.key) + if (removed === EmptyCoroutineContext) element else { + // make sure interceptor is always last in the context (and thus is fast to get when present) + val interceptor = removed[ContinuationInterceptor] + if (interceptor == null) CombinedContext(removed, element) else { + val left = removed.minusKey(ContinuationInterceptor) + if (left === EmptyCoroutineContext) CombinedContext(element, interceptor) else + CombinedContext(CombinedContext(left, element), interceptor) + } + } + } + + /** + * Returns a context containing elements from this context, but without an element with + * the specified [key]. Keys are compared _by reference_, that is to remove an element from the context + * the reference to its actual key object must be presented to this function. + */ + public fun minusKey(key: Key<*>): CoroutineContext + + /** + * An element of the [CoroutineContext]. An element of the coroutine context is a singleton context by itself. + */ + public interface Element : CoroutineContext { + /** + * A key of this coroutine context element. + */ + public val key: Key<*> + + @Suppress("UNCHECKED_CAST") + public override operator fun get(key: Key): E? = + if (this.key === key) this as E else null + + public override fun fold(initial: R, operation: (R, Element) -> R): R = + operation(initial, this) + + public override fun minusKey(key: Key<*>): CoroutineContext = + if (this.key === key) EmptyCoroutineContext else this + } + + /** + * Key for the elements of [CoroutineContext]. [E] is a type of element with this key. + * Keys in the context are compared _by reference_. + */ + public interface Key +} + +public abstract class AbstractCoroutineContextElement(public override val key: CoroutineContext.Key<*>) : CoroutineContext.Element + +public interface ContinuationInterceptor : CoroutineContext.Element { + /** + * The key that defines *the* context interceptor. + */ + companion object Key : CoroutineContext.Key + + /** + * Returns continuation that wraps the original [continuation], thus intercepting all resumptions. + * This function is invoked by coroutines framework when needed and the resulting continuations are + * cached internally per each instance of the original [continuation]. + * + * By convention, implementations that install themselves as *the* interceptor in the context with + * the [Key] shall also scan the context for other element that implement [ContinuationInterceptor] interface + * and use their [interceptContinuation] functions, too. + */ + public fun interceptContinuation(continuation: Continuation): Continuation +} + +internal class CombinedContext(val left: CoroutineContext, val element: CoroutineContext.Element) : CoroutineContext { + override fun get(key: CoroutineContext.Key): E? { + var cur = this + while (true) { + cur.element[key]?.let { return it } + val next = cur.left + if (next is CombinedContext) { + cur = next + } else { + return next[key] + } + } + } + + public override fun fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R = + operation(left.fold(initial, operation), element) + + public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext { + element[key]?.let { return left } + val newLeft = left.minusKey(key) + return when { + newLeft === left -> this + newLeft === EmptyCoroutineContext -> element + else -> CombinedContext(newLeft, element) + } + } + + private fun size(): Int = + if (left is CombinedContext) left.size() + 1 else 2 + + private fun contains(element: CoroutineContext.Element): Boolean = + get(element.key) == element + + private fun containsAll(context: CombinedContext): Boolean { + var cur = context + while (true) { + if (!contains(cur.element)) return false + val next = cur.left + if (next is CombinedContext) { + cur = next + } else { + return contains(next as CoroutineContext.Element) + } + } + } + + override fun equals(other: Any?): Boolean = + this === other || other is CombinedContext && other.size() == size() && other.containsAll(this) + + override fun hashCode(): Int = left.hashCode() + element.hashCode() + + override fun toString(): String = "CC" +} + +public object EmptyCoroutineContext : CoroutineContext { + public override fun get(key: CoroutineContext.Key): E? = null + public override fun fold(initial: R, operation: (R, CoroutineContext.Element) -> R): R = initial + public override fun plus(context: CoroutineContext): CoroutineContext = context + public override fun minusKey(key: CoroutineContext.Key<*>): CoroutineContext = this + public override fun hashCode(): Int = 0 + public override fun toString(): String = "EmptyCoroutineContext" +} + +public interface Continuation { + /** + * Context of the coroutine that corresponds to this continuation. + */ + public val context: CoroutineContext + + /** + * Resumes the execution of the corresponding coroutine passing [value] as the return value of the last suspension point. + */ + public fun resume(value: T) + + /** + * Resumes the execution of the corresponding coroutine so that the [exception] is re-thrown right after the + * last suspension point. + */ + public fun resumeWithException(exception: Throwable) +} + +public class SafeContinuation +public constructor( + private val delegate: Continuation, + initialResult: Any? +) : Continuation { + + public constructor(delegate: Continuation) : this(delegate, UNDECIDED) + + public override val context: CoroutineContext + get() = delegate.context + + private var result: Any? = initialResult + + override fun resume(value: T) { + when { + result === UNDECIDED -> { + result = value + } + result === COROUTINE_SUSPENDED -> { + result = RESUMED + delegate.resume(value) + } + else -> { + throw IllegalStateException("Already resumed") + } + } + } + + override fun resumeWithException(exception: Throwable) { + when { + result === UNDECIDED -> { + result = Fail(exception) + } + result === COROUTINE_SUSPENDED -> { + result = RESUMED + delegate.resumeWithException(exception) + } + else -> { + throw IllegalStateException("Already resumed") + } + } + } + + public fun getResult(): Any? { + if (result === UNDECIDED) { + result = COROUTINE_SUSPENDED + } + val result = this.result + return when { + result === RESUMED -> { + COROUTINE_SUSPENDED // already called continuation, indicate SUSPENDED upstream + } + result is Fail -> { + throw result.exception + } + else -> { + result // either SUSPENDED or data + } + } + } +} + +suspend inline fun suspendCoroutine(crossinline block: (Continuation) -> Unit): T = + suspendCoroutineOrReturn { c: Continuation -> + val safe = SafeContinuation(c) + block(safe) + safe.getResult() + } + +private val UNDECIDED: Any? = Any() +private val RESUMED: Any? = Any() + +private class Fail(val exception: Throwable) + +@kotlin.internal.InlineOnly +internal inline fun processBareContinuationResume(completion: Continuation<*>, block: () -> Any?) { + try { + val result = block() + if (result !== COROUTINE_SUSPENDED) { + @Suppress("UNCHECKED_CAST") + (completion as Continuation).resume(result) + } + } catch (t: Throwable) { + completion.resumeWithException(t) + } +} \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/coroutinesInternal.kt b/libraries/stdlib/js/irRuntime/coroutinesInternal.kt new file mode 100644 index 00000000000..238d8d8f74a --- /dev/null +++ b/libraries/stdlib/js/irRuntime/coroutinesInternal.kt @@ -0,0 +1,70 @@ +/* + * 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 kotlin.js + +import kotlin.coroutines.experimental.* + +internal fun getContinuation(): Continuation { throw Exception("Implemented as intrinsic") } +// Do we really need this intrinsic in JS? +internal suspend fun returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T { + throw Exception("Implemented as intrinsic") +} + +fun normalizeContinuation(continuation: Continuation): Continuation = + (continuation as? CoroutineImpl)?.facade ?: continuation + +internal fun interceptContinuationIfNeeded( + context: CoroutineContext, + continuation: Continuation +) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation + + +@SinceKotlin("1.2") +@Suppress("WRONG_MODIFIER_TARGET") +public suspend val coroutineContext: CoroutineContext + get() { + throw Exception("Implemented as intrinsic") + } + + +internal abstract class CoroutineImpl(private val completion: Continuation) : Continuation { + protected var exceptionState = 0 + protected var label: Int = 0 + + protected var pendingException: dynamic = null + + public override val context: CoroutineContext get() = completion?.context + + val facade: Continuation get() { + return if (context != null) interceptContinuationIfNeeded(context, this) + else this + } + + override fun resume(value: Any?) { + doResumeWrapper(value, null) + } + + override fun resumeWithException(exception: Throwable) { + // TODO: once we have arrays working refact it with exception table + label = exceptionState + pendingException = exception + doResumeWrapper(null, exception) + } + + protected fun doResumeWrapper(data: Any?, exception: Throwable?) { + processBareContinuationResume(completion) { doResume(data, exception) } + } + + protected abstract fun doResume(data: Any?, exception: Throwable?): Any? + + open fun create(completion: Continuation<*>): Continuation { + throw IllegalStateException("create(Continuation) has not been overridden") + } + + open fun create(value: Any?, completion: Continuation<*>): Continuation { + throw IllegalStateException("create(Any?;Continuation) has not been overridden") + } +} \ No newline at end of file diff --git a/libraries/stdlib/js/irRuntime/tests.kt b/libraries/stdlib/js/irRuntime/tests.kt new file mode 100644 index 00000000000..db6f72ee126 --- /dev/null +++ b/libraries/stdlib/js/irRuntime/tests.kt @@ -0,0 +1,11 @@ +/* + * 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 kotlin.test + +fun assertEquals(a: T, b: T) { + if (a != b) throw Exception("") +} +