[JS IR BE] Support 1.3 coroutines in backend

This commit is contained in:
Roman Artemev
2018-09-13 20:39:18 +03:00
committed by romanart
parent c0ef1311ba
commit affc827998
7 changed files with 174 additions and 100 deletions
@@ -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)
@@ -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 = "<implicitDeclarations>"
override val maxOffset = UNDEFINED_OFFSET
private val implicitDeclarationFile by lazy {
IrFileImpl(object : SourceManager.FileEntry {
override val name = "<implicitDeclarations>"
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("<get-context>")
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("<get-context>")
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<IrEnumEntrySymbol, IrSimpleFunctionSymbol>()
@@ -106,19 +123,24 @@ class JsIrBackendContext(
)
val contextGetter =
continuation.owner.declarations.filterIsInstance<IrFunction>().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_GETTER_NAME }
?: continuation.owner.declarations.filterIsInstance<IrProperty>().atMostOne { it.descriptor.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
?: continuation.owner.declarations.filterIsInstance<IrProperty>().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<ClassDescriptor, IrSimpleFunctionSymbol>
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")!! }
@@ -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)
@@ -266,4 +266,57 @@ fun IrClass.simpleFunctions(): List<IrSimpleFunction> = 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++))
}
}
@@ -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) })
}
@@ -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
}
}
})
}
}
@@ -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()}") }