Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7cb6e19

Browse files
smowtonigfoo
authored andcommitted
Extract array update operations
These are of the form arrExpr[indexExpr] op= rhs
1 parent d9c72b1 commit 7cb6e19

5 files changed

Lines changed: 181 additions & 68 deletions

File tree

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

Lines changed: 138 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,24 +1206,24 @@ open class KotlinFileExtractor(
12061206
isFunction(target, "kotlin", "Double", fName)
12071207
}
12081208

1209+
fun isArrayType(typeName: String) =
1210+
when(typeName) {
1211+
"Array" -> true
1212+
"IntArray" -> true
1213+
"ByteArray" -> true
1214+
"ShortArray" -> true
1215+
"LongArray" -> true
1216+
"FloatArray" -> true
1217+
"DoubleArray" -> true
1218+
"CharArray" -> true
1219+
"BooleanArray" -> true
1220+
else -> false
1221+
}
1222+
12091223
fun extractCall(c: IrCall, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
12101224
with("call", c) {
12111225
val target = c.symbol.owner
12121226

1213-
fun isArrayType(typeName: String) =
1214-
when(typeName) {
1215-
"Array" -> true
1216-
"IntArray" -> true
1217-
"ByteArray" -> true
1218-
"ShortArray" -> true
1219-
"LongArray" -> true
1220-
"FloatArray" -> true
1221-
"DoubleArray" -> true
1222-
"CharArray" -> true
1223-
"BooleanArray" -> true
1224-
else -> false
1225-
}
1226-
12271227
fun extractMethodAccess(syntacticCallTarget: IrFunction, extractMethodTypeArguments: Boolean = true, extractClassTypeArguments: Boolean = false) {
12281228
val typeArgs =
12291229
if (extractMethodTypeArguments)
@@ -1782,6 +1782,113 @@ open class KotlinFileExtractor(
17821782
}
17831783
}
17841784

1785+
fun getStatementOriginOperator(origin: IrStatementOrigin?) = when (origin) {
1786+
IrStatementOrigin.PLUSEQ -> "plus"
1787+
IrStatementOrigin.MINUSEQ -> "minus"
1788+
IrStatementOrigin.MULTEQ -> "times"
1789+
IrStatementOrigin.DIVEQ -> "div"
1790+
IrStatementOrigin.PERCEQ -> "rem"
1791+
else -> null
1792+
}
1793+
1794+
fun getUpdateInPlaceRHS(origin: IrStatementOrigin?, isExpectedLhs: (IrExpression?) -> Boolean, updateRhs: IrExpression): IrExpression? {
1795+
// Check for a desugared in-place update operator, such as "v += e":
1796+
return getStatementOriginOperator(origin)?.let {
1797+
if (updateRhs is IrCall &&
1798+
isNumericFunction(updateRhs.symbol.owner, it)
1799+
) {
1800+
// Check for an expression like x = get(x).op(e):
1801+
val opReceiver = updateRhs.dispatchReceiver
1802+
if (isExpectedLhs(opReceiver)) {
1803+
updateRhs.getValueArgument(0)
1804+
} else null
1805+
} else null
1806+
} ?: null
1807+
}
1808+
1809+
fun writeUpdateInPlaceExpr(origin: IrStatementOrigin, tw: TrapWriter, id: Label<DbAssignexpr>, type: TypeResults, exprParent: ExprParent): Boolean {
1810+
when(origin) {
1811+
IrStatementOrigin.PLUSEQ -> tw.writeExprs_assignaddexpr(id as Label<DbAssignaddexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
1812+
IrStatementOrigin.MINUSEQ -> tw.writeExprs_assignsubexpr(id as Label<DbAssignsubexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
1813+
IrStatementOrigin.MULTEQ -> tw.writeExprs_assignmulexpr(id as Label<DbAssignmulexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
1814+
IrStatementOrigin.DIVEQ -> tw.writeExprs_assigndivexpr(id as Label<DbAssigndivexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
1815+
IrStatementOrigin.PERCEQ -> tw.writeExprs_assignremexpr(id as Label<DbAssignremexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
1816+
else -> return false
1817+
}
1818+
return true
1819+
}
1820+
1821+
fun tryExtractArrayUpdate(e: IrContainerExpression, callable: Label<out DbCallable>, parent: StmtExprParent): Boolean {
1822+
/*
1823+
* We're expecting the pattern
1824+
* {
1825+
* val array = e1
1826+
* val idx = e2
1827+
* array.set(idx, array.get(idx).op(e3))
1828+
* }
1829+
*
1830+
* If we find it, we'll extract e1[e2] op= e3 (op is +, -, ...)
1831+
*/
1832+
if(e.statements.size != 3)
1833+
return false
1834+
(e.statements[0] as? IrVariable)?.let { arrayVarDecl ->
1835+
arrayVarDecl.initializer?.let { arrayVarInitializer ->
1836+
(e.statements[1] as? IrVariable)?.let { indexVarDecl ->
1837+
indexVarDecl.initializer?.let { indexVarInitializer ->
1838+
(e.statements[2] as? IrCall)?.let { arraySetCall ->
1839+
if (isFunction(arraySetCall.symbol.owner, "kotlin", "(some array type)", { isArrayType(it) }, "set")) {
1840+
getUpdateInPlaceRHS(
1841+
e.origin, // Using e.origin not arraySetCall.origin here distinguishes a compiler-generated block from a user manually code that looks the same.
1842+
{ oldValue ->
1843+
oldValue is IrCall &&
1844+
isFunction(oldValue.symbol.owner, "kotlin", "(some array type)", { typeName -> isArrayType(typeName) }, "get") &&
1845+
(oldValue.dispatchReceiver as? IrGetValue)?.let {
1846+
receiverVal -> receiverVal.symbol.owner == arrayVarDecl.symbol.owner
1847+
} ?: false
1848+
},
1849+
arraySetCall.getValueArgument(1)!!
1850+
)?.let { updateRhs ->
1851+
// Create an assignment skeleton _ op= _
1852+
val exprParent = parent.expr(e, callable)
1853+
val assignId = tw.getFreshIdLabel<DbAssignexpr>()
1854+
val type = useType(arrayVarInitializer.type)
1855+
val locId = tw.getLocation(e)
1856+
tw.writeExprsKotlinType(assignId, type.kotlinResult.id)
1857+
tw.writeHasLocation(assignId, locId)
1858+
tw.writeCallableEnclosingExpr(assignId, callable)
1859+
tw.writeStatementEnclosingExpr(assignId, exprParent.enclosingStmt)
1860+
1861+
if (!writeUpdateInPlaceExpr(e.origin!!, tw, assignId, type, exprParent)) {
1862+
logger.errorElement("Unexpected origin", e)
1863+
return false
1864+
}
1865+
1866+
// Extract e1[e2]
1867+
val lhsId = tw.getFreshIdLabel<DbArrayaccess>()
1868+
val elementType = useType(updateRhs.type)
1869+
tw.writeExprs_arrayaccess(lhsId, elementType.javaResult.id, assignId, 0)
1870+
tw.writeExprsKotlinType(lhsId, elementType.kotlinResult.id)
1871+
tw.writeHasLocation(lhsId, locId)
1872+
tw.writeCallableEnclosingExpr(lhsId, callable)
1873+
tw.writeStatementEnclosingExpr(lhsId, exprParent.enclosingStmt)
1874+
extractExpressionExpr(arrayVarInitializer, callable, lhsId, 0, exprParent.enclosingStmt)
1875+
extractExpressionExpr(indexVarInitializer, callable, lhsId, 1, exprParent.enclosingStmt)
1876+
1877+
// Extract e3
1878+
extractExpressionExpr(updateRhs, callable, assignId, 1, exprParent.enclosingStmt)
1879+
1880+
return true
1881+
}
1882+
}
1883+
}
1884+
}
1885+
}
1886+
}
1887+
}
1888+
1889+
return false
1890+
}
1891+
17851892
fun extractExpressionStmt(e: IrExpression, callable: Label<out DbCallable>, parent: Label<out DbStmtparent>, idx: Int) {
17861893
extractExpression(e, callable, StmtParent(parent, idx))
17871894
}
@@ -1879,13 +1986,15 @@ open class KotlinFileExtractor(
18791986
}
18801987
}
18811988
is IrContainerExpression -> {
1882-
val stmtParent = parent.stmt(e, callable)
1883-
val id = tw.getFreshIdLabel<DbBlock>()
1884-
val locId = tw.getLocation(e)
1885-
tw.writeStmts_block(id, stmtParent.parent, stmtParent.idx, callable)
1886-
tw.writeHasLocation(id, locId)
1887-
e.statements.forEachIndexed { i, s ->
1888-
extractStatement(s, callable, id, i)
1989+
if(!tryExtractArrayUpdate(e, callable, parent)) {
1990+
val stmtParent = parent.stmt(e, callable)
1991+
val id = tw.getFreshIdLabel<DbBlock>()
1992+
val locId = tw.getLocation(e)
1993+
tw.writeStmts_block(id, stmtParent.parent, stmtParent.idx, callable)
1994+
tw.writeHasLocation(id, locId)
1995+
e.statements.forEachIndexed { i, s ->
1996+
extractStatement(s, callable, id, i)
1997+
}
18891998
}
18901999
}
18912000
is IrWhileLoop -> {
@@ -2170,34 +2279,14 @@ open class KotlinFileExtractor(
21702279
val rhsValue = e.value
21712280

21722281
// Check for a desugared in-place update operator, such as "v += e":
2173-
val expectedOperator = when (e.origin) {
2174-
IrStatementOrigin.PLUSEQ -> "plus"
2175-
IrStatementOrigin.MINUSEQ -> "minus"
2176-
IrStatementOrigin.MULTEQ -> "times"
2177-
IrStatementOrigin.DIVEQ -> "div"
2178-
IrStatementOrigin.PERCEQ -> "rem"
2179-
else -> null
2180-
}
2181-
val inPlaceUpdateRhs = expectedOperator?.let {
2182-
if (rhsValue is IrCall &&
2183-
isNumericFunction(rhsValue.symbol.owner, expectedOperator)
2184-
) {
2185-
// Check for an expression like x = get(x).op(e):
2186-
val opReceiver = rhsValue.dispatchReceiver
2187-
if (opReceiver is IrGetValue && opReceiver.symbol.owner == e.symbol.owner) {
2188-
rhsValue.getValueArgument(0)
2189-
} else null
2190-
} else null
2191-
}
2192-
2193-
val extractOrigin = if (inPlaceUpdateRhs == null) null else e.origin
2194-
when(extractOrigin) {
2195-
IrStatementOrigin.PLUSEQ -> tw.writeExprs_assignaddexpr(id as Label<DbAssignaddexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
2196-
IrStatementOrigin.MINUSEQ -> tw.writeExprs_assignsubexpr(id as Label<DbAssignsubexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
2197-
IrStatementOrigin.MULTEQ -> tw.writeExprs_assignmulexpr(id as Label<DbAssignmulexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
2198-
IrStatementOrigin.DIVEQ -> tw.writeExprs_assigndivexpr(id as Label<DbAssigndivexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
2199-
IrStatementOrigin.PERCEQ -> tw.writeExprs_assignremexpr(id as Label<DbAssignremexpr>, type.javaResult.id, exprParent.parent, exprParent.idx)
2200-
else -> tw.writeExprs_assignexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
2282+
val inPlaceUpdateRhs = getUpdateInPlaceRHS(e.origin, { it is IrGetValue && it.symbol.owner == e.symbol.owner }, rhsValue)
2283+
if (inPlaceUpdateRhs != null) {
2284+
if (!writeUpdateInPlaceExpr(e.origin!!, tw, id, type, exprParent)) {
2285+
logger.errorElement("Unexpected origin", e)
2286+
return
2287+
}
2288+
} else {
2289+
tw.writeExprs_assignexpr(id, type.javaResult.id, exprParent.parent, exprParent.idx)
22012290
}
22022291

22032292
val lhsType = useType(e.symbol.owner.type)
Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
| arrayGetsSets.kt:12:3:12:7 | ...[...] | arrayGetsSets.kt:12:3:12:7 | ...=... |
2-
| arrayGetsSets.kt:12:11:12:15 | ...[...] | arrayGetsSets.kt:12:3:12:7 | ...=... |
3-
| arrayGetsSets.kt:13:3:13:7 | ...[...] | arrayGetsSets.kt:13:3:13:7 | ...=... |
4-
| arrayGetsSets.kt:13:11:13:15 | ...[...] | arrayGetsSets.kt:13:3:13:7 | ...=... |
5-
| arrayGetsSets.kt:14:3:14:7 | ...[...] | arrayGetsSets.kt:14:3:14:7 | ...=... |
6-
| arrayGetsSets.kt:14:11:14:15 | ...[...] | arrayGetsSets.kt:14:3:14:7 | ...=... |
7-
| arrayGetsSets.kt:15:3:15:7 | ...[...] | arrayGetsSets.kt:15:3:15:7 | ...=... |
8-
| arrayGetsSets.kt:15:11:15:15 | ...[...] | arrayGetsSets.kt:15:3:15:7 | ...=... |
9-
| arrayGetsSets.kt:16:3:16:7 | ...[...] | arrayGetsSets.kt:16:3:16:7 | ...=... |
10-
| arrayGetsSets.kt:16:11:16:15 | ...[...] | arrayGetsSets.kt:16:3:16:7 | ...=... |
11-
| arrayGetsSets.kt:17:3:17:7 | ...[...] | arrayGetsSets.kt:17:3:17:7 | ...=... |
12-
| arrayGetsSets.kt:17:11:17:15 | ...[...] | arrayGetsSets.kt:17:3:17:7 | ...=... |
13-
| arrayGetsSets.kt:18:3:18:7 | ...[...] | arrayGetsSets.kt:18:3:18:7 | ...=... |
14-
| arrayGetsSets.kt:18:11:18:15 | ...[...] | arrayGetsSets.kt:18:3:18:7 | ...=... |
15-
| arrayGetsSets.kt:19:3:19:7 | ...[...] | arrayGetsSets.kt:19:3:19:7 | ...=... |
16-
| arrayGetsSets.kt:19:11:19:15 | ...[...] | arrayGetsSets.kt:19:3:19:7 | ...=... |
17-
| arrayGetsSets.kt:20:3:20:7 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... |
18-
| arrayGetsSets.kt:20:11:20:15 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... |
1+
| arrayGetsSets.kt:12:3:12:7 | ...[...] | arrayGetsSets.kt:12:3:12:7 | ...=... | int[] | arrayGetsSets.kt:12:3:12:4 | a1 | arrayGetsSets.kt:12:6:12:6 | 0 |
2+
| arrayGetsSets.kt:12:11:12:15 | ...[...] | arrayGetsSets.kt:12:3:12:7 | ...=... | int | arrayGetsSets.kt:12:11:12:12 | a1 | arrayGetsSets.kt:12:14:12:14 | 0 |
3+
| arrayGetsSets.kt:13:3:13:7 | ...[...] | arrayGetsSets.kt:13:3:13:7 | ...=... | short[] | arrayGetsSets.kt:13:3:13:4 | a2 | arrayGetsSets.kt:13:6:13:6 | 0 |
4+
| arrayGetsSets.kt:13:11:13:15 | ...[...] | arrayGetsSets.kt:13:3:13:7 | ...=... | short | arrayGetsSets.kt:13:11:13:12 | a2 | arrayGetsSets.kt:13:14:13:14 | 0 |
5+
| arrayGetsSets.kt:14:3:14:7 | ...[...] | arrayGetsSets.kt:14:3:14:7 | ...=... | byte[] | arrayGetsSets.kt:14:3:14:4 | a3 | arrayGetsSets.kt:14:6:14:6 | 0 |
6+
| arrayGetsSets.kt:14:11:14:15 | ...[...] | arrayGetsSets.kt:14:3:14:7 | ...=... | byte | arrayGetsSets.kt:14:11:14:12 | a3 | arrayGetsSets.kt:14:14:14:14 | 0 |
7+
| arrayGetsSets.kt:15:3:15:7 | ...[...] | arrayGetsSets.kt:15:3:15:7 | ...=... | long[] | arrayGetsSets.kt:15:3:15:4 | a4 | arrayGetsSets.kt:15:6:15:6 | 0 |
8+
| arrayGetsSets.kt:15:11:15:15 | ...[...] | arrayGetsSets.kt:15:3:15:7 | ...=... | long | arrayGetsSets.kt:15:11:15:12 | a4 | arrayGetsSets.kt:15:14:15:14 | 0 |
9+
| arrayGetsSets.kt:16:3:16:7 | ...[...] | arrayGetsSets.kt:16:3:16:7 | ...=... | float[] | arrayGetsSets.kt:16:3:16:4 | a5 | arrayGetsSets.kt:16:6:16:6 | 0 |
10+
| arrayGetsSets.kt:16:11:16:15 | ...[...] | arrayGetsSets.kt:16:3:16:7 | ...=... | float | arrayGetsSets.kt:16:11:16:12 | a5 | arrayGetsSets.kt:16:14:16:14 | 0 |
11+
| arrayGetsSets.kt:17:3:17:7 | ...[...] | arrayGetsSets.kt:17:3:17:7 | ...=... | double[] | arrayGetsSets.kt:17:3:17:4 | a6 | arrayGetsSets.kt:17:6:17:6 | 0 |
12+
| arrayGetsSets.kt:17:11:17:15 | ...[...] | arrayGetsSets.kt:17:3:17:7 | ...=... | double | arrayGetsSets.kt:17:11:17:12 | a6 | arrayGetsSets.kt:17:14:17:14 | 0 |
13+
| arrayGetsSets.kt:18:3:18:7 | ...[...] | arrayGetsSets.kt:18:3:18:7 | ...=... | boolean[] | arrayGetsSets.kt:18:3:18:4 | a7 | arrayGetsSets.kt:18:6:18:6 | 0 |
14+
| arrayGetsSets.kt:18:11:18:15 | ...[...] | arrayGetsSets.kt:18:3:18:7 | ...=... | boolean | arrayGetsSets.kt:18:11:18:12 | a7 | arrayGetsSets.kt:18:14:18:14 | 0 |
15+
| arrayGetsSets.kt:19:3:19:7 | ...[...] | arrayGetsSets.kt:19:3:19:7 | ...=... | char[] | arrayGetsSets.kt:19:3:19:4 | a8 | arrayGetsSets.kt:19:6:19:6 | 0 |
16+
| arrayGetsSets.kt:19:11:19:15 | ...[...] | arrayGetsSets.kt:19:3:19:7 | ...=... | char | arrayGetsSets.kt:19:11:19:12 | a8 | arrayGetsSets.kt:19:14:19:14 | 0 |
17+
| arrayGetsSets.kt:20:3:20:7 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... | Object[] | arrayGetsSets.kt:20:3:20:4 | a9 | arrayGetsSets.kt:20:6:20:6 | 0 |
18+
| arrayGetsSets.kt:20:11:20:15 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... | Object | arrayGetsSets.kt:20:11:20:12 | a9 | arrayGetsSets.kt:20:14:20:14 | 0 |
19+
| arrayGetsSets.kt:32:3:32:7 | ...[...] | arrayGetsSets.kt:32:3:32:7 | ...+=... | int | arrayGetsSets.kt:32:3:32:4 | a1 | arrayGetsSets.kt:32:6:32:6 | 0 |
20+
| arrayGetsSets.kt:38:3:38:7 | ...[...] | arrayGetsSets.kt:38:3:38:7 | .../=... | long | arrayGetsSets.kt:38:3:38:4 | a4 | arrayGetsSets.kt:38:6:38:6 | 0 |
21+
| arrayGetsSets.kt:39:3:39:7 | ...[...] | arrayGetsSets.kt:39:3:39:7 | ...-=... | float | arrayGetsSets.kt:39:3:39:4 | a5 | arrayGetsSets.kt:39:6:39:6 | 0 |
22+
| arrayGetsSets.kt:40:3:40:7 | ...[...] | arrayGetsSets.kt:40:3:40:7 | ...*=... | double | arrayGetsSets.kt:40:3:40:4 | a6 | arrayGetsSets.kt:40:6:40:6 | 0 |
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import java
22

33
from ArrayAccess aa
4-
select aa, aa.getParent()
4+
select aa, aa.getParent(), aa.getType().toString(), aa.getArray(), aa.getIndexExpr()

java/ql/test/kotlin/library-tests/arrays/arrayGetsSets.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,22 @@ fun arrayGetSet(
2020
a9[0] = a9[0]
2121

2222
}
23+
24+
fun arrayGetSetInPlace(
25+
a1: IntArray,
26+
//a2: ShortArray,
27+
//a3: ByteArray,
28+
a4: LongArray,
29+
a5: FloatArray,
30+
a6: DoubleArray) {
31+
32+
a1[0] += 1
33+
// Short and Byte's arithmetic operators yield an Int,
34+
// so we don't have syntax to convert the result of the arithmetic op
35+
// back to the right type.
36+
//a2[0] %= 1.toShort()
37+
//a3[0] *= 1.toByte()
38+
a4[0] /= 1L
39+
a5[0] -= 1f
40+
a6[0] *= 1.0
41+
}

java/ql/test/kotlin/library-tests/arrays/test.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ sourceSignatures
1818
| arrayCreations.kt:27:24:27:38 | | |
1919
| arrayCreations.kt:27:24:27:38 | invoke | invoke(int) |
2020
| arrayGetsSets.kt:1:1:22:1 | arrayGetSet | arrayGetSet(int[],short[],byte[],long[],float[],double[],boolean[],char[],java.lang.Object[]) |
21+
| arrayGetsSets.kt:24:1:41:1 | arrayGetSetInPlace | arrayGetSetInPlace(int[],long[],float[],double[]) |
2122
| primitiveArrays.kt:3:1:7:1 | <obinit> | <obinit>() |
2223
| primitiveArrays.kt:3:1:7:1 | Test | Test() |
2324
| primitiveArrays.kt:5:3:5:123 | test | test(java.lang.Integer[],java.lang.Integer[],int[],java.lang.Integer[][],java.lang.Integer[][],int[][]) |

0 commit comments

Comments
 (0)