Skip to content

Commit db673c0

Browse files
authored
Merge pull request #10646 from tamasvajk/kotlin-java-kotlin-function-mapping
Kotlin: Simplify `kotlinFunctionToJavaEquivalent`
2 parents d5478a0 + 2c46737 commit db673c0

File tree

17 files changed

+361
-139
lines changed

17 files changed

+361
-139
lines changed

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 98 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,8 @@ open class KotlinFileExtractor(
18721872
isFunction(target, "kotlin", "Double", fName)
18731873
}
18741874

1875+
private fun isNumericFunction(target: IrFunction, fNames: List<String>) = fNames.any { isNumericFunction(target, it) }
1876+
18751877
private fun isArrayType(typeName: String) =
18761878
when(typeName) {
18771879
"Array" -> true
@@ -1992,63 +1994,110 @@ open class KotlinFileExtractor(
19921994
}
19931995
}
19941996

1997+
fun unaryopReceiver(id: Label<out DbExpr>, receiver: IrExpression?, receiverDescription: String) {
1998+
val locId = tw.getLocation(c)
1999+
tw.writeHasLocation(id, locId)
2000+
tw.writeCallableEnclosingExpr(id, callable)
2001+
tw.writeStatementEnclosingExpr(id, enclosingStmt)
2002+
2003+
if(receiver == null) {
2004+
logger.errorElement("$receiverDescription not found", c)
2005+
} else {
2006+
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
2007+
}
2008+
if(c.valueArgumentsCount > 0) {
2009+
logger.errorElement("Extra arguments found", c)
2010+
}
2011+
}
2012+
19952013
/**
19962014
* Populate the lhs of a binary op from this call's dispatch receiver, and the rhs from its sole argument.
19972015
*/
19982016
fun binopDisp(id: Label<out DbExpr>) {
19992017
binopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
20002018
}
20012019

2002-
/**
2003-
* Populate the lhs of a binary op from this call's extension receiver, and the rhs from its sole argument.
2004-
*/
2005-
fun binopExtensionMethod(id: Label<out DbExpr>) {
2006-
binopReceiver(id, c.extensionReceiver, "Extension receiver")
2020+
fun unaryopDisp(id: Label<out DbExpr>) {
2021+
unaryopReceiver(id, c.dispatchReceiver, "Dispatch receiver")
20072022
}
20082023

20092024
val dr = c.dispatchReceiver
20102025
when {
2011-
isNumericFunction(target, "plus")
2012-
|| isFunction(target, "kotlin", "String", "plus", false) -> {
2026+
isFunction(target, "kotlin", "String", "plus", false) -> {
20132027
val id = tw.getFreshIdLabel<DbAddexpr>()
20142028
val type = useType(c.type)
20152029
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
20162030
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2017-
if (c.extensionReceiver != null)
2018-
binopExtensionMethod(id)
2019-
else
2020-
binopDisp(id)
2031+
binopDisp(id)
20212032
}
20222033
isFunction(target, "kotlin", "String", "plus", true) -> {
20232034
findJdkIntrinsicOrWarn("stringPlus", c)?.let { stringPlusFn ->
20242035
extractRawMethodAccess(stringPlusFn, c, callable, parent, idx, enclosingStmt, listOf(c.extensionReceiver, c.getValueArgument(0)), null, null)
20252036
}
20262037
}
2027-
isNumericFunction(target, "minus") -> {
2028-
val id = tw.getFreshIdLabel<DbSubexpr>()
2029-
val type = useType(c.type)
2030-
tw.writeExprs_subexpr(id, type.javaResult.id, parent, idx)
2031-
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2032-
binopDisp(id)
2033-
}
2034-
isNumericFunction(target, "times") -> {
2035-
val id = tw.getFreshIdLabel<DbMulexpr>()
2038+
isNumericFunction(target, listOf("plus", "minus", "times", "div", "rem", "and", "or", "xor", "shl", "shr", "ushr")) -> {
20362039
val type = useType(c.type)
2037-
tw.writeExprs_mulexpr(id, type.javaResult.id, parent, idx)
2038-
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2039-
binopDisp(id)
2040-
}
2041-
isNumericFunction(target, "div") -> {
2042-
val id = tw.getFreshIdLabel<DbDivexpr>()
2043-
val type = useType(c.type)
2044-
tw.writeExprs_divexpr(id, type.javaResult.id, parent, idx)
2045-
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2046-
binopDisp(id)
2047-
}
2048-
isNumericFunction(target, "rem") -> {
2049-
val id = tw.getFreshIdLabel<DbRemexpr>()
2050-
val type = useType(c.type)
2051-
tw.writeExprs_remexpr(id, type.javaResult.id, parent, idx)
2040+
val id: Label<out DbExpr> = when (val targetName = target.name.asString()) {
2041+
"plus" -> {
2042+
val id = tw.getFreshIdLabel<DbAddexpr>()
2043+
tw.writeExprs_addexpr(id, type.javaResult.id, parent, idx)
2044+
id
2045+
}
2046+
"minus" -> {
2047+
val id = tw.getFreshIdLabel<DbSubexpr>()
2048+
tw.writeExprs_subexpr(id, type.javaResult.id, parent, idx)
2049+
id
2050+
}
2051+
"times" -> {
2052+
val id = tw.getFreshIdLabel<DbMulexpr>()
2053+
tw.writeExprs_mulexpr(id, type.javaResult.id, parent, idx)
2054+
id
2055+
}
2056+
"div" -> {
2057+
val id = tw.getFreshIdLabel<DbDivexpr>()
2058+
tw.writeExprs_divexpr(id, type.javaResult.id, parent, idx)
2059+
id
2060+
}
2061+
"rem" -> {
2062+
val id = tw.getFreshIdLabel<DbRemexpr>()
2063+
tw.writeExprs_remexpr(id, type.javaResult.id, parent, idx)
2064+
id
2065+
}
2066+
"and" -> {
2067+
val id = tw.getFreshIdLabel<DbAndbitexpr>()
2068+
tw.writeExprs_andbitexpr(id, type.javaResult.id, parent, idx)
2069+
id
2070+
}
2071+
"or" -> {
2072+
val id = tw.getFreshIdLabel<DbOrbitexpr>()
2073+
tw.writeExprs_orbitexpr(id, type.javaResult.id, parent, idx)
2074+
id
2075+
}
2076+
"xor" -> {
2077+
val id = tw.getFreshIdLabel<DbXorbitexpr>()
2078+
tw.writeExprs_xorbitexpr(id, type.javaResult.id, parent, idx)
2079+
id
2080+
}
2081+
"shl" -> {
2082+
val id = tw.getFreshIdLabel<DbLshiftexpr>()
2083+
tw.writeExprs_lshiftexpr(id, type.javaResult.id, parent, idx)
2084+
id
2085+
}
2086+
"shr" -> {
2087+
val id = tw.getFreshIdLabel<DbRshiftexpr>()
2088+
tw.writeExprs_rshiftexpr(id, type.javaResult.id, parent, idx)
2089+
id
2090+
}
2091+
"ushr" -> {
2092+
val id = tw.getFreshIdLabel<DbUrshiftexpr>()
2093+
tw.writeExprs_urshiftexpr(id, type.javaResult.id, parent, idx)
2094+
id
2095+
}
2096+
else -> {
2097+
logger.errorElement("Unhandled target name: $targetName", c)
2098+
return
2099+
}
2100+
}
20522101
tw.writeExprsKotlinType(id, type.kotlinResult.id)
20532102
binopDisp(id)
20542103
}
@@ -2074,6 +2123,20 @@ open class KotlinFileExtractor(
20742123
tw.writeExprsKotlinType(id, type.kotlinResult.id)
20752124
binOp(id, dr, callable, enclosingStmt)
20762125
}
2126+
isFunction(target, "kotlin", "Boolean", "not") -> {
2127+
val id = tw.getFreshIdLabel<DbLognotexpr>()
2128+
val type = useType(c.type)
2129+
tw.writeExprs_lognotexpr(id, type.javaResult.id, parent, idx)
2130+
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2131+
unaryopDisp(id)
2132+
}
2133+
isNumericFunction(target, "inv") -> {
2134+
val id = tw.getFreshIdLabel<DbBitnotexpr>()
2135+
val type = useType(c.type)
2136+
tw.writeExprs_bitnotexpr(id, type.javaResult.id, parent, idx)
2137+
tw.writeExprsKotlinType(id, type.kotlinResult.id)
2138+
unaryopDisp(id)
2139+
}
20772140
// We need to handle all the builtin operators defines in BuiltInOperatorNames in
20782141
// compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/IrBuiltIns.kt
20792142
// as they can't be extracted as external dependencies.

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,31 +1267,25 @@ open class KotlinUsesExtractor(
12671267
return tw.lm.locallyVisibleFunctionLabelMapping[f]?.function
12681268
}
12691269

1270-
// These are classes with Java equivalents, but whose methods don't all exist on those Java equivalents--
1271-
// for example, the numeric classes define arithmetic functions (Int.plus, Long.or and so on) that lower to
1272-
// primitive arithmetic on the JVM, but which we extract as calls to reflect the source syntax more closely.
1273-
private val expectedMissingEquivalents = setOf(
1274-
"kotlin.Boolean", "kotlin.Byte", "kotlin.Char", "kotlin.Double", "kotlin.Float", "kotlin.Int", "kotlin.Long", "kotlin.Number", "kotlin.Short"
1275-
)
1276-
12771270
private fun kotlinFunctionToJavaEquivalent(f: IrFunction, noReplace: Boolean): IrFunction =
12781271
if (noReplace)
12791272
f
12801273
else
12811274
f.parentClassOrNull?.let { parentClass ->
12821275
getJavaEquivalentClass(parentClass)?.let { javaClass ->
12831276
if (javaClass != parentClass) {
1284-
val jvmName = getJvmName(f) ?: f.name.asString()
1277+
var jvmName = getFunctionShortName(f).nameInDB
1278+
if (f.name.asString() == "get" && parentClass.fqNameWhenAvailable?.asString() == "kotlin.String") {
1279+
// `kotlin.String.get` has an equivalent `java.lang.String.get`, which in turn will be stored in the DB as `java.lang.String.charAt`.
1280+
// 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
1281+
// only operator in `JvmNames.specialFunctions`
1282+
jvmName = "get"
1283+
}
12851284
// Look for an exact type match...
12861285
javaClass.declarations.findSubType<IrFunction> { decl ->
12871286
decl.name.asString() == jvmName &&
12881287
decl.valueParameters.size == f.valueParameters.size &&
1289-
decl.valueParameters.zip(f.valueParameters).all { p -> erase(p.first.type) == erase(p.second.type) }
1290-
} ?:
1291-
// Or if there is none, look for the only viable overload
1292-
javaClass.declarations.singleOrNullSubType<IrFunction> { decl ->
1293-
decl.name.asString() == jvmName &&
1294-
decl.valueParameters.size == f.valueParameters.size
1288+
decl.valueParameters.zip(f.valueParameters).all { p -> erase(p.first.type).classifierOrNull == erase(p.second.type).classifierOrNull }
12951289
} ?:
12961290
// Or check property accessors:
12971291
(f.propertyIfAccessor as? IrProperty)?.let { kotlinProp ->
@@ -1305,9 +1299,7 @@ open class KotlinUsesExtractor(
13051299
else null
13061300
} ?: run {
13071301
val parentFqName = parentClass.fqNameWhenAvailable?.asString()
1308-
if (!expectedMissingEquivalents.contains(parentFqName)) {
1309-
logger.warn("Couldn't find a Java equivalent function to $parentFqName.${f.name} in ${javaClass.fqNameWhenAvailable}")
1310-
}
1302+
logger.warn("Couldn't find a Java equivalent function to $parentFqName.${f.name.asString()} in ${javaClass.fqNameWhenAvailable?.asString()}")
13111303
null
13121304
}
13131305
}

java/kotlin-extractor/src/main/kotlin/utils/Iterable.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,3 @@ inline fun <reified S: IrDeclaration> Iterable<IrDeclaration>.findSubType(
1212
): S? {
1313
return this.find { it is S && predicate(it) } as S?
1414
}
15-
16-
/**
17-
* This behaves the same as Iterable<IrDeclaration>.singleOrNull, but
18-
* requires that the value found is of the subtype S, and it casts
19-
* the result for you appropriately.
20-
*/
21-
inline fun <reified S: IrDeclaration> Iterable<IrDeclaration>.singleOrNullSubType(
22-
predicate: (S) -> Boolean
23-
): S? {
24-
return this.singleOrNull { it is S && predicate(it) } as S?
25-
}
26-
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import java
2+
import semmle.code.java.Diagnostics
3+
4+
from Diagnostic d
5+
where exists(d.getMessage().indexOf("Couldn't find a Java equivalent function to "))
6+
select d

java/ql/integration-tests/posix-only/kotlin/gradle_kotlinx_serialization/PrintAst.expected

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ app/src/main/kotlin/testProject/App.kt:
1717
# 7| 0: [WhenBranch] ... -> ...
1818
# 7| 0: [ValueNEExpr] ... (value not-equals) ...
1919
# 7| 0: [IntegerLiteral] 3
20-
# 7| 1: [MethodAccess] and(...)
21-
# 7| -1: [IntegerLiteral] 3
22-
# 7| 0: [VarAccess] seen1
20+
# 7| 1: [AndBitwiseExpr] ... & ...
21+
# 7| 0: [IntegerLiteral] 3
22+
# 7| 1: [VarAccess] seen1
2323
# 7| 1: [ExprStmt] <Expr>;
2424
# 7| 0: [MethodAccess] throwMissingFieldException(...)
2525
# 7| -1: [TypeAccess] PluginExceptionsKt
@@ -232,9 +232,9 @@ app/src/main/kotlin/testProject/App.kt:
232232
# 7| 1: [ExprStmt] <Expr>;
233233
# 7| 0: [AssignExpr] ...=...
234234
# 7| 0: [VarAccess] tmp3_bitMask0
235-
# 7| 1: [MethodAccess] or(...)
236-
# 7| -1: [VarAccess] tmp3_bitMask0
237-
# 7| 0: [IntegerLiteral] 1
235+
# 7| 1: [OrBitwiseExpr] ... | ...
236+
# 7| 0: [VarAccess] tmp3_bitMask0
237+
# 7| 1: [IntegerLiteral] 1
238238
# 7| 1: [BlockStmt] { ... }
239239
# 7| 0: [ExprStmt] <Expr>;
240240
# 7| 0: [AssignExpr] ...=...
@@ -246,9 +246,9 @@ app/src/main/kotlin/testProject/App.kt:
246246
# 7| 1: [ExprStmt] <Expr>;
247247
# 7| 0: [AssignExpr] ...=...
248248
# 7| 0: [VarAccess] tmp3_bitMask0
249-
# 7| 1: [MethodAccess] or(...)
250-
# 7| -1: [VarAccess] tmp3_bitMask0
251-
# 7| 0: [IntegerLiteral] 2
249+
# 7| 1: [OrBitwiseExpr] ... | ...
250+
# 7| 0: [VarAccess] tmp3_bitMask0
251+
# 7| 1: [IntegerLiteral] 2
252252
# 7| 1: [WhenBranch] ... -> ...
253253
# 7| 0: [BooleanLiteral] true
254254
# 7| 1: [WhileStmt] while (...)
@@ -285,9 +285,9 @@ app/src/main/kotlin/testProject/App.kt:
285285
# 7| 1: [ExprStmt] <Expr>;
286286
# 7| 0: [AssignExpr] ...=...
287287
# 7| 0: [VarAccess] tmp3_bitMask0
288-
# 7| 1: [MethodAccess] or(...)
289-
# 7| -1: [VarAccess] tmp3_bitMask0
290-
# 7| 0: [IntegerLiteral] 1
288+
# 7| 1: [OrBitwiseExpr] ... | ...
289+
# 7| 0: [VarAccess] tmp3_bitMask0
290+
# 7| 1: [IntegerLiteral] 1
291291
# 7| 2: [WhenBranch] ... -> ...
292292
# 7| 0: [ValueEQExpr] ... (value equals) ...
293293
# 7| 0: [VarAccess] tmp2_index
@@ -303,9 +303,9 @@ app/src/main/kotlin/testProject/App.kt:
303303
# 7| 1: [ExprStmt] <Expr>;
304304
# 7| 0: [AssignExpr] ...=...
305305
# 7| 0: [VarAccess] tmp3_bitMask0
306-
# 7| 1: [MethodAccess] or(...)
307-
# 7| -1: [VarAccess] tmp3_bitMask0
308-
# 7| 0: [IntegerLiteral] 2
306+
# 7| 1: [OrBitwiseExpr] ... | ...
307+
# 7| 0: [VarAccess] tmp3_bitMask0
308+
# 7| 1: [IntegerLiteral] 2
309309
# 7| 3: [WhenBranch] ... -> ...
310310
# 7| 0: [BooleanLiteral] true
311311
# 7| 1: [ThrowStmt] throw ...
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.dec in java.lang.Integer |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.dec in java.lang.Integer |
2+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.dec in java.lang.Integer |
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
2+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
3+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
4+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
5+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
6+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
7+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
8+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
9+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
10+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
11+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
12+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Byte.toInt in java.lang.Byte |
13+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.dec in java.lang.Integer |
14+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.rangeTo in java.lang.Integer |
15+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Int.rangeTo in java.lang.Integer |
16+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
17+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
18+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
19+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
20+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
21+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
22+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
23+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
24+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
25+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
26+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |
27+
| file://:0:0:0:0 | Couldn't find a Java equivalent function to kotlin.Short.toInt in java.lang.Short |

0 commit comments

Comments
 (0)