[go: nahoru, domu]

blob: db1536a771d4cddce08531c63c2e388426b96e59 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* 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.
*/
package androidx.compose.plugins.kotlin.compiler.lower
import androidx.compose.plugins.kotlin.ComposeFqNames
import org.jetbrains.kotlin.backend.common.pop
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrMetadataSourceOwner
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrTypeParametersContainer
import org.jetbrains.kotlin.ir.declarations.copyAttributes
import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionBase
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeAbbreviation
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrTypeAbbreviationImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.makeNullable
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.types.withHasQuestionMark
import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.DeepCopySymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRemapper
import org.jetbrains.kotlin.ir.util.SymbolRenamer
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isFunction
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.psi2ir.PsiSourceManager
import org.jetbrains.kotlin.psi2ir.findFirstFunction
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
class DeepCopyIrTreeWithSymbolsPreservingMetadata(
val context: JvmBackendContext,
val symbolRemapper: DeepCopySymbolRemapper,
val typeRemapper: TypeRemapper,
val typeTranslator: TypeTranslator,
symbolRenamer: SymbolRenamer = SymbolRenamer.DEFAULT
) : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, symbolRenamer) {
override fun visitClass(declaration: IrClass): IrClass {
return super.visitClass(declaration).also { it.copyMetadataFrom(declaration) }
}
override fun visitFunction(declaration: IrFunction): IrStatement {
return super.visitFunction(declaration).also { it.copyMetadataFrom(declaration) }
}
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrSimpleFunction {
return super.visitSimpleFunction(declaration).also {
it.correspondingPropertySymbol = declaration.correspondingPropertySymbol
it.copyMetadataFrom(declaration)
}
}
override fun visitField(declaration: IrField): IrField {
return super.visitField(declaration).also {
(it as IrFieldImpl).metadata = declaration.metadata
}
}
override fun visitProperty(declaration: IrProperty): IrProperty {
return super.visitProperty(declaration).also { it.copyMetadataFrom(declaration) }
}
override fun visitFile(declaration: IrFile): IrFile {
val srcManager = context.psiSourceManager
val fileEntry = srcManager.getFileEntry(declaration) as? PsiSourceManager.PsiFileEntry
return super.visitFile(declaration).also {
if (fileEntry != null) {
srcManager.putFileEntry(it, fileEntry)
}
if (it is IrFileImpl) {
it.metadata = declaration.metadata
}
}
}
override fun visitConstructorCall(expression: IrConstructorCall): IrConstructorCall {
val ownerFn = expression.symbol.owner as? IrConstructor
// If we are calling an external constructor, we want to "remap" the types of its signature
// as well, since if it they are @Composable it will have its unmodified signature. These
// types won't be traversed by default by the DeepCopyIrTreeWithSymbols so we have to
// do it ourself here.
if (
ownerFn != null &&
ownerFn.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
) {
symbolRemapper.visitConstructor(ownerFn)
val newFn = super.visitConstructor(ownerFn).also {
it.parent = ownerFn.parent
it.patchDeclarationParents(it.parent)
}
val newCallee = symbolRemapper.getReferencedConstructor(newFn.symbol)
return IrConstructorCallImpl(
expression.startOffset, expression.endOffset,
expression.type.remapType(),
newCallee,
newCallee.descriptor,
expression.typeArgumentsCount,
expression.constructorTypeArgumentsCount,
expression.valueArgumentsCount,
mapStatementOrigin(expression.origin)
).apply {
copyRemappedTypeArgumentsFrom(expression)
transformValueArguments(expression)
}.copyAttributes(expression)
}
return super.visitConstructorCall(expression)
}
override fun visitCall(expression: IrCall): IrCall {
val ownerFn = expression.symbol.owner as? IrSimpleFunction
val containingClass = expression.descriptor.containingDeclaration as? ClassDescriptor
// Any virtual calls on composable functions we want to make sure we update the call to
// the right function base class (of n+1 arity). The most often virtual call to make on
// a function instance is `invoke`, which we *already* do in the ComposeParamTransformer.
// There are others that can happen though as well, such as `equals` and `hashCode`. In this
// case, we want to update those calls as well.
if (
ownerFn != null &&
ownerFn.origin == IrDeclarationOrigin.FAKE_OVERRIDE &&
containingClass != null &&
containingClass.defaultType.isFunctionType &&
expression.dispatchReceiver?.type?.isComposable() == true
) {
val typeArguments = containingClass.defaultType.arguments
val newFnClass = context.irIntrinsics.symbols.getFunction(typeArguments.size)
val newDescriptor = newFnClass
.descriptor
.unsubstitutedMemberScope
.findFirstFunction(ownerFn.name.identifier) { true }
var newFn: IrSimpleFunction = IrFunctionImpl(
ownerFn.startOffset,
ownerFn.endOffset,
ownerFn.origin,
newDescriptor,
expression.type
)
symbolRemapper.visitSimpleFunction(newFn)
newFn = super.visitSimpleFunction(newFn).also { fn ->
fn.parent = newFnClass.owner
ownerFn.overriddenSymbols.mapTo(fn.overriddenSymbols) { it }
fn.dispatchReceiverParameter = ownerFn.dispatchReceiverParameter
fn.extensionReceiverParameter = ownerFn.extensionReceiverParameter
newDescriptor.valueParameters.forEach { p ->
fn.addValueParameter(p.name.identifier, p.type.toIrType())
}
fn.patchDeclarationParents(fn.parent)
assert(fn.body == null) { "expected body to be null" }
}
val newCallee = symbolRemapper.getReferencedSimpleFunction(newFn.symbol)
return shallowCopyCall(expression, newCallee).apply {
copyRemappedTypeArgumentsFrom(expression)
transformValueArguments(expression)
}
}
// If we are calling an external function, we want to "remap" the types of its signature
// as well, since if it is @Composable it will have its unmodified signature. These
// functions won't be traversed by default by the DeepCopyIrTreeWithSymbols so we have to
// do it ourself here.
if (
ownerFn != null &&
ownerFn.origin == IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
) {
symbolRemapper.visitSimpleFunction(ownerFn)
val newFn = super.visitSimpleFunction(ownerFn).also {
it.parent = ownerFn.parent
it.correspondingPropertySymbol = ownerFn.correspondingPropertySymbol
it.patchDeclarationParents(it.parent)
}
val newCallee = symbolRemapper.getReferencedSimpleFunction(newFn.symbol)
return shallowCopyCall(expression, newCallee).apply {
copyRemappedTypeArgumentsFrom(expression)
transformValueArguments(expression)
}
}
return super.visitCall(expression)
}
/* copied verbatim from DeepCopyIrTreeWithSymbols, except with newCallee as a parameter */
private fun shallowCopyCall(expression: IrCall, newCallee: IrSimpleFunctionSymbol): IrCall {
return IrCallImpl(
expression.startOffset, expression.endOffset,
expression.type.remapType(),
newCallee,
newCallee.descriptor,
expression.typeArgumentsCount,
expression.valueArgumentsCount,
mapStatementOrigin(expression.origin),
symbolRemapper.getReferencedClassOrNull(expression.superQualifierSymbol)
).apply {
copyRemappedTypeArgumentsFrom(expression)
}.copyAttributes(expression)
}
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private fun IrMemberAccessExpression.copyRemappedTypeArgumentsFrom(other: IrMemberAccessExpression) {
assert(typeArgumentsCount == other.typeArgumentsCount) {
"Mismatching type arguments: $typeArgumentsCount vs ${other.typeArgumentsCount} "
}
for (i in 0 until typeArgumentsCount) {
putTypeArgument(i, other.getTypeArgument(i)?.remapType())
}
}
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private fun <T : IrMemberAccessExpression> T.transformValueArguments(original: T) {
transformReceiverArguments(original)
for (i in 0 until original.valueArgumentsCount) {
putValueArgument(i, original.getValueArgument(i)?.transform())
}
}
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private fun <T : IrMemberAccessExpression> T.transformReceiverArguments(original: T): T =
apply {
dispatchReceiver = original.dispatchReceiver?.transform()
extensionReceiver = original.extensionReceiver?.transform()
}
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private inline fun <reified T : IrElement> T.transform() =
transform(this@DeepCopyIrTreeWithSymbolsPreservingMetadata, null) as T
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private fun IrType.remapType() = typeRemapper.remapType(this)
/* copied verbatim from DeepCopyIrTreeWithSymbols */
private fun mapStatementOrigin(origin: IrStatementOrigin?) = origin
private fun IrElement.copyMetadataFrom(owner: IrMetadataSourceOwner) {
when (this) {
is IrPropertyImpl -> metadata = owner.metadata
is IrFunctionBase -> metadata = owner.metadata
is IrClassImpl -> metadata = owner.metadata
}
}
private fun IrType.isComposable(): Boolean {
return annotations.hasAnnotation(ComposeFqNames.Composable)
}
private fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
}
class ComposerTypeRemapper(
private val context: JvmBackendContext,
private val symbolRemapper: SymbolRemapper,
private val typeTranslator: TypeTranslator,
private val composerTypeDescriptor: ClassDescriptor
) : TypeRemapper {
lateinit var deepCopy: IrElementTransformerVoid
private val scopeStack = mutableListOf<IrTypeParametersContainer>()
private val shouldTransform: Boolean get() {
// we don't want to remap the types of composable decoys. they are there specifically for
// their types to be unaltered!
return scopeStack.isEmpty() || scopeStack.last().origin != COMPOSABLE_DECOY_IMPL
}
override fun enterScope(irTypeParametersContainer: IrTypeParametersContainer) {
scopeStack.add(irTypeParametersContainer)
}
override fun leaveScope() {
scopeStack.pop()
}
private fun IrType.isComposable(): Boolean {
return annotations.hasAnnotation(ComposeFqNames.Composable)
}
private fun KotlinType.toIrType(): IrType = typeTranslator.translateType(this)
override fun remapType(type: IrType): IrType {
// TODO(lmr):
// This is basically creating the KotlinType and then converting to an IrType. Consider
// rewriting to just create the IrType directly, which would probably be more efficient.
if (type !is IrSimpleType) return type
if (!type.isFunction()) return underlyingRemapType(type)
if (!type.isComposable()) return underlyingRemapType(type)
if (!shouldTransform) return underlyingRemapType(type)
val oldArguments = type.toKotlinType().arguments
val newArguments =
oldArguments.subList(0, oldArguments.size - 1) +
TypeProjectionImpl(composerTypeDescriptor.defaultType) +
oldArguments.last()
val transformedComposableType = context
.irBuiltIns
.builtIns
.getFunction(oldArguments.size) // return type is an argument, so this is n + 1
.defaultType
.replace(newArguments)
.toIrType()
.withHasQuestionMark(type.hasQuestionMark) as IrSimpleType
return underlyingRemapType(transformedComposableType)
}
private fun underlyingRemapType(type: IrSimpleType): IrType {
return IrSimpleTypeImpl(
null,
symbolRemapper.getReferencedClassifier(type.classifier),
type.hasQuestionMark,
type.arguments.map { remapTypeArgument(it) },
type.annotations.map { it.transform(deepCopy, null) as IrConstructorCall },
type.abbreviation?.remapTypeAbbreviation()
)
}
private fun remapTypeArgument(typeArgument: IrTypeArgument): IrTypeArgument =
if (typeArgument is IrTypeProjection)
makeTypeProjection(this.remapType(typeArgument.type), typeArgument.variance)
else
typeArgument
private fun IrTypeAbbreviation.remapTypeAbbreviation() =
IrTypeAbbreviationImpl(
symbolRemapper.getReferencedTypeAlias(typeAlias),
hasQuestionMark,
arguments.map { remapTypeArgument(it) },
annotations
)
}