'break' & 'continue'
This commit is contained in:
committed by
Dmitry Petrov
parent
87c7b4834b
commit
03a666690b
+7
-2
@@ -17,10 +17,12 @@
|
|||||||
package org.jetbrains.kotlin.psi2ir.generators
|
package org.jetbrains.kotlin.psi2ir.generators
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.IrBlockImpl
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrLoop
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrReturnImpl
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.psi.KtWhileExpression
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -62,5 +64,8 @@ class ExpressionBodyGenerator(val scopeOwner: CallableDescriptor, override val c
|
|||||||
fun putLoop(expression: KtExpression, irLoop: IrLoop) {
|
fun putLoop(expression: KtExpression, irLoop: IrLoop) {
|
||||||
loopTable[expression] = irLoop
|
loopTable[expression] = irLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getLoop(expression: KtExpression): IrLoop? =
|
||||||
|
loopTable[expression]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+49
-4
@@ -17,13 +17,15 @@
|
|||||||
package org.jetbrains.kotlin.psi2ir.generators
|
package org.jetbrains.kotlin.psi2ir.generators
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.psi.KtDoWhileExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtWhileExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtWhileExpressionBase
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
|
||||||
class LoopExpressionGenerator(val statementGenerator: StatementGenerator) {
|
class LoopExpressionGenerator(val statementGenerator: StatementGenerator) : GeneratorWithScope {
|
||||||
|
override val scope: Scope get() = statementGenerator.scope
|
||||||
|
override val context: GeneratorContext get() = statementGenerator.context
|
||||||
|
|
||||||
fun generateWhileExpression(expression: KtWhileExpression): IrExpression =
|
fun generateWhileExpression(expression: KtWhileExpression): IrExpression =
|
||||||
generateConditionalLoop(expression, IrWhileLoopImpl(expression.startOffset, expression.endOffset, IrOperator.WHILE_LOOP))
|
generateConditionalLoop(expression, IrWhileLoopImpl(expression.startOffset, expression.endOffset, IrOperator.WHILE_LOOP))
|
||||||
|
|
||||||
@@ -36,4 +38,47 @@ class LoopExpressionGenerator(val statementGenerator: StatementGenerator) {
|
|||||||
irLoop.body = statementGenerator.generateExpression(expression.body!!)
|
irLoop.body = statementGenerator.generateExpression(expression.body!!)
|
||||||
return irLoop
|
return irLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun generateBreakExpression(expression: KtBreakExpression): IrExpression {
|
||||||
|
val parentLoop = findParentLoop(expression)
|
||||||
|
return IrBreakImpl(expression.startOffset, expression.endOffset, context.builtIns.nothingType, parentLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateContinueExpression(expression: KtContinueExpression): IrExpression {
|
||||||
|
val parentLoop = findParentLoop(expression)
|
||||||
|
return IrContinueImpl(expression.startOffset, expression.endOffset, context.builtIns.nothingType, parentLoop)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findParentLoop(expression: KtExpressionWithLabel): IrLoop =
|
||||||
|
findParentLoop(expression, expression.getTargetLabel()?.getReferencedName())
|
||||||
|
|
||||||
|
private fun findParentLoop(expression: KtExpression, targetLabel: String?): IrLoop {
|
||||||
|
var finger: KtExpression? = expression
|
||||||
|
while (finger != null) {
|
||||||
|
finger = finger.getParentOfType<KtLoopExpression>(true)
|
||||||
|
if (finger == null) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (targetLabel == null) {
|
||||||
|
return getLoop(finger)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val parent = finger.parent
|
||||||
|
if (parent is KtLabeledExpression) {
|
||||||
|
val label = parent.getLabelName()!!
|
||||||
|
if (targetLabel == label) {
|
||||||
|
return getLoop(finger)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw AssertionError("No parent loop for break/continue @$targetLabel")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLoop(finger: KtLoopExpression): IrLoop {
|
||||||
|
return statementGenerator.expressionBodyGenerator.getLoop(finger) ?:
|
||||||
|
throw AssertionError("Loop was not visited:\n${finger.text}")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -277,6 +277,12 @@ class StatementGenerator(
|
|||||||
|
|
||||||
override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: Nothing?): IrStatement =
|
override fun visitDoWhileExpression(expression: KtDoWhileExpression, data: Nothing?): IrStatement =
|
||||||
LoopExpressionGenerator(this).generateDoWhileExpression(expression)
|
LoopExpressionGenerator(this).generateDoWhileExpression(expression)
|
||||||
|
|
||||||
|
override fun visitBreakExpression(expression: KtBreakExpression, data: Nothing?): IrStatement =
|
||||||
|
LoopExpressionGenerator(this).generateBreakExpression(expression)
|
||||||
|
|
||||||
|
override fun visitContinueExpression(expression: KtContinueExpression, data: Nothing?): IrStatement =
|
||||||
|
LoopExpressionGenerator(this).generateContinueExpression(expression)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.ir.expressions
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.util.dump
|
||||||
|
import org.jetbrains.kotlin.ir.util.render
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
|
interface IrBreakContinue : IrExpression {
|
||||||
|
var loop: IrLoop
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IrBreak: IrBreakContinue
|
||||||
|
|
||||||
|
interface IrContinue: IrBreakContinue
|
||||||
|
|
||||||
|
fun IrBreakContinue.getDepth(): Int {
|
||||||
|
var depth = 0
|
||||||
|
var finger: IrElement = this
|
||||||
|
while (true) {
|
||||||
|
val parent = finger.parent ?: throw AssertionError("No parent loop in tree for ${this.render()}:\n${finger.dump()}")
|
||||||
|
if (parent is IrLoop) {
|
||||||
|
if (parent == loop) {
|
||||||
|
return depth
|
||||||
|
}
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
finger = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class IrBreakContinueBase(
|
||||||
|
startOffset: Int,
|
||||||
|
endOffset: Int,
|
||||||
|
type: KotlinType,
|
||||||
|
override var loop: IrLoop
|
||||||
|
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrBreakContinue {
|
||||||
|
}
|
||||||
|
|
||||||
|
class IrBreakImpl(
|
||||||
|
startOffset: Int,
|
||||||
|
endOffset: Int,
|
||||||
|
type: KotlinType,
|
||||||
|
loop: IrLoop
|
||||||
|
) : IrBreakContinueBase(startOffset, endOffset, type, loop), IrBreak {
|
||||||
|
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||||
|
visitor.visitBreak(this, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
class IrContinueImpl(
|
||||||
|
startOffset: Int,
|
||||||
|
endOffset: Int,
|
||||||
|
type: KotlinType,
|
||||||
|
loop: IrLoop
|
||||||
|
) : IrBreakContinueBase(startOffset, endOffset, type, loop), IrContinue {
|
||||||
|
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
|
||||||
|
visitor.visitContinue(this, data)
|
||||||
|
}
|
||||||
@@ -106,6 +106,12 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
|
|||||||
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): String =
|
override fun visitDoWhileLoop(loop: IrDoWhileLoop, data: Nothing?): String =
|
||||||
"DO_WHILE operator=${loop.operator}"
|
"DO_WHILE operator=${loop.operator}"
|
||||||
|
|
||||||
|
override fun visitBreak(jump: IrBreak, data: Nothing?): String =
|
||||||
|
"BREAK loop.operator=${jump.loop.operator} depth=${jump.getDepth()}"
|
||||||
|
|
||||||
|
override fun visitContinue(jump: IrContinue, data: Nothing?): String =
|
||||||
|
"CONTINUE loop.operator=${jump.loop.operator} depth=${jump.getDepth()}"
|
||||||
|
|
||||||
override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String =
|
override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String =
|
||||||
"DUMMY ${declaration.descriptor.name}"
|
"DUMMY ${declaration.descriptor.name}"
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,13 @@ interface IrElementVisitor<out R, in D> {
|
|||||||
fun visitWhileLoop(loop: IrWhileLoop, data: D) = visitLoop(loop, data)
|
fun visitWhileLoop(loop: IrWhileLoop, data: D) = visitLoop(loop, data)
|
||||||
fun visitDoWhileLoop(loop: IrDoWhileLoop, data: D) = visitLoop(loop, data)
|
fun visitDoWhileLoop(loop: IrDoWhileLoop, data: D) = visitLoop(loop, data)
|
||||||
|
|
||||||
|
fun visitBreakContinue(jump: IrBreakContinue, data: D) = visitExpression(jump, data)
|
||||||
|
fun visitBreak(jump: IrBreak, data: D) = visitBreakContinue(jump, data)
|
||||||
|
fun visitContinue(jump: IrContinue, data: D) = visitBreakContinue(jump, data)
|
||||||
|
|
||||||
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
|
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
|
||||||
fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data)
|
fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data)
|
||||||
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
|
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
fun test1() {
|
||||||
|
while (true) { break }
|
||||||
|
do { break } while (true)
|
||||||
|
while (true) { continue }
|
||||||
|
do { continue } while (true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test2() {
|
||||||
|
OUTER@while(true) {
|
||||||
|
INNER@while(true) {
|
||||||
|
break@INNER
|
||||||
|
break@OUTER
|
||||||
|
}
|
||||||
|
break@OUTER
|
||||||
|
}
|
||||||
|
OUTER@while(true) {
|
||||||
|
INNER@while(true) {
|
||||||
|
continue@INNER
|
||||||
|
continue@OUTER
|
||||||
|
}
|
||||||
|
continue@OUTER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test3() {
|
||||||
|
L@while(true) {
|
||||||
|
L@while(true) {
|
||||||
|
break@L
|
||||||
|
}
|
||||||
|
break@L
|
||||||
|
}
|
||||||
|
L@while(true) {
|
||||||
|
L@while(true) {
|
||||||
|
continue@L
|
||||||
|
}
|
||||||
|
continue@L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
IrFile /breakContinue.kt
|
||||||
|
IrFunction public fun test1(): kotlin.Unit
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=0
|
||||||
|
DO_WHILE operator=DO_WHILE_LOOP
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
BREAK loop.operator=DO_WHILE_LOOP depth=0
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=0
|
||||||
|
DO_WHILE operator=DO_WHILE_LOOP
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
CONTINUE loop.operator=DO_WHILE_LOOP depth=0
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
IrFunction public fun test2(): kotlin.Unit
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=0
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=1
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=0
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=0
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=1
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=0
|
||||||
|
IrFunction public fun test3(): kotlin.Unit
|
||||||
|
IrExpressionBody
|
||||||
|
BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=0
|
||||||
|
BREAK loop.operator=WHILE_LOOP depth=0
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
WHILE operator=WHILE_LOOP
|
||||||
|
condition: CONST Boolean type=kotlin.Boolean value='true'
|
||||||
|
body: BLOCK type=<no-type> hasResult=false operator=null
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=0
|
||||||
|
CONTINUE loop.operator=WHILE_LOOP depth=0
|
||||||
@@ -83,6 +83,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("breakContinue.kt")
|
||||||
|
public void testBreakContinue() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/breakContinue.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("callWithReorderedArguments.kt")
|
@TestMetadata("callWithReorderedArguments.kt")
|
||||||
public void testCallWithReorderedArguments() throws Exception {
|
public void testCallWithReorderedArguments() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/callWithReorderedArguments.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/callWithReorderedArguments.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user