Skip to content

Commit e356720

Browse files
authored
Merge pull request github#11014 from tamasvajk/kotlin-for-loop-var
Kotlin: Resugar `for` loops
2 parents d959630 + 4cd0f1c commit e356720

File tree

10 files changed

+476
-70
lines changed

10 files changed

+476
-70
lines changed

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

Lines changed: 147 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ open class KotlinFileExtractor(
14941494
}
14951495
}
14961496

1497-
private fun extractVariableExpr(v: IrVariable, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>) {
1497+
private fun extractVariableExpr(v: IrVariable, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>, extractInitializer: Boolean = true) {
14981498
with("variable expr", v) {
14991499
val varId = useVariable(v)
15001500
val exprId = tw.getFreshIdLabel<DbLocalvariabledeclexpr>()
@@ -1509,7 +1509,7 @@ open class KotlinFileExtractor(
15091509
tw.writeCallableEnclosingExpr(exprId, callable)
15101510
tw.writeStatementEnclosingExpr(exprId, enclosingStmt)
15111511
val i = v.initializer
1512-
if (i != null) {
1512+
if (i != null && extractInitializer) {
15131513
extractExpressionExpr(i, callable, exprId, 0, enclosingStmt)
15141514
}
15151515
if (!v.isVar) {
@@ -3181,6 +3181,90 @@ open class KotlinFileExtractor(
31813181
}
31823182
}
31833183

3184+
/**
3185+
* This method tries to extract a block as an enhanced for loop.
3186+
* It returns true if it succeeds, and false otherwise.
3187+
*/
3188+
private fun tryExtractForLoop(e: IrContainerExpression, callable: Label<out DbCallable>, parent: StmtExprParent): Boolean {
3189+
/*
3190+
* We're expecting the pattern
3191+
* {
3192+
* val iterator = [expr].iterator()
3193+
* while (iterator.hasNext()) {
3194+
* val [loopVar] = iterator.next()
3195+
* [block]
3196+
* }
3197+
* }
3198+
*/
3199+
3200+
if (e.origin != IrStatementOrigin.FOR_LOOP ||
3201+
e.statements.size != 2) {
3202+
return false
3203+
}
3204+
3205+
val iteratorVariable = e.statements[0] as? IrVariable
3206+
val innerWhile = e.statements[1] as? IrWhileLoop
3207+
3208+
if (iteratorVariable == null ||
3209+
iteratorVariable.origin != IrDeclarationOrigin.FOR_LOOP_ITERATOR ||
3210+
innerWhile == null ||
3211+
innerWhile.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE) {
3212+
return false
3213+
}
3214+
3215+
val initializer = iteratorVariable.initializer as? IrCall
3216+
if (initializer == null ||
3217+
initializer.origin != IrStatementOrigin.FOR_LOOP_ITERATOR ||
3218+
initializer.symbol.owner.name.asString() != "iterator") {
3219+
return false
3220+
}
3221+
3222+
val expr = initializer.dispatchReceiver
3223+
val cond = innerWhile.condition as? IrCall
3224+
val body = innerWhile.body as? IrBlock
3225+
3226+
if (expr == null ||
3227+
cond == null ||
3228+
cond.origin != IrStatementOrigin.FOR_LOOP_HAS_NEXT ||
3229+
(cond.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable ||
3230+
body == null ||
3231+
body.origin != IrStatementOrigin.FOR_LOOP_INNER_WHILE ||
3232+
body.statements.size < 2) {
3233+
return false
3234+
}
3235+
3236+
val loopVar = body.statements[0] as? IrVariable
3237+
val nextCall = loopVar?.initializer as? IrCall
3238+
3239+
if (loopVar == null ||
3240+
!(loopVar.origin == IrDeclarationOrigin.FOR_LOOP_VARIABLE || loopVar.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE) ||
3241+
nextCall == null ||
3242+
nextCall.origin != IrStatementOrigin.FOR_LOOP_NEXT ||
3243+
(nextCall.dispatchReceiver as? IrGetValue)?.symbol?.owner != iteratorVariable) {
3244+
return false
3245+
}
3246+
3247+
val id = extractLoop(innerWhile, null, parent, callable) { p, idx ->
3248+
tw.getFreshIdLabel<DbEnhancedforstmt>().also {
3249+
tw.writeStmts_enhancedforstmt(it, p, idx, callable)
3250+
}
3251+
}
3252+
3253+
extractVariableExpr(loopVar, callable, id, 0, id, extractInitializer = false)
3254+
extractExpressionExpr(expr, callable, id, 1, id)
3255+
val block = body.statements[1] as? IrBlock
3256+
if (body.statements.size == 2 && block != null) {
3257+
// Extract the body that was given to us by the compiler
3258+
extractExpressionStmt(block, callable, id, 2)
3259+
} else {
3260+
// Extract a block with all but the first (loop variable declaration) statement
3261+
extractBlock(body, body.statements.takeLast(body.statements.size - 1), id, 2, callable)
3262+
}
3263+
3264+
return true
3265+
}
3266+
3267+
31843268
/**
31853269
* This tried to extract a block as an array update.
31863270
* It returns true if it succeeds, and false otherwise.
@@ -3412,22 +3496,17 @@ open class KotlinFileExtractor(
34123496
}
34133497
}
34143498
is IrContainerExpression -> {
3415-
if(!tryExtractArrayUpdate(e, callable, parent)) {
3416-
val stmtParent = parent.stmt(e, callable)
3417-
val id = tw.getFreshIdLabel<DbBlock>()
3418-
val locId = tw.getLocation(e)
3419-
tw.writeStmts_block(id, stmtParent.parent, stmtParent.idx, callable)
3420-
tw.writeHasLocation(id, locId)
3421-
e.statements.forEachIndexed { i, s ->
3422-
extractStatement(s, callable, id, i)
3423-
}
3499+
if (!tryExtractArrayUpdate(e, callable, parent) &&
3500+
!tryExtractForLoop(e, callable, parent)) {
3501+
3502+
extractBlock(e, e.statements, parent, callable)
34243503
}
34253504
}
34263505
is IrWhileLoop -> {
3427-
extractLoop(e, parent, callable)
3506+
extractLoopWithCondition(e, parent, callable)
34283507
}
34293508
is IrDoWhileLoop -> {
3430-
extractLoop(e, parent, callable)
3509+
extractLoopWithCondition(e, parent, callable)
34313510
}
34323511
is IrInstanceInitializerCall -> {
34333512
val irConstructor = declarationStack.peek().first as? IrConstructor
@@ -3892,6 +3971,32 @@ open class KotlinFileExtractor(
38923971
}
38933972
}
38943973

3974+
private fun extractBlock(
3975+
e: IrContainerExpression,
3976+
statements: List<IrStatement>,
3977+
parent: StmtExprParent,
3978+
callable: Label<out DbCallable>
3979+
) {
3980+
val stmtParent = parent.stmt(e, callable)
3981+
extractBlock(e, statements, stmtParent.parent, stmtParent.idx, callable)
3982+
}
3983+
3984+
private fun extractBlock(
3985+
e: IrElement,
3986+
statements: List<IrStatement>,
3987+
parent: Label<out DbStmtparent>,
3988+
idx: Int,
3989+
callable: Label<out DbCallable>
3990+
) {
3991+
val id = tw.getFreshIdLabel<DbBlock>()
3992+
val locId = tw.getLocation(e)
3993+
tw.writeStmts_block(id, parent, idx, callable)
3994+
tw.writeHasLocation(id, locId)
3995+
statements.forEachIndexed { i, s ->
3996+
extractStatement(s, callable, id, i)
3997+
}
3998+
}
3999+
38954000
private inline fun <D: DeclarationDescriptor, reified B: IrSymbolOwner> getBoundSymbolOwner(symbol: IrBindableSymbol<D, B>, e: IrExpression): B? {
38964001
if (symbol.isBound) {
38974002
return symbol.owner
@@ -4000,9 +4105,11 @@ open class KotlinFileExtractor(
40004105

40014106
private fun extractLoop(
40024107
loop: IrLoop,
4108+
bodyIdx: Int?,
40034109
stmtExprParent: StmtExprParent,
4004-
callable: Label<out DbCallable>
4005-
) {
4110+
callable: Label<out DbCallable>,
4111+
getId: (Label<out DbStmtparent>, Int) -> Label<out DbStmt>
4112+
) : Label<out DbStmt> {
40064113
val stmtParent = stmtExprParent.stmt(loop, callable)
40074114
val locId = tw.getLocation(loop)
40084115

@@ -4023,22 +4130,34 @@ open class KotlinFileExtractor(
40234130
parent = stmtParent.parent
40244131
}
40254132

4026-
val id = if (loop is IrWhileLoop) {
4027-
val id = tw.getFreshIdLabel<DbWhilestmt>()
4028-
tw.writeStmts_whilestmt(id, parent, idx, callable)
4029-
id
4030-
} else {
4031-
val id = tw.getFreshIdLabel<DbDostmt>()
4032-
tw.writeStmts_dostmt(id, parent, idx, callable)
4033-
id
4034-
}
4035-
4133+
val id = getId(parent, idx)
40364134
tw.writeHasLocation(id, locId)
4037-
extractExpressionExpr(loop.condition, callable, id, 0, id)
4135+
40384136
val body = loop.body
4039-
if (body != null) {
4040-
extractExpressionStmt(body, callable, id, 1)
4137+
if (body != null && bodyIdx != null) {
4138+
extractExpressionStmt(body, callable, id, bodyIdx)
40414139
}
4140+
4141+
return id
4142+
}
4143+
4144+
private fun extractLoopWithCondition(
4145+
loop: IrLoop,
4146+
stmtExprParent: StmtExprParent,
4147+
callable: Label<out DbCallable>
4148+
) {
4149+
val id = extractLoop(loop, 1, stmtExprParent, callable) { parent, idx ->
4150+
if (loop is IrWhileLoop) {
4151+
tw.getFreshIdLabel<DbWhilestmt>().also {
4152+
tw.writeStmts_whilestmt(it, parent, idx, callable)
4153+
}
4154+
} else {
4155+
tw.getFreshIdLabel<DbDostmt>().also {
4156+
tw.writeStmts_dostmt(it, parent, idx, callable)
4157+
}
4158+
}
4159+
}
4160+
extractExpressionExpr(loop.condition, callable, id, 0, id)
40424161
}
40434162

40444163
private fun IrValueParameter.isExtensionReceiver(): Boolean {
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
| arrayIterators.kt:6:15:6:15 | iterator(...) | iterator(java.lang.Object[]) | kotlin.jvm.internal.ArrayIteratorKt |
2-
| arrayIterators.kt:7:15:7:15 | iterator(...) | iterator(int[]) | kotlin.jvm.internal.ArrayIteratorsKt |
3-
| arrayIterators.kt:8:15:8:15 | iterator(...) | iterator(boolean[]) | kotlin.jvm.internal.ArrayIteratorsKt |
41
| arrayIterators.kt:10:16:10:25 | iterator(...) | iterator(java.lang.Object[]) | kotlin.jvm.internal.ArrayIteratorKt |
52
| arrayIterators.kt:11:16:11:25 | iterator(...) | iterator(int[]) | kotlin.jvm.internal.ArrayIteratorsKt |
63
| arrayIterators.kt:12:16:12:25 | iterator(...) | iterator(boolean[]) | kotlin.jvm.internal.ArrayIteratorsKt |
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +0,0 @@
1-
| test.kt:5:14:5:14 | hasNext(...) |
2-
| test.kt:5:14:5:14 | iterator(...) |
3-
| test.kt:5:14:5:14 | next(...) |
4-
| test.kt:6:14:6:14 | hasNext(...) |
5-
| test.kt:6:14:6:14 | iterator(...) |
6-
| test.kt:6:14:6:14 | next(...) |
7-
| test.kt:7:14:7:14 | hasNext(...) |
8-
| test.kt:7:14:7:14 | iterator(...) |
9-
| test.kt:7:14:7:14 | next(...) |

0 commit comments

Comments
 (0)