diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/BreakableBlockInfo.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/BreakableBlockInfo.java index 2d06c241368..b2672f0d9c2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/BreakableBlockInfo.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/BreakableBlockInfo.java @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtElement; import java.util.Collections; import java.util.Set; -public class BreakableBlockInfo extends BlockInfo { +public abstract class BreakableBlockInfo extends BlockInfo { private final KtElement element; private final Label entryPoint; private final Label exitPoint; diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt index cf51f82350b..5213094a16a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilder.kt @@ -39,8 +39,9 @@ interface ControlFlowBuilder { fun exitBlockScope(block: KtElement) - fun getExitPoint(labelElement: KtElement): Label? - fun getConditionEntryPoint(labelElement: KtElement): Label + fun getSubroutineExitPoint(labelElement: KtElement): Label? + fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label + fun getLoopExitPoint(loop: KtLoopExpression): Label? // Declarations fun declareParameter(parameter: KtParameter) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt index 2c774b45446..74e6e9d3ad6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowBuilderAdapter.kt @@ -123,12 +123,16 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder { delegateBuilder.throwException(throwExpression, thrownValue) } - override fun getExitPoint(labelElement: KtElement): Label? { - return delegateBuilder.getExitPoint(labelElement) + override fun getSubroutineExitPoint(labelElement: KtElement): Label? { + return delegateBuilder.getSubroutineExitPoint(labelElement) } - override fun getConditionEntryPoint(labelElement: KtElement): Label { - return delegateBuilder.getConditionEntryPoint(labelElement) + override fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label { + return delegateBuilder.getLoopConditionEntryPoint(loop) + } + + override fun getLoopExitPoint(loop: KtLoopExpression): Label? { + return delegateBuilder.getLoopExitPoint(loop) } override fun enterLoop(expression: KtLoopExpression): LoopInfo { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 61dffa450ac..629e9fbc254 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -797,7 +797,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { val loop = getCorrespondingLoop(expression) if (loop != null) { if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { - builder.getExitPoint(loop)?.let { builder.jump(it, expression) } + builder.getLoopExitPoint(loop)?.let { builder.jump(it, expression) } } } } @@ -806,7 +806,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { val loop = getCorrespondingLoop(expression) if (loop != null) { if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { - builder.jump(builder.getConditionEntryPoint(loop), expression) + builder.jump(builder.getLoopConditionEntryPoint(loop), expression) } } } @@ -824,7 +824,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } } - private fun getCorrespondingLoop(expression: KtExpressionWithLabel): KtElement? { + private fun getCorrespondingLoop(expression: KtExpressionWithLabel): KtLoopExpression? { val labelName = expression.getLabelName() val loop: KtLoopExpression? if (labelName != null) { @@ -860,14 +860,14 @@ class ControlFlowProcessor(private val trace: BindingTrace) { return loop } - private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtElement): Boolean { + private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtLoopExpression): Boolean { val bindingContext = trace.bindingContext val labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression) val labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget) return if (labelExprEnclosingFunc !== labelTargetEnclosingFunc) { // Check to report only once - if (builder.getExitPoint(jumpTarget) != null || + if (builder.getLoopExitPoint(jumpTarget) != null || // Local class secondary constructors are handled differently // They are the only local class element NOT included in owner pseudocode // See generateInitializersForScriptClassOrObject && generateDeclarationForLocalClassOrObjectIfNeeded diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/SubroutineInfo.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/SubroutineInfo.kt new file mode 100644 index 00000000000..4fa9ecd9341 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/SubroutineInfo.kt @@ -0,0 +1,21 @@ +/* + * 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.cfg + +import org.jetbrains.kotlin.psi.KtElement + +class SubroutineInfo(subroutine: KtElement, entryPoint: Label, exitPoint: Label) : BreakableBlockInfo(subroutine, entryPoint, exitPoint) \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt index 6de539054c3..7d364716e75 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/ControlFlowInstructionsGenerator.kt @@ -40,7 +40,8 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { private val loopInfo = Stack() private val blockScopes = Stack() - private val elementToBlockInfo = HashMap() + private val elementToLoopInfo = HashMap() + private val elementToSubroutineInfo = HashMap() private var labelCount = 0 private val builders = Stack() @@ -130,12 +131,12 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { createUnboundLabel("body exit point"), createUnboundLabel("condition entry point")) bindLabel(info.entryPoint) - elementToBlockInfo.put(expression, info) + elementToLoopInfo.put(expression, info) return info } override fun enterLoopBody(expression: KtLoopExpression) { - val info = elementToBlockInfo[expression] as LoopInfo + val info = elementToLoopInfo[expression]!! bindLabel(info.bodyEntryPoint) loopInfo.push(info) allBlocks.push(info) @@ -143,7 +144,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { override fun exitLoopBody(expression: KtLoopExpression) { val info = loopInfo.pop() - elementToBlockInfo.remove(expression) + elementToLoopInfo.remove(expression) allBlocks.pop() bindLabel(info.bodyExitPoint) } @@ -152,11 +153,11 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { get() = if (loopInfo.empty()) null else loopInfo.peek().element override fun enterSubroutine(subroutine: KtElement) { - val blockInfo = BreakableBlockInfo( + val blockInfo = SubroutineInfo( subroutine, /* entry point */ createUnboundLabel(), /* exit point */ createUnboundLabel()) - elementToBlockInfo.put(subroutine, blockInfo) + elementToSubroutineInfo.put(subroutine, blockInfo) allBlocks.push(blockInfo) bindLabel(blockInfo.entryPoint) add(SubroutineEnterInstruction(subroutine, currentScope)) @@ -165,15 +166,19 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { override val currentSubroutine: KtElement get() = pseudocode.correspondingElement - override fun getConditionEntryPoint(labelElement: KtElement): Label { - val blockInfo = elementToBlockInfo[labelElement] - assert(blockInfo is LoopInfo) { "expected LoopInfo for " + labelElement.text } - return (blockInfo as LoopInfo).conditionEntryPoint + override fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label { + val blockInfo = elementToLoopInfo[loop] ?: error("expected LoopInfo for ${loop.text}") + return blockInfo.conditionEntryPoint } - override fun getExitPoint(labelElement: KtElement): Label? { + override fun getLoopExitPoint(loop: KtLoopExpression): Label? { + // It's quite possible to have null here, see testBreakInsideLocal + return elementToLoopInfo[loop]?.exitPoint + } + + override fun getSubroutineExitPoint(labelElement: KtElement): Label? { // It's quite possible to have null here, e.g. for non-local returns (see KT-10823) - return elementToBlockInfo[labelElement]?.exitPoint + return elementToSubroutineInfo[labelElement]?.exitPoint } private val currentScope: BlockScope @@ -214,13 +219,13 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { } override fun exitSubroutine(subroutine: KtElement): Pseudocode { - getExitPoint(subroutine)?.let { bindLabel(it) } + getSubroutineExitPoint(subroutine)?.let { bindLabel(it) } pseudocode.addExitInstruction(SubroutineExitInstruction(subroutine, currentScope, false)) bindLabel(error) pseudocode.addErrorInstruction(SubroutineExitInstruction(subroutine, currentScope, true)) bindLabel(sink) pseudocode.addSinkInstruction(SubroutineSinkInstruction(subroutine, currentScope, "")) - elementToBlockInfo.remove(subroutine) + elementToSubroutineInfo.remove(subroutine) allBlocks.pop() return pseudocode } @@ -242,13 +247,13 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() { } override fun returnValue(returnExpression: KtExpression, returnValue: PseudoValue, subroutine: KtElement) { - val exitPoint = getExitPoint(subroutine) ?: return + val exitPoint = getSubroutineExitPoint(subroutine) ?: return handleJumpInsideTryFinally(exitPoint) add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue)) } override fun returnNoValue(returnExpression: KtReturnExpression, subroutine: KtElement) { - val exitPoint = getExitPoint(subroutine) ?: return + val exitPoint = getSubroutineExitPoint(subroutine) ?: return handleJumpInsideTryFinally(exitPoint) add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint)) }