Support finally blocks in non-local returns
This commit is contained in:
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
@@ -279,7 +280,7 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
val labels = sourceCompiler.getContextLabels()
|
||||
|
||||
val infos = MethodInliner.processReturns(adapter, ReturnLabelOwner { labels.contains(it) }, true, null)
|
||||
sourceCompiler.generateAndInsertFinallyBlocks(
|
||||
generateAndInsertFinallyBlocks(
|
||||
adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index
|
||||
)
|
||||
if (!sourceCompiler.isFinallyMarkerRequired()) {
|
||||
@@ -299,6 +300,71 @@ abstract class InlineCodegen<out T : BaseExpressionCodegen>(
|
||||
return result
|
||||
}
|
||||
|
||||
fun generateAndInsertFinallyBlocks(
|
||||
intoNode: MethodNode,
|
||||
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
||||
offsetForFinallyLocalVar: Int
|
||||
) {
|
||||
if (!sourceCompiler.hasFinallyBlocks()) return
|
||||
|
||||
val extensionPoints = HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>()
|
||||
for (insertPoint in insertPoints) {
|
||||
extensionPoints.put(insertPoint.beforeIns, insertPoint)
|
||||
}
|
||||
|
||||
val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)
|
||||
|
||||
var curFinallyDepth = 0
|
||||
var curInstr: AbstractInsnNode? = intoNode.instructions.first
|
||||
while (curInstr != null) {
|
||||
processor.processInstruction(curInstr, true)
|
||||
if (isFinallyStart(curInstr)) {
|
||||
//TODO depth index calc could be more precise
|
||||
curFinallyDepth = getConstant(curInstr.previous)
|
||||
}
|
||||
|
||||
val extension = extensionPoints[curInstr]
|
||||
if (extension != null) {
|
||||
val start = Label()
|
||||
|
||||
val finallyNode = createEmptyMethodNode()
|
||||
finallyNode.visitLabel(start)
|
||||
|
||||
val finallyCodegen =
|
||||
sourceCompiler.createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode, curFinallyDepth)
|
||||
|
||||
val frameMap = finallyCodegen.frameMap
|
||||
val mark = frameMap.mark()
|
||||
var marker = -1
|
||||
val intervals = processor.localVarsMetaInfo.currentIntervals
|
||||
for (interval in intervals) {
|
||||
marker = Math.max(interval.node.index + 1, marker)
|
||||
}
|
||||
while (frameMap.currentSize < Math.max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
|
||||
frameMap.enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
sourceCompiler.generateFinallyBlocksIfNeeded(finallyCodegen, extension.returnType, extension.finallyIntervalEnd.label)
|
||||
|
||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||
insertNodeBefore(finallyNode, intoNode, curInstr)
|
||||
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
|
||||
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
|
||||
|
||||
mark.dropTo()
|
||||
}
|
||||
|
||||
curInstr = curInstr.next
|
||||
}
|
||||
|
||||
processor.substituteTryBlockNodes(intoNode)
|
||||
|
||||
//processor.substituteLocalVarTable(intoNode);
|
||||
}
|
||||
|
||||
protected abstract fun generateAssertFieldIfNeeded(info: RootInliningContext)
|
||||
|
||||
private fun isInlinedToInlineFunInKotlinRuntime(): Boolean {
|
||||
|
||||
+22
-74
@@ -28,10 +28,7 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
interface SourceCompilerForInline {
|
||||
@@ -64,11 +61,14 @@ interface SourceCompilerForInline {
|
||||
asmMethod: Method
|
||||
): SMAPAndMethodNode
|
||||
|
||||
fun generateAndInsertFinallyBlocks(
|
||||
intoNode: MethodNode,
|
||||
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
||||
offsetForFinallyLocalVar: Int
|
||||
)
|
||||
fun hasFinallyBlocks(): Boolean
|
||||
|
||||
fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(
|
||||
finallyNode: MethodNode,
|
||||
curFinallyDepth: Int
|
||||
): BaseExpressionCodegen
|
||||
|
||||
fun generateFinallyBlocksIfNeeded(finallyCodegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label)
|
||||
|
||||
fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean
|
||||
|
||||
@@ -84,7 +84,8 @@ interface SourceCompilerForInline {
|
||||
}
|
||||
|
||||
|
||||
class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement) : SourceCompilerForInline {
|
||||
class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, override val callElement: KtElement) :
|
||||
SourceCompilerForInline {
|
||||
|
||||
override val state = codegen.state
|
||||
|
||||
@@ -314,74 +315,21 @@ class PsiSourceCompilerForInline(private val codegen: ExpressionCodegen, overrid
|
||||
return SMAPAndMethodNode(node, smap)
|
||||
}
|
||||
|
||||
override fun generateAndInsertFinallyBlocks(
|
||||
intoNode: MethodNode,
|
||||
insertPoints: List<MethodInliner.PointForExternalFinallyBlocks>,
|
||||
offsetForFinallyLocalVar: Int
|
||||
) {
|
||||
if (!codegen.hasFinallyBlocks()) return
|
||||
override fun hasFinallyBlocks() = codegen.hasFinallyBlocks()
|
||||
|
||||
val extensionPoints = HashMap<AbstractInsnNode, MethodInliner.PointForExternalFinallyBlocks>()
|
||||
for (insertPoint in insertPoints) {
|
||||
extensionPoints.put(insertPoint.beforeIns, insertPoint)
|
||||
}
|
||||
|
||||
val processor = DefaultProcessor(intoNode, offsetForFinallyLocalVar)
|
||||
|
||||
var curFinallyDepth = 0
|
||||
var curInstr: AbstractInsnNode? = intoNode.instructions.first
|
||||
while (curInstr != null) {
|
||||
processor.processInstruction(curInstr, true)
|
||||
if (isFinallyStart(curInstr)) {
|
||||
//TODO depth index calc could be more precise
|
||||
curFinallyDepth = getConstant(curInstr.previous)
|
||||
}
|
||||
|
||||
val extension = extensionPoints[curInstr]
|
||||
if (extension != null) {
|
||||
val start = Label()
|
||||
|
||||
val finallyNode = createEmptyMethodNode()
|
||||
finallyNode.visitLabel(start)
|
||||
|
||||
val finallyCodegen = ExpressionCodegen(
|
||||
finallyNode, codegen.frameMap, codegen.returnType,
|
||||
codegen.getContext(), codegen.state, codegen.parentCodegen
|
||||
)
|
||||
finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
|
||||
|
||||
val frameMap = finallyCodegen.frameMap
|
||||
val mark = frameMap.mark()
|
||||
var marker = -1
|
||||
val intervals = processor.localVarsMetaInfo.currentIntervals
|
||||
for (interval in intervals) {
|
||||
marker = Math.max(interval.node.index + 1, marker)
|
||||
}
|
||||
while (frameMap.currentSize < Math.max(processor.nextFreeLocalIndex, offsetForFinallyLocalVar + marker)) {
|
||||
frameMap.enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, null, extension.finallyIntervalEnd.label)
|
||||
|
||||
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
|
||||
insertNodeBefore(finallyNode, intoNode, curInstr)
|
||||
|
||||
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
|
||||
processor.tryBlocksMetaInfo.splitAndRemoveCurrentIntervals(splitBy, true)
|
||||
|
||||
//processor.getLocalVarsMetaInfo().splitAndRemoveIntervalsFromCurrents(splitBy);
|
||||
|
||||
mark.dropTo()
|
||||
}
|
||||
|
||||
curInstr = curInstr.next
|
||||
}
|
||||
|
||||
processor.substituteTryBlockNodes(intoNode)
|
||||
|
||||
//processor.substituteLocalVarTable(intoNode);
|
||||
override fun generateFinallyBlocksIfNeeded(finallyCodegen: BaseExpressionCodegen, returnType: Type, afterReturnLabel: Label) {
|
||||
require(finallyCodegen is ExpressionCodegen)
|
||||
finallyCodegen.generateFinallyBlocksIfNeeded(returnType, null, afterReturnLabel)
|
||||
}
|
||||
|
||||
override fun createCodegenForExternalFinallyBlockGenerationOnNonLocalReturn(finallyNode: MethodNode, curFinallyDepth: Int) =
|
||||
ExpressionCodegen(
|
||||
finallyNode, codegen.frameMap, codegen.returnType,
|
||||
codegen.getContext(), codegen.state, codegen.parentCodegen
|
||||
).also {
|
||||
it.addBlockStackElementsForNonLocalReturns(codegen.blockStackElements, curFinallyDepth)
|
||||
}
|
||||
|
||||
override fun isCallInsideSameModuleAsDeclared(functionDescriptor: FunctionDescriptor): Boolean {
|
||||
return JvmCodegenUtil.isCallInsideSameModuleAsDeclared(functionDescriptor, codegen.getContext(), codegen.state.outDirectory)
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ internal fun getReturnType(opcode: Int): Type {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun insertNodeBefore(from: MethodNode, to: MethodNode, beforeNode: AbstractInsnNode) {
|
||||
fun insertNodeBefore(from: MethodNode, to: MethodNode, beforeNode: AbstractInsnNode) {
|
||||
val iterator = from.instructions.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val next = iterator.next()
|
||||
@@ -291,7 +291,7 @@ internal fun insertNodeBefore(from: MethodNode, to: MethodNode, beforeNode: Abst
|
||||
}
|
||||
}
|
||||
|
||||
internal fun createEmptyMethodNode() = MethodNode(Opcodes.API_VERSION, 0, "fake", "()V", null, null)
|
||||
fun createEmptyMethodNode() = MethodNode(Opcodes.API_VERSION, 0, "fake", "()V", null, null)
|
||||
|
||||
internal fun createFakeContinuationMethodNodeForInline(): MethodNode {
|
||||
val methodNode = createEmptyMethodNode()
|
||||
@@ -347,16 +347,16 @@ internal fun buildClassReaderByInternalName(state: GenerationState, internalName
|
||||
return ClassReader(file.contentsToByteArray())
|
||||
}
|
||||
|
||||
internal fun generateFinallyMarker(v: InstructionAdapter, depth: Int, start: Boolean) {
|
||||
fun generateFinallyMarker(v: InstructionAdapter, depth: Int, start: Boolean) {
|
||||
v.iconst(depth)
|
||||
v.invokestatic(INLINE_MARKER_CLASS_NAME, if (start) INLINE_MARKER_FINALLY_START else INLINE_MARKER_FINALLY_END, "(I)V", false)
|
||||
}
|
||||
|
||||
internal fun isFinallyEnd(node: AbstractInsnNode) = isFinallyMarker(node, INLINE_MARKER_FINALLY_END)
|
||||
fun isFinallyEnd(node: AbstractInsnNode) = isFinallyMarker(node, INLINE_MARKER_FINALLY_END)
|
||||
|
||||
internal fun isFinallyStart(node: AbstractInsnNode) = isFinallyMarker(node, INLINE_MARKER_FINALLY_START)
|
||||
fun isFinallyStart(node: AbstractInsnNode) = isFinallyMarker(node, INLINE_MARKER_FINALLY_START)
|
||||
|
||||
internal fun isFinallyMarker(node: AbstractInsnNode?): Boolean = node != null && (isFinallyStart(node) || isFinallyEnd(node))
|
||||
fun isFinallyMarker(node: AbstractInsnNode?): Boolean = node != null && (isFinallyStart(node) || isFinallyEnd(node))
|
||||
|
||||
private fun isFinallyMarker(node: AbstractInsnNode, name: String): Boolean {
|
||||
if (node !is MethodInsnNode) return false
|
||||
@@ -365,7 +365,7 @@ private fun isFinallyMarker(node: AbstractInsnNode, name: String): Boolean {
|
||||
|
||||
internal fun isFinallyMarkerRequired(context: MethodContext) = context.isInlineMethodContext || context is InlineLambdaContext
|
||||
|
||||
internal fun getConstant(ins: AbstractInsnNode): Int {
|
||||
fun getConstant(ins: AbstractInsnNode): Int {
|
||||
val opcode = ins.opcode
|
||||
return when (opcode) {
|
||||
in Opcodes.ICONST_0..Opcodes.ICONST_5 -> opcode - Opcodes.ICONST_0
|
||||
|
||||
Reference in New Issue
Block a user