From affc8279984fb4e2710a4277405b6d25e22c8beb Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Thu, 13 Sep 2018 20:39:18 +0300 Subject: [PATCH] [JS IR BE] Support 1.3 coroutines in backend --- .../common/lower/InitializersLowering.kt | 4 +- .../ir/backend/js/JsIrBackendContext.kt | 100 +++++++++++------- .../kotlin/ir/backend/js/compiler.kt | 6 +- .../kotlin/ir/backend/js/ir/IrBuilder.kt | 53 ++++++++++ .../js/lower/IntrinsicifyCallsLowering.kt | 59 +---------- .../coroutines/CoroutineIntrinsicLowering.kt | 38 +++++++ .../backend/js/utils/SimpleNameGenerator.kt | 14 +++ 7 files changed, 174 insertions(+), 100 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/CoroutineIntrinsicLowering.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt index f9cf2f99111..d6e028c3414 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InitializersLowering.kt @@ -64,7 +64,7 @@ class InitializersLowering( val irFieldInitializer = declaration.initializer?.expression ?: return val receiver = - if (declaration.descriptor.dispatchReceiverParameter != null) // TODO isStaticField + if (!declaration.isStatic) // TODO isStaticField IrGetValueImpl( irFieldInitializer.startOffset, irFieldInitializer.endOffset, irClass.thisReceiver!!.type, irClass.thisReceiver!!.symbol @@ -79,7 +79,7 @@ class InitializersLowering( null, null ) - if (DescriptorUtils.isStaticDeclaration(declaration.descriptor)) { + if (declaration.isStatic) { staticInitializerStatements.add(irSetField) } else { instanceInitializerStatements.add(irSetField) 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 1d31f5a38a2..8b4650a9bd7 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,6 +13,7 @@ import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.Symbols import org.jetbrains.kotlin.backend.js.JsDeclarationFactory import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor @@ -44,23 +45,34 @@ class JsIrBackendContext( val module: ModuleDescriptor, override val irBuiltIns: IrBuiltIns, val symbolTable: SymbolTable, - irModuleFragment: IrModuleFragment + irModuleFragment: IrModuleFragment, + val configuration: CompilerConfiguration ) : CommonBackendContext { override val builtIns = module.builtIns val internalPackageFragmentDescriptor = KnownPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.js.internal")) - val implicitDeclarationFile = IrFileImpl(object : SourceManager.FileEntry { - override val name = "" - override val maxOffset = UNDEFINED_OFFSET + private val implicitDeclarationFile by lazy { + IrFileImpl(object : SourceManager.FileEntry { + override val name = "" + override val maxOffset = UNDEFINED_OFFSET - override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int) = - SourceRangeInfo("", UNDEFINED_OFFSET, UNDEFINED_OFFSET, UNDEFINED_OFFSET, UNDEFINED_OFFSET, UNDEFINED_OFFSET, UNDEFINED_OFFSET) + override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int) = + SourceRangeInfo( + "", + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + UNDEFINED_OFFSET + ) - override fun getLineNumber(offset: Int) = UNDEFINED_OFFSET - override fun getColumnNumber(offset: Int) = UNDEFINED_OFFSET - }, internalPackageFragmentDescriptor).also { - irModuleFragment.files += it + override fun getLineNumber(offset: Int) = UNDEFINED_OFFSET + override fun getColumnNumber(offset: Int) = UNDEFINED_OFFSET + }, internalPackageFragmentDescriptor).also { + irModuleFragment.files += it + } } override val sharedVariablesManager = @@ -68,31 +80,36 @@ class JsIrBackendContext( override val declarationFactory = JsDeclarationFactory() override val reflectionTypes: ReflectionTypes by lazy(LazyThreadSafetyMode.PUBLICATION) { // TODO - ReflectionTypes(module, FqName("kotlin.reflect")) + ReflectionTypes(module, REFLECT_PACKAGE_FQNAME) } - private val internalPackageName = FqName("kotlin.js") - private val internalPackage = module.getPackage(internalPackageName) + companion object { + 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 CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context") - // TODO: replace it with appropriate package name once we migrate to 1.3 coroutines - private val coroutinePackageNameSrting_12 = "kotlin.coroutines.experimental" - private val coroutinePackageNameSrting_13 = "kotlin.coroutines" + private val REFLECT_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "reflect")) + private val JS_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "js")) + private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal")) + private val COROUTINE_PACKAGE_FQNAME_12 = FqName.fromSegments(listOf("kotlin", "coroutines", "experimental")) + private val COROUTINE_PACKAGE_FQNAME_13 = FqName.fromSegments(listOf("kotlin", "coroutines")) + private val COROUTINE_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME_13 + private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME) - 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: due to name clash those weird suffix is required, remove it once `NameGenerator` is implemented + private val COROUTINE_SUSPEND_OR_RETURN_JS_NAME = "suspendCoroutineUninterceptedOrReturnJS" + private val GET_COROUTINE_CONTEXT_NAME = "getCoroutineContext" + } - // TODO: what is more clear way reference this getter? - private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("") - private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context") + private val internalPackage = module.getPackage(JS_PACKAGE_FQNAME) - private val coroutinePackageName = FqName(coroutinePackageNameSrting_12) - private val coroutineIntrinsicsPackageName = coroutinePackageName.child(INTRINSICS_PACKAGE_NAME) - - private val coroutinePackage = module.getPackage(coroutinePackageName) - private val coroutineIntrinsicsPackage = module.getPackage(coroutineIntrinsicsPackageName) + private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME) + private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME) val enumEntryToGetInstanceFunction = mutableMapOf() @@ -106,19 +123,24 @@ class JsIrBackendContext( ) val contextGetter = continuation.owner.declarations.filterIsInstance().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME } - ?: continuation.owner.declarations.filterIsInstance().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!! + ?: continuation.owner.declarations.filterIsInstance().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!! return contextGetter.symbol } + val coroutineGetContextJs = symbolTable.referenceSimpleFunction(getInternalFunctions(GET_COROUTINE_CONTEXT_NAME).single()) + val coroutineContextProperty: PropertyDescriptor get() { - val vars = internalPackage.memberScope.getContributedVariables( + val vars = coroutinePackage.memberScope.getContributedVariables( COROUTINE_CONTEXT_NAME, NoLookupLocation.FROM_BACKEND ) return vars.single() } + val coroutineSuspendOrReturn = + symbolTable.referenceSimpleFunction(getInternalFunctions(COROUTINE_SUSPEND_OR_RETURN_JS_NAME).single()) + val intrinsics = JsIntrinsics(irBuiltIns, this) private val operatorMap = referenceOperators() @@ -130,14 +152,11 @@ class JsIrBackendContext( } val primitiveCompanionObjects = PrimitiveType.NUMBER_TYPES + .asSequence() .filter { it.name != "LONG" && it.name != "CHAR" } // skip due to they have own explicit companions .map { it.typeName to symbolTable.lazyWrapper.referenceClass( - getClass( - internalPackageName - .child(Name.identifier("internal")) - .child(Name.identifier("${it.typeName.identifier}CompanionObject")) - ) + getClass(JS_INTERNAL_PACKAGE_FQNAME.child(Name.identifier("${it.typeName.identifier}CompanionObject"))) ) }.toMap() @@ -186,16 +205,19 @@ class JsIrBackendContext( get() = TODO("not implemented") override val copyRangeTo: Map get() = TODO("not implemented") - override val coroutineImpl = symbolTable.referenceClass(getInternalClass(COROUTINE_IMPL_NAME.identifier)) + override val coroutineImpl = symbolTable.referenceClass(findClass(coroutinePackage.memberScope, COROUTINE_IMPL_NAME.identifier)) override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction( - coroutineIntrinsicsPackage.memberScope.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND).filterNot { it.isExpect }.single().getter!! + coroutineIntrinsicsPackage.memberScope.getContributedVariables( + COROUTINE_SUSPENDED_NAME, + NoLookupLocation.FROM_BACKEND + ).filterNot { it.isExpect }.single().getter!! ) } override fun shouldGenerateHandlerParameterForDefaultBodyFun() = true } - val coroutineImplLabelProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("label")!! } + val coroutineImplLabelProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("state")!! } val coroutineImplResultSymbol by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("result")!! } val coroutineImplExceptionProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("exception")!! } val coroutineImplExceptionStateProperty by lazy { ir.symbols.coroutineImpl.getPropertyDeclaration("exceptionState")!! } 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 a33bfc247b7..d570b0666f9 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 @@ -14,6 +14,7 @@ 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.coroutines.CoroutineIntrinsicLowering 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 @@ -55,7 +56,8 @@ fun compile( analysisResult.moduleDescriptor, psi2IrContext.irBuiltIns, psi2IrContext.symbolTable, - moduleFragment + moduleFragment, + configuration ) ExternalDependenciesGenerator(psi2IrContext.moduleDescriptor, psi2IrContext.symbolTable, psi2IrContext.irBuiltIns) @@ -68,6 +70,8 @@ fun compile( MoveExternalDeclarationsToSeparatePlace().lower(moduleFragment.files) + moduleFragment.files.forEach(CoroutineIntrinsicLowering(context)::lower) + context.performInlining(moduleFragment) context.lower(moduleFragment) 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 b4e36748fc6..bca44a8d7b7 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 @@ -266,4 +266,57 @@ fun IrClass.simpleFunctions(): List = this.declarations.flatMa } } +// TODO extract to common place? +fun irCall( + call: IrCall, + newSymbol: IrFunctionSymbol, + dispatchReceiverAsFirstArgument: Boolean = false, + firstArgumentAsDispatchReceiver: Boolean = false +): IrCall = + call.run { + IrCallImpl( + startOffset, + endOffset, + type, + newSymbol, + newSymbol.descriptor, + typeArgumentsCount, + origin + ).apply { + copyTypeAndValueArgumentsFrom( + call, + dispatchReceiverAsFirstArgument, + firstArgumentAsDispatchReceiver + ) + } + } +// TODO extract to common place? +private fun IrCall.copyTypeAndValueArgumentsFrom( + call: IrCall, + dispatchReceiverAsFirstArgument: Boolean = false, + firstArgumentAsDispatchReceiver: Boolean = false +) { + copyTypeArgumentsFrom(call) + + var toValueArgumentIndex = 0 + var fromValueArgumentIndex = 0 + + when { + dispatchReceiverAsFirstArgument -> { + putValueArgument(toValueArgumentIndex++, call.dispatchReceiver) + } + firstArgumentAsDispatchReceiver -> { + dispatchReceiver = call.getValueArgument(fromValueArgumentIndex++) + } + else -> { + dispatchReceiver = call.dispatchReceiver + } + } + + extensionReceiver = call.extensionReceiver + + while (fromValueArgumentIndex < call.valueArgumentsCount) { + putValueArgument(toValueArgumentIndex++, call.getValueArgument(fromValueArgumentIndex++)) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt index 1f9a377b755..88ee703d718 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/IntrinsicifyCallsLowering.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.utils.isSubtypeOfClass import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder +import org.jetbrains.kotlin.ir.backend.js.ir.irCall import org.jetbrains.kotlin.ir.backend.js.utils.ConversionNames import org.jetbrains.kotlin.ir.backend.js.utils.Namer import org.jetbrains.kotlin.ir.backend.js.utils.OperatorNames @@ -649,68 +650,10 @@ fun translateEqualsForNullableString(rhs: IrType): EqualityLoweringType = when { else -> RuntimeFunctionCall } - - private fun IrType.isNullableJsNumber(): Boolean = isNullablePrimitiveType() && !isNullableLong() && !isNullableChar() private fun IrType.isJsNumber(): Boolean = isPrimitiveType() && !isLong() && !isChar() - -// TODO extract to common place? -fun irCall( - call: IrCall, - newSymbol: IrFunctionSymbol, - dispatchReceiverAsFirstArgument: Boolean = false, - firstArgumentAsDispatchReceiver: Boolean = false -): IrCall = - call.run { - IrCallImpl( - startOffset, - endOffset, - type, - newSymbol, - newSymbol.descriptor, - typeArgumentsCount, - origin - ).apply { - copyTypeAndValueArgumentsFrom( - call, - dispatchReceiverAsFirstArgument, - firstArgumentAsDispatchReceiver - ) - } - } - -// TODO extract to common place? -private fun IrCall.copyTypeAndValueArgumentsFrom( - call: IrCall, - dispatchReceiverAsFirstArgument: Boolean = false, - firstArgumentAsDispatchReceiver: Boolean = false -) { - copyTypeArgumentsFrom(call) - - var toValueArgumentIndex = 0 - var fromValueArgumentIndex = 0 - - when { - dispatchReceiverAsFirstArgument -> { - putValueArgument(toValueArgumentIndex++, call.dispatchReceiver) - } - firstArgumentAsDispatchReceiver -> { - dispatchReceiver = call.getValueArgument(fromValueArgumentIndex++) - } - else -> { - dispatchReceiver = call.dispatchReceiver - } - } - - extensionReceiver = call.extensionReceiver - - while (fromValueArgumentIndex < call.valueArgumentsCount) { - putValueArgument(toValueArgumentIndex++, call.getValueArgument(fromValueArgumentIndex++)) - } -} - private fun MemberToTransformer.op(type: IrType, name: Name, v: IrSimpleFunctionSymbol) { op(type, name, v = { irCall(it, v, dispatchReceiverAsFirstArgument = true) }) } diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/CoroutineIntrinsicLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/CoroutineIntrinsicLowering.kt new file mode 100644 index 00000000000..003ea4acee2 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/CoroutineIntrinsicLowering.kt @@ -0,0 +1,38 @@ +/* + * 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.FileLoweringPass +import org.jetbrains.kotlin.backend.common.isBuiltInIntercepted +import org.jetbrains.kotlin.backend.common.isBuiltInSuspendCoroutineUninterceptedOrReturn +import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.ir.irCall +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid + +class CoroutineIntrinsicLowering(val context: JsIrBackendContext): FileLoweringPass { + private val languageVersion = context.configuration.languageVersionSettings + override fun lower(irFile: IrFile) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + val call = super.visitCall(expression) + return when { + expression.descriptor.isBuiltInSuspendCoroutineUninterceptedOrReturn(languageVersion) -> + irCall(expression, context.coroutineSuspendOrReturn) + expression.descriptor.isBuiltInIntercepted(languageVersion) -> + error("Intercepted should not be used with release coroutines") + expression.symbol.owner == context.intrinsics.jsCoroutineContext.owner -> + irCall(expression, context.coroutineGetContextJs) + else -> call + } + } + }) + } +} 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 524e6216fce..217f793d1f7 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 @@ -148,7 +148,20 @@ class SimpleNameGenerator : NameGenerator { if (declaration.kind == ClassKind.OBJECT || declaration.name.isSpecial || declaration.visibility == Visibilities.LOCAL) { nameDeclarator = context.staticContext.rootScope::declareFreshName + val parent = declaration.parent + when (parent) { + is IrDeclaration -> nameBuilder.append(getNameForDeclaration(parent, context)) + is IrPackageFragment -> nameBuilder.append(parent.fqName.asString()) + } } + + // TODO: remove asap `NameGenerator` is implemented + (declaration.parent as? IrPackageFragment)?.let { + if (declaration.isInline && it.fqName.asString() != "kotlin") { + nameBuilder.append("_FIX") + } + } + } is IrConstructor -> { nameBuilder.append(getNameForDeclaration(declaration.parent as IrClass, context)) @@ -158,6 +171,7 @@ class SimpleNameGenerator : NameGenerator { nameDeclarator = context.currentScope::declareFreshName } is IrSimpleFunction -> { + nameBuilder.append(declaration.name.asString()) declaration.extensionReceiverParameter?.let { nameBuilder.append("_\$${it.type.render()}") } declaration.typeParameters.forEach { nameBuilder.append("_${it.name.asString()}") }