mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
Merge pull request #10646 from tamasvajk/kotlin-java-kotlin-function-mapping
Kotlin: Simplify `kotlinFunctionToJavaEquivalent`
This commit is contained in:
@@ -1872,6 +1872,8 @@ open class KotlinFileExtractor(
|
||||
isFunction(target, "kotlin", "Double", fName)
|
||||
}
|
||||
|
||||
private fun isNumericFunction(target: IrFunction, fNames: List<String>) = fNames.any { isNumericFunction(target, it) }
|
||||
|
||||
private fun isArrayType(typeName: String) =
|
||||
when(typeName) {
|
||||
"Array" -> true
|
||||
@@ -1992,6 +1994,22 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
fun unaryopReceiver(id: Label<out DbExpr>, receiver: IrExpression?, receiverDescription: String) {
|
||||
val locId = tw.getLocation(c)
|
||||
tw.writeHasLocation(id, locId)
|
||||
tw.writeCallableEnclosingExpr(id, callable)
|
||||
tw.writeStatementEnclosingExpr(id, enclosingStmt)
|
||||
|
||||
if(receiver == null) {
|
||||
logger.errorElement("$receiverDescription not found", c)
|
||||
} else {
|
||||
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
|
||||
}
|
||||
if(c.valueArgumentsCount > 0) {
|
||||
logger.errorElement("Extra arguments found", c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the lhs of a binary op from this call's dispatch receiver, and the rhs from its sole argument.
|
||||
*/
|
||||
@@ -1999,56 +2017,87 @@ open class KotlinFileExtractor(
|
||||
binopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the lhs of a binary op from this call's extension receiver, and the rhs from its sole argument.
|
||||
*/
|
||||
fun binopExtensionMethod(id: Label<out DbExpr>) {
|
||||
binopReceiver(id, c.extensionReceiver, "Extension receiver")
|
||||
fun unaryopDisp(id: Label<out DbExpr>) {
|
||||
unaryopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
|
||||
}
|
||||
|
||||
val dr = c.dispatchReceiver
|
||||
when {
|
||||
isNumericFunction(target, "plus")
|
||||
|| isFunction(target, "kotlin", "String", "plus", false) -> {
|
||||
isFunction(target, "kotlin", "String", "plus", false) -> {
|
||||
val id = tw.getFreshIdLabel<DbAddexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
if (c.extensionReceiver != null)
|
||||
binopExtensionMethod(id)
|
||||
else
|
||||
binopDisp(id)
|
||||
binopDisp(id)
|
||||
}
|
||||
isFunction(target, "kotlin", "String", "plus", true) -> {
|
||||
findJdkIntrinsicOrWarn("stringPlus", c)?.let { stringPlusFn ->
|
||||
extractRawMethodAccess(stringPlusFn, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
|
||||
}
|
||||
}
|
||||
isNumericFunction(target, "minus") -> {
|
||||
val id = tw.getFreshIdLabel<DbSubexpr>()
|
||||
isNumericFunction(target, listOf("plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr")) -> {
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_subexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
binopDisp(id)
|
||||
}
|
||||
isNumericFunction(target, "times") -> {
|
||||
val id = tw.getFreshIdLabel<DbMulexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_mulexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
binopDisp(id)
|
||||
}
|
||||
isNumericFunction(target, "div") -> {
|
||||
val id = tw.getFreshIdLabel<DbDivexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_divexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
binopDisp(id)
|
||||
}
|
||||
isNumericFunction(target, "rem") -> {
|
||||
val id = tw.getFreshIdLabel<DbRemexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_remexpr(id, type.javaResult.id, parent, idx)
|
||||
val id: Label<out DbExpr> = when (val targetName = target.name.asString()) {
|
||||
"plus" -> {
|
||||
val id = tw.getFreshIdLabel<DbAddexpr>()
|
||||
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"minus" -> {
|
||||
val id = tw.getFreshIdLabel<DbSubexpr>()
|
||||
tw.writeExprs_subexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"times" -> {
|
||||
val id = tw.getFreshIdLabel<DbMulexpr>()
|
||||
tw.writeExprs_mulexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"div" -> {
|
||||
val id = tw.getFreshIdLabel<DbDivexpr>()
|
||||
tw.writeExprs_divexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"rem" -> {
|
||||
val id = tw.getFreshIdLabel<DbRemexpr>()
|
||||
tw.writeExprs_remexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"and" -> {
|
||||
val id = tw.getFreshIdLabel<DbAndbitexpr>()
|
||||
tw.writeExprs_andbitexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"or" -> {
|
||||
val id = tw.getFreshIdLabel<DbOrbitexpr>()
|
||||
tw.writeExprs_orbitexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"xor" -> {
|
||||
val id = tw.getFreshIdLabel<DbXorbitexpr>()
|
||||
tw.writeExprs_xorbitexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"shl" -> {
|
||||
val id = tw.getFreshIdLabel<DbLshiftexpr>()
|
||||
tw.writeExprs_lshiftexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"shr" -> {
|
||||
val id = tw.getFreshIdLabel<DbRshiftexpr>()
|
||||
tw.writeExprs_rshiftexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
"ushr" -> {
|
||||
val id = tw.getFreshIdLabel<DbUrshiftexpr>()
|
||||
tw.writeExprs_urshiftexpr(id, type.javaResult.id, parent, idx)
|
||||
id
|
||||
}
|
||||
else -> {
|
||||
logger.errorElement("Unhandled target name: $targetName", c)
|
||||
return
|
||||
}
|
||||
}
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
binopDisp(id)
|
||||
}
|
||||
@@ -2074,6 +2123,20 @@ open class KotlinFileExtractor(
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
binOp(id, dr, callable, enclosingStmt)
|
||||
}
|
||||
isFunction(target, "kotlin", "Boolean", "not") -> {
|
||||
val id = tw.getFreshIdLabel<DbLognotexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_lognotexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
unaryopDisp(id)
|
||||
}
|
||||
isNumericFunction(target, "inv") -> {
|
||||
val id = tw.getFreshIdLabel<DbBitnotexpr>()
|
||||
val type = useType(c.type)
|
||||
tw.writeExprs_bitnotexpr(id, type.javaResult.id, parent, idx)
|
||||
tw.writeExprsKotlinType(id, type.kotlinResult.id)
|
||||
unaryopDisp(id)
|
||||
}
|
||||
// We need to handle all the builtin operators defines in BuiltInOperatorNames in
|
||||
// compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt
|
||||
// as they can't be extracted as external dependencies.
|
||||
|
||||
@@ -1267,13 +1267,6 @@ open class KotlinUsesExtractor(
|
||||
return tw.lm.locallyVisibleFunctionLabelMapping[f]?.function
|
||||
}
|
||||
|
||||
// These are classes with Java equivalents, but whose methods don't all exist on those Java equivalents--
|
||||
// for example, the numeric classes define arithmetic functions (Int.plus, Long.or and so on) that lower to
|
||||
// primitive arithmetic on the JVM, but which we extract as calls to reflect the source syntax more closely.
|
||||
private val expectedMissingEquivalents = setOf(
|
||||
"kotlin.Boolean", "kotlin.Byte", "kotlin.Char", "kotlin.Double", "kotlin.Float", "kotlin.Int", "kotlin.Long", "kotlin.Number", "kotlin.Short"
|
||||
)
|
||||
|
||||
private fun kotlinFunctionToJavaEquivalent(f: IrFunction, noReplace: Boolean): IrFunction =
|
||||
if (noReplace)
|
||||
f
|
||||
@@ -1281,17 +1274,18 @@ open class KotlinUsesExtractor(
|
||||
f.parentClassOrNull?.let { parentClass ->
|
||||
getJavaEquivalentClass(parentClass)?.let { javaClass ->
|
||||
if (javaClass != parentClass) {
|
||||
val jvmName = getJvmName(f) ?: f.name.asString()
|
||||
var jvmName = getFunctionShortName(f).nameInDB
|
||||
if (f.name.asString() == "get" && parentClass.fqNameWhenAvailable?.asString() == "kotlin.String") {
|
||||
// `kotlin.String.get` has an equivalent `java.lang.String.get`, which in turn will be stored in the DB as `java.lang.String.charAt`.
|
||||
// Maybe all operators should be handled the same way, but so far I only found this case that needed to be special cased. This is the
|
||||
// only operator in `JvmNames.specialFunctions`
|
||||
jvmName = "get"
|
||||
}
|
||||
// Look for an exact type match...
|
||||
javaClass.declarations.findSubType<IrFunction> { decl ->
|
||||
decl.name.asString() == jvmName &&
|
||||
decl.valueParameters.size == f.valueParameters.size &&
|
||||
decl.valueParameters.zip(f.valueParameters).all { p -> erase(p.first.type) == erase(p.second.type) }
|
||||
} ?:
|
||||
// Or if there is none, look for the only viable overload
|
||||
javaClass.declarations.singleOrNullSubType<IrFunction> { decl ->
|
||||
decl.name.asString() == jvmName &&
|
||||
decl.valueParameters.size == f.valueParameters.size
|
||||
decl.valueParameters.zip(f.valueParameters).all { p -> erase(p.first.type).classifierOrNull == erase(p.second.type).classifierOrNull }
|
||||
} ?:
|
||||
// Or check property accessors:
|
||||
(f.propertyIfAccessor as? IrProperty)?.let { kotlinProp ->
|
||||
@@ -1305,9 +1299,7 @@ open class KotlinUsesExtractor(
|
||||
else null
|
||||
} ?: run {
|
||||
val parentFqName = parentClass.fqNameWhenAvailable?.asString()
|
||||
if (!expectedMissingEquivalents.contains(parentFqName)) {
|
||||
logger.warn("Couldn't find a Java equivalent function to $parentFqName.${f.name} in ${javaClass.fqNameWhenAvailable}")
|
||||
}
|
||||
logger.warn("Couldn't find a Java equivalent function to $parentFqName.${f.name.asString()} in ${javaClass.fqNameWhenAvailable?.asString()}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,3 @@ inline fun <reified S: IrDeclaration> Iterable<IrDeclaration>.findSubType(
|
||||
): S? {
|
||||
return this.find { it is S && predicate(it) } as S?
|
||||
}
|
||||
|
||||
/**
|
||||
* This behaves the same as Iterable<IrDeclaration>.singleOrNull, but
|
||||
* requires that the value found is of the subtype S, and it casts
|
||||
* the result for you appropriately.
|
||||
*/
|
||||
inline fun <reified S: IrDeclaration> Iterable<IrDeclaration>.singleOrNullSubType(
|
||||
predicate: (S) -> Boolean
|
||||
): S? {
|
||||
return this.singleOrNull { it is S && predicate(it) } as S?
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user