[go: nahoru, domu]

blob: cd726fb0fba97e927145b512aeef4d40a9481612 [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.compiler.plugins.kotlin.lower.decoys
import androidx.compose.compiler.plugins.kotlin.lower.hasAnnotationSafe
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContextImpl
import org.jetbrains.kotlin.backend.common.ir.isTopLevel
import org.jetbrains.kotlin.backend.common.ir.remapTypeParameters
import org.jetbrains.kotlin.backend.common.serialization.IrModuleDeserializer
import org.jetbrains.kotlin.backend.common.serialization.KotlinIrLinker
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData.SymbolKind.FUNCTION_SYMBOL
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationContainer
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.linkage.IrDeserializer
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.TypeRemapper
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.module
internal interface DecoyTransformBase {
val context: IrPluginContext
val signatureBuilder: IdSignatureSerializer
fun IrFunction.getSignatureId(): Long {
val signature = symbol.signature
?: signatureBuilder.composeSignatureForDeclaration(this)
return signature.getSignatureId()
}
private fun IdSignature.getSignatureId(): Long {
return when (this) {
is IdSignature.PublicSignature -> id!!
is IdSignature.AccessorSignature -> accessorSignature.id!!
is IdSignature.FileLocalSignature -> id
is IdSignature.ScopeLocalDeclaration -> id.toLong()
is IdSignature.SpecialFakeOverrideSignature -> memberSignature.getSignatureId()
}
}
fun irVarargString(valueArguments: List<String>): IrExpression {
val stringArrayType = IrSimpleTypeImpl(
classifier = context.irBuiltIns.arrayClass,
hasQuestionMark = false,
arguments = listOf(context.irBuiltIns.stringType as IrTypeArgument),
annotations = emptyList()
)
return IrVarargImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
type = stringArrayType,
varargElementType = context.irBuiltIns.stringType,
elements = valueArguments.map { it.toIrConst(context.irBuiltIns.stringType) }
)
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun IrFunction.getComposableForDecoy(): IrFunctionSymbol {
val implementationName = getDecoyTargetName()
val signatureId = getSignatureId()
val implementation = (parent as? IrDeclarationContainer)?.declarations
?.filterIsInstance<IrFunction>()
?.firstOrNull {
it.getDecoyImplementationName() == implementationName &&
it.getDecoyImplementationId() == signatureId
}
if (implementation != null) {
return implementation.symbol
}
val signature = getDecoySignature()
require(signature.size == 4) {
"Could not find local implementation for $implementationName"
}
// top-level
val idSig = IdSignature.PublicSignature(
packageFqName = signature[0],
declarationFqName = signature[1],
id = signature[2].toLongOrNull(),
mask = signature[3].toLong()
)
val linker = (context as IrPluginContextImpl).linker
val symbol = if (isTopLevel) {
linker.getDeclaration(module, idSig) as? IrSimpleFunctionSymbol
} else {
(parent as? IrDeclarationContainer)?.declarations
?.filterIsInstance<IrFunction>()
?.find {
it.symbol.signature == idSig
}
?.symbol
}
return symbol ?: error("Couldn't find implementation for $name")
}
fun IrFunction.getDecoyImplementationName(): String? {
val annotation = getAnnotation(DecoyFqNames.DecoyImplementation) ?: return null
@Suppress("UNCHECKED_CAST")
val decoyImplName = annotation.getValueArgument(0) as IrConst<String>
return decoyImplName.value
}
fun IrFunction.getDecoyImplementationId(): Long? {
val annotation = getAnnotation(DecoyFqNames.DecoyImplementation) ?: return null
@Suppress("UNCHECKED_CAST")
val decoyImplName = annotation.getValueArgument(1) as IrConst<Long>
return decoyImplName.value
}
private fun IrFunction.getDecoyTargetName(): String {
val annotation = getAnnotation(DecoyFqNames.Decoy)!!
@Suppress("UNCHECKED_CAST")
val decoyTargetName = annotation.getValueArgument(0) as IrConst<String>
return decoyTargetName.value
}
private fun IrFunction.getDecoySignature(): List<String> {
val annotation = getAnnotation(DecoyFqNames.Decoy)!!
val decoyVararg = annotation.getValueArgument(1) as IrVararg
@Suppress("UNCHECKED_CAST")
return decoyVararg.elements.map {
(it as IrConst<String>).value
}
}
// todo(KT-44100): functions generated by this plugin are not referenceable from other modules
private fun IrDeserializer.getDeclaration(
moduleDescriptor: ModuleDescriptor,
idSignature: IdSignature
): IrSymbol? {
val moduleDeserializerField =
KotlinIrLinker::class.java.getDeclaredField("deserializersForModules")
moduleDeserializerField.isAccessible = true
@Suppress("UNCHECKED_CAST")
val moduleMap = moduleDeserializerField.get(this)
as Map<ModuleDescriptor, IrModuleDeserializer>
val moduleDeserializer = moduleMap[moduleDescriptor] ?: return null
val symbol = moduleDeserializer.deserializeIrSymbol(idSignature, FUNCTION_SYMBOL)
moduleDeserializer.deserializeReachableDeclarations()
return symbol
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun IrDeclaration.isDecoy(): Boolean =
hasAnnotationSafe(DecoyFqNames.Decoy)
inline fun <reified T : IrElement> T.copyWithNewTypeParams(
source: IrFunction,
target: IrFunction
): T {
return deepCopyWithSymbols(target) { symbolRemapper, typeRemapper ->
val typeParamRemapper = object : TypeRemapper by typeRemapper {
override fun remapType(type: IrType): IrType {
return typeRemapper.remapType(type.remapTypeParameters(source, target))
}
}
val deepCopy = DeepCopyIrTreeWithSymbols(symbolRemapper, typeParamRemapper)
(typeRemapper as? DeepCopyTypeRemapper)?.deepCopy = deepCopy
deepCopy
}
}