All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.codegen.optimization.common.ControlFlowGraph.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.codegen.optimization.common

import gnu.trove.TIntHashSet
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*


class ControlFlowGraph private constructor(private val insns: InsnList) {
    private val successors: Array> = Array(insns.size()) { ArrayList(2) }
    private val predecessors: Array> = Array(insns.size()) { ArrayList(2) }

    fun getSuccessorsIndices(insn: AbstractInsnNode): List = getSuccessorsIndices(insns.indexOf(insn))
    fun getSuccessorsIndices(index: Int): List = successors[index]
    fun getPredecessorsIndices(insn: AbstractInsnNode): List = getPredecessorsIndices(insns.indexOf(insn))
    fun getPredecessorsIndices(index: Int): List = predecessors[index]

    private class Builder(
        private val method: MethodNode,
        private val followExceptions: Boolean
    ) {
        private val instructions = method.instructions
        private val nInsns = instructions.size()

        private val handlers: Array?> = arrayOfNulls(nInsns)

        private val queued = BooleanArray(nInsns)
        private val queue = IntArray(nInsns)
        private var top = 0

        private val predecessors = Array(nInsns) { TIntHashSet() }

        private val AbstractInsnNode.indexOf get() = instructions.indexOf(this)

        fun build(): ControlFlowGraph {
            val graph = ControlFlowGraph(method.instructions)
            if (nInsns == 0) return graph

            checkAssertions()
            computeExceptionHandlersForEachInsn()
            initControlFlowAnalysis()
            traverseCfg()

            for ((i, preds) in predecessors.withIndex()) {
                for (pred in preds.toArray()) {
                    graph.predecessors[i].add(pred)
                    graph.successors[pred].add(i)
                }
            }
            return graph
        }

        private fun traverseCfg() {
            while (top > 0) {
                val insn = queue[--top]
                val insnNode = method.instructions[insn]
                val insnOpcode = insnNode.opcode

                when (insnNode.type) {
                    AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME ->
                        visitOpInsn(insn)
                    AbstractInsnNode.JUMP_INSN ->
                        visitJumpInsnNode(insnNode as JumpInsnNode, insn, insnOpcode)
                    AbstractInsnNode.LOOKUPSWITCH_INSN ->
                        visitLookupSwitchInsnNode(insn, insnNode as LookupSwitchInsnNode)
                    AbstractInsnNode.TABLESWITCH_INSN ->
                        visitTableSwitchInsnNode(insn, insnNode as TableSwitchInsnNode)
                    else -> {
                        if (insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN)) {
                            visitOpInsn(insn)
                        }
                    }
                }


                handlers[insn]?.forEach { tcb ->
                    visitExceptionEdge(insn, tcb.handler.indexOf)
                }
            }
        }

        private fun checkAssertions() {
            if (instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
                throw AssertionError("Subroutines are deprecated since Java 6")
        }

        private fun visitOpInsn(insn: Int) {
            visitEdge(insn, insn + 1)
        }

        private fun visitTableSwitchInsnNode(insn: Int, insnNode: TableSwitchInsnNode) {
            var jump = insnNode.dflt.indexOf
            visitEdge(insn, jump)
            for (label in insnNode.labels) {
                jump = label.indexOf
                visitEdge(insn, jump)
            }
        }

        private fun visitLookupSwitchInsnNode(insn: Int, insnNode: LookupSwitchInsnNode) {
            var jump = insnNode.dflt.indexOf
            visitEdge(insn, jump)
            for (label in insnNode.labels) {
                jump = label.indexOf
                visitEdge(insn, jump)
            }
        }

        private fun visitJumpInsnNode(insnNode: JumpInsnNode, insn: Int, insnOpcode: Int) {
            if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
                visitEdge(insn, insn + 1)
            }
            val jump = insnNode.label.indexOf
            visitEdge(insn, jump)
        }

        private fun initControlFlowAnalysis() {
            queued[0] = true
            queue[top++] = 0
        }

        private fun computeExceptionHandlersForEachInsn() {
            for (tcb in method.tryCatchBlocks) {
                val begin = tcb.start.indexOf
                val end = tcb.end.indexOf
                for (j in begin until end) {
                    val insnHandlers = handlers[j]
                        ?: ArrayList().also { handlers[j] = it }
                    insnHandlers.add(tcb)
                }
            }
        }

        private fun visitExceptionEdge(from: Int, to: Int) {
            if (followExceptions) {
                predecessors[to].add(from)
            }
            enqueue(to)
        }

        private fun visitEdge(from: Int, to: Int) {
            predecessors[to].add(from)
            enqueue(to)
        }

        private fun enqueue(insn: Int) {
            if (!queued[insn]) {
                queued[insn] = true
                queue[top++] = insn
            }
        }
    }

    companion object {
        @JvmStatic
        fun build(node: MethodNode, followExceptions: Boolean = true): ControlFlowGraph {
            return Builder(node, followExceptions).build()
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy