JVM_IR: support non-local break/continue in the inliner
Not in the frontend or psi2ir, though, so this not a complete
implementation of KT-1436, but rather a part of it that is currently
useful to make other code compile. In particular, lambdas passed to
array constructors and JVM-style `assert` are inlined as IR returnable
blocks, which are then converted into `do { ... } while (false)` loops,
so non-local returns from them become non-local `break`s.
This commit is contained in:
+1
-1
@@ -319,7 +319,7 @@ class AnonymousObjectTransformer(
|
||||
), null
|
||||
)
|
||||
|
||||
val result = inliner.doInline(deferringVisitor, LocalVarRemapper(parameters, 0), false, ReturnLabelOwner.NOT_APPLICABLE)
|
||||
val result = inliner.doInline(deferringVisitor, LocalVarRemapper(parameters, 0), false, mapOf())
|
||||
result.reifiedTypeParametersUsages.mergeAll(typeParametersToReify)
|
||||
deferringVisitor.visitMaxs(-1, -1)
|
||||
return result
|
||||
|
||||
@@ -243,12 +243,10 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
//hack to keep linenumber info, otherwise jdi will skip begin of linenumber chain
|
||||
adapter.visitInsn(Opcodes.NOP)
|
||||
|
||||
val result = inliner.doInline(adapter, remapper, true, ReturnLabelOwner.SKIP_ALL)
|
||||
val result = inliner.doInline(adapter, remapper, true, mapOf())
|
||||
result.reifiedTypeParametersUsages.mergeAll(reificationResult)
|
||||
|
||||
val labels = sourceCompiler.getContextLabels()
|
||||
|
||||
val infos = MethodInliner.processReturns(adapter, ReturnLabelOwner { labels.contains(it) }, true, null)
|
||||
val infos = MethodInliner.processReturns(adapter, sourceCompiler.getContextLabels(), null)
|
||||
generateAndInsertFinallyBlocks(
|
||||
adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index
|
||||
)
|
||||
@@ -319,7 +317,9 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
frameMap.enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
sourceCompiler.generateFinallyBlocksIfNeeded(finallyCodegen, extension.returnType, extension.finallyIntervalEnd.label)
|
||||
sourceCompiler.generateFinallyBlocksIfNeeded(
|
||||
finallyCodegen, extension.returnType, extension.finallyIntervalEnd.label, extension.jumpTarget
|
||||
)
|
||||
|
||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||
insertNodeBefore(finallyNode, intoNode, curInstr)
|
||||
|
||||
@@ -30,17 +30,14 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.*
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
interface FunctionalArgument
|
||||
|
||||
abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgument, ReturnLabelOwner {
|
||||
abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgument {
|
||||
|
||||
abstract val isBoundCallableReference: Boolean
|
||||
|
||||
@@ -54,6 +51,9 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu
|
||||
|
||||
abstract val capturedVars: List<CapturedParamDesc>
|
||||
|
||||
open val returnLabels: Map<String, Label?>
|
||||
get() = mapOf()
|
||||
|
||||
lateinit var node: SMAPAndMethodNode
|
||||
|
||||
abstract fun generateLambdaBody(sourceCompiler: SourceCompilerForInline, reifiedTypeInliner: ReifiedTypeInliner<*>)
|
||||
@@ -129,8 +129,6 @@ abstract class DefaultLambda(
|
||||
final override lateinit var capturedVars: List<CapturedParamDesc>
|
||||
private set
|
||||
|
||||
override fun isReturnFromMe(labelName: String): Boolean = false
|
||||
|
||||
var originalBoundReceiverType: Type? = null
|
||||
private set
|
||||
|
||||
@@ -247,7 +245,7 @@ class PsiExpressionLambda(
|
||||
|
||||
val functionWithBodyOrCallableReference: KtExpression = (expression as? KtLambdaExpression)?.functionLiteral ?: expression
|
||||
|
||||
private val labels: Set<String>
|
||||
override val returnLabels: Map<String, Label?>
|
||||
|
||||
override val isSuspend: Boolean
|
||||
|
||||
@@ -284,7 +282,7 @@ class PsiExpressionLambda(
|
||||
closure = it!!
|
||||
}
|
||||
|
||||
labels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor)
|
||||
returnLabels = InlineCodegen.getDeclarationLabels(expression, invokeMethodDescriptor).associateWith { null }
|
||||
invokeMethod = typeMapper.mapAsmMethod(invokeMethodDescriptor)
|
||||
isSuspend = invokeMethodDescriptor.isSuspend
|
||||
}
|
||||
@@ -324,10 +322,6 @@ class PsiExpressionLambda(
|
||||
}
|
||||
}
|
||||
|
||||
override fun isReturnFromMe(labelName: String): Boolean {
|
||||
return labels.contains(labelName)
|
||||
}
|
||||
|
||||
val isPropertyReference: Boolean
|
||||
get() = propertyReferenceInfo != null
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||
import org.jetbrains.kotlin.codegen.optimization.nullCheck.isCheckParameterIsNotNull
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
@@ -72,9 +73,9 @@ class MethodInliner(
|
||||
adapter: MethodVisitor,
|
||||
remapper: LocalVarRemapper,
|
||||
remapReturn: Boolean,
|
||||
returnLabelOwner: ReturnLabelOwner
|
||||
returnLabels: Map<String, Label?>
|
||||
): InlineResult {
|
||||
return doInline(adapter, remapper, remapReturn, returnLabelOwner, 0)
|
||||
return doInline(adapter, remapper, remapReturn, returnLabels, 0)
|
||||
}
|
||||
|
||||
private fun recordTransformation(info: TransformationInfo) {
|
||||
@@ -91,11 +92,11 @@ class MethodInliner(
|
||||
adapter: MethodVisitor,
|
||||
remapper: LocalVarRemapper,
|
||||
remapReturn: Boolean,
|
||||
returnLabelOwner: ReturnLabelOwner,
|
||||
returnLabels: Map<String, Label?>,
|
||||
finallyDeepShift: Int
|
||||
): InlineResult {
|
||||
//analyze body
|
||||
var transformedNode = markPlacesForInlineAndRemoveInlinable(node, returnLabelOwner, finallyDeepShift)
|
||||
var transformedNode = markPlacesForInlineAndRemoveInlinable(node, returnLabels, finallyDeepShift)
|
||||
if (inliningContext.isInliningLambda && isDefaultLambdaWithReification(inliningContext.lambdaInfo!!)) {
|
||||
//TODO maybe move reification in one place
|
||||
inliningContext.root.inlineMethodReifier.reifyInstructions(transformedNode)
|
||||
@@ -139,7 +140,9 @@ class MethodInliner(
|
||||
)
|
||||
}
|
||||
|
||||
processReturns(resultNode, returnLabelOwner, remapReturn, end)
|
||||
if (remapReturn) {
|
||||
processReturns(resultNode, returnLabels, end)
|
||||
}
|
||||
//flush transformed node to output
|
||||
resultNode.accept(SkipMaxAndEndVisitor(adapter))
|
||||
return result
|
||||
@@ -297,7 +300,7 @@ class MethodInliner(
|
||||
|
||||
val varRemapper = LocalVarRemapper(lambdaParameters, valueParamShift)
|
||||
//TODO add skipped this and receiver
|
||||
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info, invokeCall.finallyDepthShift)
|
||||
val lambdaResult = inliner.doInline(localVariablesSorter, varRemapper, true, info.returnLabels, invokeCall.finallyDepthShift)
|
||||
result.mergeWithNotChangeInfo(lambdaResult)
|
||||
result.reifiedTypeParametersUsages.mergeAll(lambdaResult.reifiedTypeParametersUsages)
|
||||
|
||||
@@ -486,11 +489,11 @@ class MethodInliner(
|
||||
}
|
||||
|
||||
private fun markPlacesForInlineAndRemoveInlinable(
|
||||
node: MethodNode, returnLabelOwner: ReturnLabelOwner, finallyDeepShift: Int
|
||||
node: MethodNode, returnLabels: Map<String, Label?>, finallyDeepShift: Int
|
||||
): MethodNode {
|
||||
val processingNode = prepareNode(node, finallyDeepShift)
|
||||
|
||||
preprocessNodeBeforeInline(processingNode, returnLabelOwner)
|
||||
preprocessNodeBeforeInline(processingNode, returnLabels)
|
||||
|
||||
replaceContinuationAccessesWithFakeContinuationsIfNeeded(processingNode)
|
||||
|
||||
@@ -765,7 +768,7 @@ class MethodInliner(
|
||||
}
|
||||
}
|
||||
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, returnLabelOwner: ReturnLabelOwner) {
|
||||
private fun preprocessNodeBeforeInline(node: MethodNode, returnLabels: Map<String, Label?>) {
|
||||
try {
|
||||
FixStackWithLabelNormalizationMethodTransformer().transform("fake", node)
|
||||
} catch (e: Throwable) {
|
||||
@@ -787,16 +790,13 @@ class MethodInliner(
|
||||
|
||||
if (!isReturnOpcode(insnNode.opcode)) continue
|
||||
|
||||
var insertBeforeInsn = insnNode
|
||||
|
||||
// TODO extract isLocalReturn / isNonLocalReturn, see processReturns
|
||||
val labelName = getMarkedReturnLabelOrNull(insnNode)
|
||||
if (labelName != null) {
|
||||
if (!returnLabelOwner.isReturnFromMe(labelName)) continue
|
||||
insertBeforeInsn = insnNode.previous
|
||||
if (labelName == null) {
|
||||
localReturnsNormalizer.addLocalReturnToTransform(insnNode, insnNode, frame)
|
||||
} else if (labelName in returnLabels) {
|
||||
localReturnsNormalizer.addLocalReturnToTransform(insnNode, insnNode.previous, frame)
|
||||
}
|
||||
|
||||
localReturnsNormalizer.addLocalReturnToTransform(insnNode, insertBeforeInsn, frame)
|
||||
}
|
||||
|
||||
localReturnsNormalizer.transform(node)
|
||||
@@ -1001,7 +1001,8 @@ class MethodInliner(
|
||||
class PointForExternalFinallyBlocks(
|
||||
@JvmField val beforeIns: AbstractInsnNode,
|
||||
@JvmField val returnType: Type,
|
||||
@JvmField val finallyIntervalEnd: LabelNode
|
||||
@JvmField val finallyIntervalEnd: LabelNode,
|
||||
@JvmField val jumpTarget: Label?
|
||||
)
|
||||
|
||||
companion object {
|
||||
@@ -1115,55 +1116,44 @@ class MethodInliner(
|
||||
//process local and global returns (local substituted with goto end-label global kept unchanged)
|
||||
@JvmStatic
|
||||
fun processReturns(
|
||||
node: MethodNode, returnLabelOwner: ReturnLabelOwner, remapReturn: Boolean, endLabel: Label?
|
||||
node: MethodNode, returnLabels: Map<String, Label?>, endLabel: Label?
|
||||
): List<PointForExternalFinallyBlocks> {
|
||||
if (!remapReturn) {
|
||||
return emptyList()
|
||||
}
|
||||
val result = ArrayList<PointForExternalFinallyBlocks>()
|
||||
val instructions = node.instructions
|
||||
var insnNode: AbstractInsnNode? = instructions.first
|
||||
while (insnNode != null) {
|
||||
if (isReturnOpcode(insnNode.opcode)) {
|
||||
var isLocalReturn = true
|
||||
val labelName = getMarkedReturnLabelOrNull(insnNode)
|
||||
val returnType = getReturnType(insnNode.opcode)
|
||||
|
||||
if (labelName != null) {
|
||||
isLocalReturn = returnLabelOwner.isReturnFromMe(labelName)
|
||||
//remove global return flag
|
||||
if (isLocalReturn) {
|
||||
instructions.remove(insnNode.previous)
|
||||
}
|
||||
val isLocalReturn = labelName == null || labelName in returnLabels
|
||||
val jumpTarget = returnLabels[labelName] ?: endLabel
|
||||
|
||||
if (isLocalReturn && labelName != null) {
|
||||
// remove non-local return flag
|
||||
instructions.remove(insnNode.previous)
|
||||
}
|
||||
|
||||
if (isLocalReturn && endLabel != null) {
|
||||
val nop = InsnNode(Opcodes.NOP)
|
||||
instructions.insert(insnNode, nop)
|
||||
|
||||
val labelNode = endLabel.info as LabelNode
|
||||
val jumpInsnNode = JumpInsnNode(Opcodes.GOTO, labelNode)
|
||||
instructions.insert(nop, jumpInsnNode)
|
||||
|
||||
if (isLocalReturn && jumpTarget != null) {
|
||||
val jumpInsnNode = JumpInsnNode(Opcodes.GOTO, jumpTarget.info as LabelNode)
|
||||
instructions.insertBefore(insnNode, InsnNode(Opcodes.NOP))
|
||||
if (jumpTarget != endLabel) {
|
||||
instructions.insertBefore(insnNode, PseudoInsn.FIX_STACK_BEFORE_JUMP.createInsnNode())
|
||||
}
|
||||
instructions.insertBefore(insnNode, jumpInsnNode)
|
||||
instructions.remove(insnNode)
|
||||
insnNode = jumpInsnNode
|
||||
}
|
||||
|
||||
//generate finally block before nonLocalReturn flag/return/goto
|
||||
val label = LabelNode()
|
||||
instructions.insert(insnNode, label)
|
||||
result.add(
|
||||
PointForExternalFinallyBlocks(
|
||||
getInstructionToInsertFinallyBefore(insnNode, isLocalReturn), getReturnType(insnNode.opcode), label
|
||||
)
|
||||
)
|
||||
// generate finally blocks before the non-local return flag or the stack fixup pseudo instruction
|
||||
val finallyInsertionPoint = if (isLocalReturn && jumpTarget == endLabel) insnNode else insnNode.previous
|
||||
result.add(PointForExternalFinallyBlocks(finallyInsertionPoint, returnType, label, jumpTarget))
|
||||
}
|
||||
insnNode = insnNode.next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getInstructionToInsertFinallyBefore(nonLocalReturnOrJump: AbstractInsnNode, isLocal: Boolean): AbstractInsnNode {
|
||||
return if (isLocal) nonLocalReturnOrJump else nonLocalReturnOrJump.previous
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.codegen.inline;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ReturnLabelOwner {
|
||||
boolean isReturnFromMe(@NotNull String labelName);
|
||||
|
||||
ReturnLabelOwner SKIP_ALL = name -> false;
|
||||
|
||||
ReturnLabelOwner NOT_APPLICABLE = name -> {
|
||||
throw new RuntimeException("This operation not applicable for current context");
|
||||
};
|
||||
}
|
||||
@@ -64,7 +64,7 @@ interface SourceCompilerForInline {
|
||||
curFinallyDepth: Int
|
||||
): BaseExpressionCodegen
|
||||
|
||||
fun generateFinallyBlocksIfNeeded(finallyCodegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label)
|
||||
fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?)
|
||||
|
||||
fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean
|
||||
|
||||
@@ -74,7 +74,7 @@ interface SourceCompilerForInline {
|
||||
|
||||
val compilationContextFunctionDescriptor: FunctionDescriptor
|
||||
|
||||
fun getContextLabels(): Set<String>
|
||||
fun getContextLabels(): Map<String, Label?>
|
||||
|
||||
fun reportSuspensionPointInsideMonitor(stackTraceElement: String)
|
||||
}
|
||||
@@ -311,9 +311,10 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
|
||||
override fun hasFinallyBlocks() = codegen.hasFinallyBlocks()
|
||||
|
||||
override fun generateFinallyBlocksIfNeeded(finallyCodegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label) {
|
||||
require(finallyCodegen is ExpressionCodegen)
|
||||
finallyCodegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel)
|
||||
override fun generateFinallyBlocksIfNeeded(codegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label, target: Label?) {
|
||||
// TODO use the target label for non-local break/continue
|
||||
require(codegen is ExpressionCodegen)
|
||||
codegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel)
|
||||
}
|
||||
|
||||
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
|
||||
@@ -337,14 +338,15 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
override val compilationContextFunctionDescriptor
|
||||
get() = codegen.getContext().functionDescriptor
|
||||
|
||||
override fun getContextLabels(): Set<String> {
|
||||
override fun getContextLabels(): Map<String, Label?> {
|
||||
val context = codegen.getContext()
|
||||
val parentContext = context.parentContext
|
||||
val descriptor = if (parentContext is ClosureContext && parentContext.originalSuspendLambdaDescriptor != null) {
|
||||
parentContext.originalSuspendLambdaDescriptor!!
|
||||
} else context.contextDescriptor
|
||||
|
||||
return InlineCodegen.getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
|
||||
val labels = InlineCodegen.getDeclarationLabels(DescriptorToSourceUtils.descriptorToDeclaration(descriptor), descriptor)
|
||||
return labels.associateWith { null } // TODO add break/continue labels
|
||||
}
|
||||
|
||||
fun initializeInlineFunctionContext(functionDescriptor: FunctionDescriptor) {
|
||||
|
||||
Reference in New Issue
Block a user