org.jetbrains.kotlin.cfg.ControlFlowProcessor.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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 com.intellij.psi.PsiElement
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartFMap
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.ControlFlowBuilder.PredefinedOperation.NOT_NULL_ASSERTION
import org.jetbrains.kotlin.cfg.ControlFlowBuilder.PredefinedOperation.OR
import org.jetbrains.kotlin.cfg.pseudocode.ControlFlowInstructionsGenerator
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.BreakContinueInInlineLambdas
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitQualifiedAccessToUninitializedEnumEntry
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getEnclosingFunctionDescriptor
import org.jetbrains.kotlin.resolve.bindingContextUtil.recordUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tower.getFakeDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.util.*
typealias DeferredGenerator = (ControlFlowBuilder) -> Unit
class ControlFlowProcessor(
private val trace: BindingTrace,
private val languageVersionSettings: LanguageVersionSettings
) {
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
fun generatePseudocode(subroutine: KtElement): Pseudocode {
val pseudocode = generate(subroutine, null)
(pseudocode as PseudocodeImpl).postProcess()
return pseudocode
}
private fun generate(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange? = null): Pseudocode {
builder.enterSubroutine(subroutine, eventOccurrencesRange)
val cfpVisitor = CFPVisitor(builder)
if (subroutine is KtDeclarationWithBody && subroutine !is KtSecondaryConstructor) {
val valueParameters = subroutine.valueParameters
for (valueParameter in valueParameters) {
cfpVisitor.generateInstructions(valueParameter)
}
val bodyExpression = subroutine.bodyExpression
if (bodyExpression != null) {
cfpVisitor.generateInstructions(bodyExpression)
if (!subroutine.hasBlockBody()) {
generateImplicitReturnValue(bodyExpression, subroutine)
}
}
} else {
cfpVisitor.generateInstructions(subroutine)
}
return builder.exitSubroutine(subroutine, eventOccurrencesRange)
}
private fun generateImplicitReturnValue(bodyExpression: KtExpression, subroutine: KtElement) {
val subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine) as CallableDescriptor? ?: return
val returnType = subroutineDescriptor.returnType
if (returnType != null && KotlinBuiltIns.isUnit(returnType) && subroutineDescriptor is AnonymousFunctionDescriptor) return
val returnValue = builder.getBoundValue(bodyExpression) ?: return
builder.returnValue(bodyExpression, returnValue, subroutine)
}
private fun processLocalDeclaration(subroutine: KtDeclaration) {
val afterDeclaration = builder.createUnboundLabel("after local declaration")
builder.nondeterministicJump(afterDeclaration, subroutine, null)
generate(subroutine, null)
builder.bindLabel(afterDeclaration)
}
private class CatchFinallyLabels(val onException: Label?, val toFinally: Label?, val tryExpression: KtTryExpression?)
private inner class CFPVisitor(private val builder: ControlFlowBuilder) : KtVisitorVoid() {
private val catchFinallyStack = Stack()
// Some language constructs (e.g. inlined lambdas) should be partially processed before call
// (to provide argument for call itself), and partially - after (in case of inlined lambdas,
// their body should be generated after call). To do so, we store deferred generators, which
// will be called after call instruction is emitted.
// Stack is necessary to store generators across nested calls
private val deferredGeneratorsStack = Stack>()
private val conditionVisitor = object : KtVisitorVoid() {
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
condition.getStrictParentOfType()?.subjectExpression
override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
if (!generateCall(condition.operationReference)) {
val rangeExpression = condition.rangeExpression
generateInstructions(rangeExpression)
createNonSyntheticValue(condition, MagicKind.UNRESOLVED_CALL, rangeExpression)
}
}
override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
mark(condition)
createNonSyntheticValue(condition, MagicKind.IS, getSubjectExpression(condition))
}
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
mark(condition)
val expression = condition.expression
generateInstructions(expression)
val subjectExpression = getSubjectExpression(condition)
if (subjectExpression != null) {
// todo: this can be replaced by equals() invocation (when corresponding resolved call is recorded)
createNonSyntheticValue(condition, MagicKind.EQUALS_IN_WHEN_CONDITION, subjectExpression, expression)
} else {
copyValue(expression, condition)
}
}
override fun visitKtElement(element: KtElement) {
throw UnsupportedOperationException("[ControlFlowProcessor] $element")
}
}
private fun mark(element: KtElement) {
builder.mark(element)
}
fun generateInstructions(element: KtElement?) {
if (element == null) return
element.accept(this)
checkNothingType(element)
}
private fun checkNothingType(element: KtElement) {
if (element !is KtExpression) return
val expression = KtPsiUtil.deparenthesize(element) ?: return
if (expression is KtStatementExpression || expression is KtTryExpression
|| expression is KtIfExpression || expression is KtWhenExpression
) {
return
}
val type = trace.bindingContext.getType(expression)
if (type != null && KotlinBuiltIns.isNothing(type)) {
builder.jumpToError(expression)
}
}
private fun createSyntheticValue(instructionElement: KtElement, kind: MagicKind, vararg from: KtElement): PseudoValue =
builder.magic(instructionElement, null, elementsToValues(from.asList()), kind).outputValue
private fun createNonSyntheticValue(to: KtElement, from: List, kind: MagicKind): PseudoValue =
builder.magic(to, to, elementsToValues(from), kind).outputValue
private fun createNonSyntheticValue(to: KtElement, kind: MagicKind, vararg from: KtElement?): PseudoValue =
createNonSyntheticValue(to, from.asList(), kind)
private fun mergeValues(from: List, to: KtExpression) {
builder.merge(to, elementsToValues(from))
}
private fun copyValue(from: KtElement?, to: KtElement) {
getBoundOrUnreachableValue(from)?.let { builder.bindValue(it, to) }
}
private fun getBoundOrUnreachableValue(element: KtElement?): PseudoValue? {
if (element == null) return null
val value = builder.getBoundValue(element)
return if (value != null || element is KtDeclaration) value else builder.newValue(element)
}
private fun elementsToValues(from: List): List =
from.mapNotNull { element -> getBoundOrUnreachableValue(element) }
private fun generateInitializer(declaration: KtDeclaration, initValue: PseudoValue) {
builder.write(declaration, declaration, initValue, getDeclarationAccessTarget(declaration), emptyMap())
}
private fun getResolvedCallAccessTarget(element: KtElement?): AccessTarget =
element.getResolvedCall(trace.bindingContext)?.let { AccessTarget.Call(it) }
?: AccessTarget.BlackBox
private fun getDeclarationAccessTarget(element: KtElement): AccessTarget {
val descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
return if (descriptor is VariableDescriptor)
AccessTarget.Declaration(descriptor)
else
AccessTarget.BlackBox
}
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) {
mark(expression)
val innerExpression = expression.expression
if (innerExpression != null) {
generateInstructions(innerExpression)
copyValue(innerExpression, expression)
}
}
override fun visitAnnotatedExpression(expression: KtAnnotatedExpression) {
val baseExpression = expression.baseExpression
if (baseExpression != null) {
generateInstructions(baseExpression)
copyValue(baseExpression, expression)
}
}
override fun visitThisExpression(expression: KtThisExpression) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall == null) {
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL)
return
}
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor is ReceiverParameterDescriptor) {
builder.readVariable(expression, resolvedCall, getReceiverValues(resolvedCall))
}
copyValue(expression, expression.instanceReference)
}
override fun visitConstantExpression(expression: KtConstantExpression) {
val constant = ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)
builder.loadConstant(expression, constant)
}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall is VariableAsFunctionResolvedCall) {
generateCall(resolvedCall.variableCall)
} else {
if (resolvedCall == null) {
val qualifierExpression =
when (languageVersionSettings.supportsFeature(ProhibitQualifiedAccessToUninitializedEnumEntry)) {
true -> expression.getTopmostParentQualifiedExpressionForSelector() ?: expression
false -> expression
}
val qualifier = trace.bindingContext[BindingContext.QUALIFIER, qualifierExpression]
if (qualifier != null && generateQualifier(expression, qualifier)) return
}
if (!generateCall(expression) && expression.parent !is KtCallExpression) {
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, generateAndGetReceiverIfAny(expression))
}
}
}
override fun visitLabeledExpression(expression: KtLabeledExpression) {
mark(expression)
val baseExpression = expression.baseExpression
if (baseExpression != null) {
generateInstructions(baseExpression)
copyValue(baseExpression, expression)
}
val labelNameExpression = expression.getTargetLabel()
if (labelNameExpression != null) {
val deparenthesizedBaseExpression = KtPsiUtil.deparenthesize(expression)
if (deparenthesizedBaseExpression !is KtLambdaExpression &&
deparenthesizedBaseExpression !is KtLoopExpression &&
deparenthesizedBaseExpression !is KtNamedFunction
) {
trace.report(REDUNDANT_LABEL_WARNING.on(labelNameExpression))
}
}
}
override fun visitBinaryExpression(expression: KtBinaryExpression) {
val operationReference = expression.operationReference
val operationType = operationReference.getReferencedNameElementType()
val left = expression.left
val right = expression.right
if (operationType === ANDAND || operationType === OROR) {
generateBooleanOperation(expression)
} else if (operationType === EQ) {
visitAssignment(left, getDeferredValue(right), expression)
} else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall != null) {
val rhsValue = generateCall(resolvedCall).outputValue
val assignMethodName = OperatorConventions.getNameForOperationSymbol(expression.operationToken as KtToken)
if (resolvedCall.resultingDescriptor.name != assignMethodName) {
/* At this point assignment of the form a += b actually means a = a + b
* So we first generate call of "+" operation and then use its output pseudo-value
* as a right-hand side when generating assignment call
*/
visitAssignment(left, getValueAsFunction(rhsValue), expression)
}
} else {
generateBothArgumentsAndMark(expression)
}
} else if (operationType === ELVIS) {
generateInstructions(left)
mark(expression)
val afterElvis = builder.createUnboundLabel("after elvis operator")
builder.jumpOnTrue(afterElvis, expression, builder.getBoundValue(left))
generateInstructions(right)
builder.bindLabel(afterElvis)
mergeValues(listOfNotNull(left, right), expression)
if (right != null && languageVersionSettings.supportsFeature(LanguageFeature.ProhibitNonExhaustiveIfInRhsOfElvis)) {
right.recordUsedAsExpression(trace, true)
}
} else {
if (!generateCall(expression)) {
generateBothArgumentsAndMark(expression)
}
}
}
private fun generateBooleanOperation(expression: KtBinaryExpression) {
val operationType = expression.operationReference.getReferencedNameElementType()
val left = expression.left
val right = expression.right
val resultLabel = builder.createUnboundLabel("result of boolean operation")
generateInstructions(left)
if (operationType === ANDAND) {
builder.jumpOnFalse(resultLabel, expression, builder.getBoundValue(left))
} else {
builder.jumpOnTrue(resultLabel, expression, builder.getBoundValue(left))
}
generateInstructions(right)
builder.bindLabel(resultLabel)
val operation = if (operationType === ANDAND) ControlFlowBuilder.PredefinedOperation.AND else OR
builder.predefinedOperation(expression, operation, elementsToValues(listOfNotNull(left, right)))
}
private fun getValueAsFunction(value: PseudoValue?): () -> PseudoValue? = { value }
private fun getDeferredValue(expression: KtExpression?): () -> PseudoValue? = {
generateInstructions(expression)
getBoundOrUnreachableValue(expression)
}
private fun generateBothArgumentsAndMark(expression: KtBinaryExpression) {
val left = KtPsiUtil.deparenthesize(expression.left)
if (left != null) {
generateInstructions(left)
}
val right = expression.right
if (right != null) {
generateInstructions(right)
}
mark(expression)
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, left, right)
}
private fun visitAssignment(
lhs: KtExpression?,
rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression
) {
val left = KtPsiUtil.deparenthesize(lhs)
if (left == null) {
val arguments = rhsDeferredValue()?.let { listOf(it) } ?: emptyList()
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNSUPPORTED_ELEMENT)
return
}
if (left is KtArrayAccessExpression) {
generateArrayAssignment(left, rhsDeferredValue, parentExpression)
return
}
var receiverValues: Map = SmartFMap.emptyMap()
var accessTarget: AccessTarget = AccessTarget.BlackBox
if (left is KtSimpleNameExpression || left is KtQualifiedExpression) {
accessTarget = getResolvedCallAccessTarget(left.getQualifiedElementSelector())
if (accessTarget is AccessTarget.Call) {
receiverValues = getReceiverValues(accessTarget.resolvedCall)
}
} else if (left is KtProperty) {
accessTarget = getDeclarationAccessTarget(left)
}
if (accessTarget === AccessTarget.BlackBox && left !is KtProperty) {
generateInstructions(left)
createSyntheticValue(left, MagicKind.VALUE_CONSUMER, left)
}
val rightValue = rhsDeferredValue.invoke()
val rValue = rightValue ?: createSyntheticValue(parentExpression, MagicKind.UNRECOGNIZED_WRITE_RHS)
builder.write(parentExpression, left, rValue, accessTarget, receiverValues)
}
private fun generateArrayAssignment(
lhs: KtArrayAccessExpression,
rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression
) {
val setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, lhs)
if (setResolvedCall == null) {
generateArrayAccess(lhs, null)
val arguments = listOfNotNull(getBoundOrUnreachableValue(lhs), rhsDeferredValue.invoke())
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNRESOLVED_CALL)
return
}
// In case of simple ('=') array assignment mark instruction is not generated yet, so we put it before generating "set" call
if ((parentExpression as KtOperationExpression).operationReference.getReferencedNameElementType() === EQ) {
mark(lhs)
}
generateInstructions(lhs.arrayExpression)
val receiverValues = getReceiverValues(setResolvedCall)
val argumentValues = getArraySetterArguments(rhsDeferredValue, setResolvedCall)
builder.call(parentExpression, setResolvedCall, receiverValues, argumentValues)
}
/* We assume that assignment right-hand side corresponds to the last argument of the call
* So receiver instructions/pseudo-values are generated for all arguments except the last one which is replaced
* by pre-generated pseudo-value
* For example, assignment a[1, 2] += 3 means a.set(1, 2, a.get(1) + 3), so in order to generate "set" call
* we first generate instructions for 1 and 2 whereas 3 is replaced by pseudo-value corresponding to "a.get(1) + 3"
*/
private fun getArraySetterArguments(
rhsDeferredValue: () -> PseudoValue?,
setResolvedCall: ResolvedCall
): SmartFMap {
val valueArguments = setResolvedCall.resultingDescriptor.valueParameters.flatMapTo(
ArrayList()
) { descriptor -> setResolvedCall.valueArguments[descriptor]?.arguments ?: emptyList() }
val rhsArgument = valueArguments.lastOrNull()
var argumentValues = SmartFMap.emptyMap()
for (valueArgument in valueArguments) {
val argumentMapping = setResolvedCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: continue
val parameterDescriptor = argumentMapping.valueParameter
if (valueArgument !== rhsArgument) {
argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues)
} else {
val rhsValue = rhsDeferredValue.invoke()
if (rhsValue != null) {
argumentValues = argumentValues.plus(rhsValue, parameterDescriptor)
}
}
}
return argumentValues
}
private fun generateArrayAccess(arrayAccessExpression: KtArrayAccessExpression, resolvedCall: ResolvedCall<*>?) {
if (builder.getBoundValue(arrayAccessExpression) != null) return
mark(arrayAccessExpression)
if (!checkAndGenerateCall(resolvedCall)) {
generateArrayAccessWithoutCall(arrayAccessExpression)
}
}
private fun generateArrayAccessWithoutCall(arrayAccessExpression: KtArrayAccessExpression) {
createNonSyntheticValue(arrayAccessExpression, generateArrayAccessArguments(arrayAccessExpression), MagicKind.UNRESOLVED_CALL)
}
private fun generateArrayAccessArguments(arrayAccessExpression: KtArrayAccessExpression): List {
val inputExpressions = ArrayList()
val arrayExpression = arrayAccessExpression.arrayExpression
if (arrayExpression != null) {
inputExpressions.add(arrayExpression)
}
generateInstructions(arrayExpression)
for (index in arrayAccessExpression.indexExpressions) {
generateInstructions(index)
inputExpressions.add(index)
}
return inputExpressions
}
override fun visitUnaryExpression(expression: KtUnaryExpression) {
val operationSign = expression.operationReference
val operationType = operationSign.getReferencedNameElementType()
val baseExpression = expression.baseExpression ?: return
if (EXCLEXCL === operationType) {
generateInstructions(baseExpression)
builder.predefinedOperation(expression, NOT_NULL_ASSERTION, elementsToValues(listOf(baseExpression)))
return
}
val incrementOrDecrement = isIncrementOrDecrement(operationType)
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
val rhsValue: PseudoValue? = if (resolvedCall != null) {
generateCall(resolvedCall).outputValue
} else {
generateInstructions(baseExpression)
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression)
}
if (incrementOrDecrement && resolvedCall != null) {
visitAssignment(baseExpression, getValueAsFunction(rhsValue), expression)
if (expression is KtPostfixExpression) {
copyValue(baseExpression, expression)
}
}
}
private fun isIncrementOrDecrement(operationType: IElementType): Boolean =
operationType === PLUSPLUS || operationType === MINUSMINUS
override fun visitIfExpression(expression: KtIfExpression) {
mark(expression)
val branches = ArrayList(2)
val condition = expression.condition
generateInstructions(condition)
val elseLabel = builder.createUnboundLabel("else branch")
builder.jumpOnFalse(elseLabel, expression, builder.getBoundValue(condition))
val thenBranch = expression.then
if (thenBranch != null) {
branches.add(thenBranch)
generateInstructions(thenBranch)
} else {
builder.loadUnit(expression)
}
val resultLabel = builder.createUnboundLabel("'if' expression result")
builder.jump(resultLabel, expression)
builder.bindLabel(elseLabel)
val elseBranch = expression.`else`
if (elseBranch != null) {
branches.add(elseBranch)
generateInstructions(elseBranch)
} else {
builder.loadUnit(expression)
}
builder.bindLabel(resultLabel)
mergeValues(branches, expression)
}
private inner class FinallyBlockGenerator(private val finallyBlock: KtFinallySection?) {
private var startFinally: Label? = null
private var finishFinally: Label? = null
fun generate() {
val finalExpression = finallyBlock?.finalExpression ?: return
catchFinallyStack.push(CatchFinallyLabels(null, null, null))
startFinally?.let {
assert(finishFinally != null) { "startFinally label is set to $startFinally but finishFinally label is not set" }
builder.repeatPseudocode(it, finishFinally!!)
catchFinallyStack.pop()
return
}
builder.createUnboundLabel("start finally").let {
startFinally = it
builder.bindLabel(it)
}
generateInstructions(finalExpression)
builder.createUnboundLabel("finish finally").let {
finishFinally = it
builder.bindLabel(it)
}
catchFinallyStack.pop()
}
}
override fun visitTryExpression(expression: KtTryExpression) {
mark(expression)
val finallyBlock = expression.finallyBlock
val finallyBlockGenerator = FinallyBlockGenerator(finallyBlock)
val hasFinally = finallyBlock != null
if (hasFinally) {
builder.enterTryFinally(object : GenerationTrigger {
private var working = false
override fun generate() {
// This checks are needed for the case of having e.g. return inside finally: 'try {return} finally{return}'
if (working) return
working = true
finallyBlockGenerator.generate()
working = false
}
})
}
val onExceptionToFinallyBlock = generateTryAndCatches(expression)
if (hasFinally) {
assert(onExceptionToFinallyBlock != null) { "No finally label generated: " + expression.text }
builder.exitTryFinally()
val skipFinallyToErrorBlock = builder.createUnboundLabel("skipFinallyToErrorBlock")
builder.jump(skipFinallyToErrorBlock, expression)
builder.bindLabel(onExceptionToFinallyBlock!!)
finallyBlockGenerator.generate()
builder.jumpToError(expression)
builder.bindLabel(skipFinallyToErrorBlock)
finallyBlockGenerator.generate()
}
val branches = ArrayList()
branches.add(expression.tryBlock)
for (catchClause in expression.catchClauses) {
catchClause.catchBody?.let { branches.add(it) }
}
mergeValues(branches, expression)
}
// Returns label for 'finally' block
private fun generateTryAndCatches(expression: KtTryExpression): Label? {
val catchClauses = expression.catchClauses
val hasCatches = catchClauses.isNotEmpty()
var onException: Label? = null
if (hasCatches) {
onException = builder.createUnboundLabel("onException")
builder.nondeterministicJump(onException, expression, null)
}
var onExceptionToFinallyBlock: Label? = null
if (expression.finallyBlock != null) {
onExceptionToFinallyBlock = builder.createUnboundLabel("onExceptionToFinallyBlock")
builder.nondeterministicJump(onExceptionToFinallyBlock, expression, null)
}
val tryBlock = expression.tryBlock
catchFinallyStack.push(CatchFinallyLabels(onException, onExceptionToFinallyBlock, expression))
generateInstructions(tryBlock)
generateJumpsToCatchAndFinally()
catchFinallyStack.pop()
if (hasCatches && onException != null) {
val afterCatches = builder.createUnboundLabel("afterCatches")
builder.jump(afterCatches, expression)
builder.bindLabel(onException)
val catchLabels = LinkedList