Extract functions using their name from JvmName annotation

This commit is contained in:
Tamas Vajk
2022-04-20 11:09:05 +02:00
committed by Ian Lynagh
parent a24753f552
commit b66a3141f6
9 changed files with 98 additions and 20 deletions

View File

@@ -721,12 +721,16 @@ open class KotlinFileExtractor(
val returnType = useType(substReturnType, TypeContext.RETURN)
val shortName = getFunctionShortName(f)
val methodId = id.cast<DbMethod>()
tw.writeMethods(methodId, shortName, "$shortName$paramsSignature", returnType.javaResult.id, parentId, sourceDeclaration.cast<DbMethod>())
tw.writeMethods(methodId, shortName.nameInDB, "${shortName.nameInDB}$paramsSignature", returnType.javaResult.id, parentId, sourceDeclaration.cast<DbMethod>())
tw.writeMethodsKotlinType(methodId, returnType.kotlinResult.id)
if (!isExternalDeclaration(f)) {
extractTypeAccessRecursive(f.returnType, locId, id, -1)
}
if (shortName.nameInDB != shortName.kotlinName) {
tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName)
}
}
tw.writeHasLocation(id, locId)

View File

@@ -6,6 +6,7 @@ import com.semmle.extractor.java.OdasaOutput
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConst
@@ -48,29 +49,33 @@ open class KotlinUsesExtractor(
TypeResult(fakeKotlinType(), "", "")
)
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
fun extractFileClass(f: IrFile): Label<out DbClass> {
val fileName = f.fileEntry.name
val pkg = f.fqName.asString()
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
var jvmName = defaultName
for(a: IrConstructorCall in f.annotations) {
fun getJvmName(container: IrAnnotationContainer): String? {
for(a: IrConstructorCall in container.annotations) {
val t = a.type
if(t is IrSimpleType && a.valueArgumentsCount == 1) {
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
val owner = t.classifier.owner
val v = a.getValueArgument(0)
if(owner is IrClass) {
if (owner is IrClass) {
val aPkg = owner.packageFqName?.asString()
val name = owner.name.asString()
if(aPkg == "kotlin.jvm" && name == "JvmName" && v is IrConst<*>) {
val value = v.value
if(value is String) {
jvmName = value
if (value is String) {
return value
}
}
}
}
}
return null
}
@OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5
fun extractFileClass(f: IrFile): Label<out DbClass> {
val fileName = f.fileEntry.name
val pkg = f.fqName.asString()
val defaultName = fileName.replaceFirst(Regex(""".*[/\\]"""), "").replaceFirst(Regex("""\.kt$"""), "").replaceFirstChar({ it.uppercase() }) + "Kt"
var jvmName = getJvmName(f) ?: defaultName
val qualClassName = if (pkg.isEmpty()) jvmName else "$pkg.$jvmName"
val label = "@\"class;$qualClassName\""
val id: Label<DbClass> = tw.getLabelFor(label, {
@@ -668,14 +673,31 @@ open class KotlinUsesExtractor(
private val IrDeclaration.isAnonymousFunction get() = this is IrSimpleFunction && name == SpecialNames.NO_NAME_PROVIDED
fun getFunctionShortName(f: IrFunction) : String {
data class FunctionNames(val nameInDB: String, val kotlinName: String)
fun getFunctionShortName(f: IrFunction) : FunctionNames {
if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction)
return OperatorNameConventions.INVOKE.asString()
return FunctionNames(
OperatorNameConventions.INVOKE.asString(),
OperatorNameConventions.INVOKE.asString())
(f as? IrSimpleFunction)?.correspondingPropertySymbol?.let {
val propName = it.owner.name.asString()
when(f) {
it.owner.getter -> return JvmAbi.getterName(propName)
it.owner.setter -> return JvmAbi.setterName(propName)
val getter = it.owner.getter
val setter = it.owner.setter
if (it.owner.parentClassOrNull?.kind == ClassKind.ANNOTATION_CLASS) {
if (getter == null) {
logger.error("Expected to find a getter for a property inside an annotation class")
return FunctionNames(propName, propName)
} else {
val jvmName = getJvmName(getter)
return FunctionNames(jvmName ?: propName, propName)
}
}
when (f) {
getter -> return FunctionNames(getJvmName(getter) ?: JvmAbi.getterName(propName), JvmAbi.getterName(propName))
setter -> return FunctionNames(getJvmName(setter) ?: JvmAbi.setterName(propName), JvmAbi.setterName(propName))
else -> {
logger.error(
"Function has a corresponding property, but is neither the getter nor the setter"
@@ -683,7 +705,7 @@ open class KotlinUsesExtractor(
}
}
}
return f.name.asString()
return FunctionNames(getJvmName(f) ?: f.name.asString(), f.name.asString())
}
// This excludes class type parameters that show up in (at least) constructors' typeParameters list.
@@ -727,7 +749,7 @@ open class KotlinUsesExtractor(
* allow it to be passed in.
*/
fun getFunctionLabel(f: IrFunction, maybeParentId: Label<out DbElement>?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
getFunctionLabel(f.parent, maybeParentId, getFunctionShortName(f), f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses)
getFunctionLabel(f.parent, maybeParentId, getFunctionShortName(f).nameInDB, f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses)
/*
* This function actually generates the label for a function.