Introduce add continuation pass, which transforms each suspend function

and call to have additional continuation parameter.
This commit is contained in:
Ilmir Usmanov
2019-04-26 16:21:03 +03:00
parent b3a05b1e0d
commit 379390c472
6 changed files with 240 additions and 18 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBasedDeclarationDescriptor
@@ -452,7 +453,18 @@ open class WrappedSimpleFunctionDescriptor(
override fun <V : Any?> getUserData(key: CallableDescriptor.UserDataKey<V>?): V? = null
override fun newCopyBuilder(): FunctionDescriptor.CopyBuilder<out SimpleFunctionDescriptor> {
TODO("not implemented")
return SimpleFunctionDescriptorImpl
.create(containingDeclaration, annotations, name, kind, source)
.initialize(
extensionReceiverParameter,
dispatchReceiverParameter,
typeParameters,
valueParameters,
returnType,
modality,
visibility,
null
).newCopyBuilder()
}
override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>?, data: D) =
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -12,16 +12,19 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.coroutinesPackageFqName
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
class JvmBackendContext(
@@ -48,6 +51,16 @@ class JvmBackendContext(
override val internalPackageFqn = FqName("kotlin.jvm")
val transformedSuspendFunctionsCache = mutableMapOf<IrSimpleFunction, IrSimpleFunction>()
private val coroutinePackage = state.module.getPackage(state.languageVersionSettings.coroutinesPackageFqName())
val continuationClass = symbolTable.referenceClass(
coroutinePackage.memberScope.getContributedClassifier(
Name.identifier("Continuation"), NoLookupLocation.FROM_BACKEND
) as ClassDescriptor
)
internal fun getTopLevelClass(fqName: FqName): IrClassSymbol {
val descriptor = state.module.getPackage(fqName.parent()).memberScope.getContributedClassifier(
fqName.shortName(), NoLookupLocation.FROM_BACKEND
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.jvm
@@ -129,6 +118,7 @@ val jvmPhases = namedIrFilePhase<JvmBackendContext>(
singletonReferencesPhase then
localDeclarationsPhase then
singleAbstractMethodPhase then
addContinuationPhase then
callableReferencePhase then
functionNVarargInvokePhase then
@@ -1,18 +1,27 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.jvm.codegen
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
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.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME
@@ -20,6 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_N
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes
@@ -0,0 +1,198 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView
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.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
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.expressions.impl.IrFunctionReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.types.Variance
import java.util.*
// TODO: Move to common prefix
internal val addContinuationPhase = makeIrFilePhase(
::AddContinuationLowering,
"AddContinuation",
"Add continuation parameter to suspend functions and suspend calls"
)
private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass {
object CONTINUATION_PARAMETER : IrDeclarationOriginImpl("CONTINUATION_PARAMETER")
override fun lower(irFile: IrFile) {
val suspendLambdas = markSuspendLambdas(irFile)
addContinuationParameter(irFile)
transformSuspendCalls(irFile, suspendLambdas)
}
private fun addContinuationParameter(irFile: IrFile) {
fun tryAddContinuationParameter(element: IrElement): List<IrDeclaration>? {
return if (element is IrSimpleFunction && element.isSuspend) {
listOf(element.getOrCreateView())
} else null
}
irFile.transformDeclarationsFlat(::tryAddContinuationParameter)
irFile.acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
declaration.transformDeclarationsFlat(::tryAddContinuationParameter)
}
})
}
private fun markSuspendLambdas(irElement: IrElement): List<IrFunction> {
val suspendLambdas = arrayListOf<IrFunction>()
irElement.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFunctionReference(expression: IrFunctionReference) {
expression.acceptChildrenVoid(this)
if (expression.isSuspend) {
suspendLambdas += expression.symbol.owner
}
}
})
return suspendLambdas
}
private fun transformSuspendCalls(irFile: IrFile, suspendLambdas: List<IrFunction>) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
private val functionStack = Stack<IrFunction>()
override fun visitElement(element: IrElement): IrElement {
element.transformChildrenVoid(this)
return element
}
override fun visitFunction(declaration: IrFunction): IrStatement {
functionStack.push(declaration)
val res = super.visitFunction(declaration)
functionStack.pop()
return res
}
override fun visitCall(expression: IrCall): IrExpression {
if (!expression.isSuspend) return super.visitCall(expression)
val caller = functionStack.peek()
val continuationArgument = IrGetValueImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
continuationType(caller.returnType),
if (caller in suspendLambdas) (caller.dispatchReceiverParameter as IrValueParameter).symbol
else caller.valueParameters.last().symbol
)
val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView()
val res = IrCallImpl(expression.startOffset, expression.endOffset, expression.type, view.symbol).apply {
copyTypeArgumentsFrom(expression)
dispatchReceiver = expression.dispatchReceiver
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(i, expression.getValueArgument(i))
}
putValueArgument(expression.valueArgumentsCount, continuationArgument)
}
return super.visitCall(res)
}
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
if (!expression.isSuspend) return super.visitFunctionReference(expression)
val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView()
val res = IrFunctionReferenceImpl(
expression.startOffset,
expression.endOffset,
expression.type,
view.symbol,
view.descriptor,
expression.typeArgumentsCount,
expression.origin
).apply {
copyTypeArgumentsFrom(expression)
dispatchReceiver = expression.dispatchReceiver
for (i in 0 until expression.valueArgumentsCount) {
putValueArgument(i, expression.getValueArgument(i))
}
}
return super.visitFunctionReference(res)
}
})
}
private fun IrSimpleFunction.getOrCreateView(): IrSimpleFunction =
context.transformedSuspendFunctionsCache.getOrPut(this) {
IrFunctionImpl(
startOffset, endOffset, origin, getOrCreateJvmSuspendFunctionView(descriptor, context.state), returnType
).also {
it.parent = parent
it.copyTypeParametersFrom(this)
it.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(it)
valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) }
it.valueParameters += createContinuationValueParameter(it.valueParameters.size, returnType).apply { parent = it }
// Fix links to parameters
val valueParametersMapping = (valueParameters + dispatchReceiverParameter)
.zip(it.valueParameters.dropLast(1) + it.dispatchReceiverParameter).toMap()
it.body = body?.deepCopyWithSymbols(this)
it.body?.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue) =
valueParametersMapping[expression.symbol.owner]?.let { newParam ->
expression.run { IrGetValueImpl(startOffset, endOffset, type, newParam.symbol, origin) }
} ?: expression
})
}
}
private fun createContinuationValueParameter(index: Int, returnType: IrType): IrValueParameter {
val descriptor = WrappedValueParameterDescriptor()
return IrValueParameterImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
CONTINUATION_PARAMETER,
IrValueParameterSymbolImpl(descriptor),
Name.identifier("\$completion"),
index,
continuationType(returnType),
null, isCrossinline = false, isNoinline = false
).also {
descriptor.bind(it)
}
}
private fun continuationType(returnType: IrType) = context.continuationClass.createType(
hasQuestionMark = false,
arguments = listOf(makeTypeProjection(returnType, Variance.INVARIANT))
)
}
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME
// WITH_COROUTINES
// COMMON_COROUTINES_TEST