[JVM] Create base FastAnalyzer class for all Fast...Analyzer
This commit is contained in:
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2023 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 org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||||
|
|
||||||
|
abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F: Frame<V>>(
|
||||||
|
protected val owner: String,
|
||||||
|
protected val method: MethodNode,
|
||||||
|
protected val interpreter: I,
|
||||||
|
) {
|
||||||
|
protected val nInsns = method.instructions.size()
|
||||||
|
protected val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
||||||
|
|
||||||
|
protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) {
|
||||||
|
when {
|
||||||
|
insnType == AbstractInsnNode.JUMP_INSN ->
|
||||||
|
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
||||||
|
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
||||||
|
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
||||||
|
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
||||||
|
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
||||||
|
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
||||||
|
visitOpInsn(insnNode, current, insn)
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun visitJumpInsnNode(insnNode: JumpInsnNode, current: F, insn: Int, insnOpcode: Int)
|
||||||
|
protected abstract fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: F)
|
||||||
|
protected abstract fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: F)
|
||||||
|
protected abstract fun visitOpInsn(insnNode: AbstractInsnNode, current: F, insn: Int)
|
||||||
|
|
||||||
|
protected fun checkAssertions() {
|
||||||
|
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
||||||
|
throw AssertionError("Subroutines are deprecated since Java 6")
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun AbstractInsnNode.indexOf() =
|
||||||
|
method.instructions.indexOf(this)
|
||||||
|
|
||||||
|
protected fun F.dump(): String {
|
||||||
|
return buildString {
|
||||||
|
append("{\n")
|
||||||
|
append(" locals: [\n")
|
||||||
|
for (i in 0 until method.maxLocals) {
|
||||||
|
append(" #$i: ${this@dump.getLocal(i)}\n")
|
||||||
|
}
|
||||||
|
append(" ]\n")
|
||||||
|
val stackSize = this@dump.stackSize
|
||||||
|
append(" stack: size=")
|
||||||
|
append(stackSize)
|
||||||
|
if (stackSize == 0) {
|
||||||
|
append(" []\n")
|
||||||
|
} else {
|
||||||
|
append(" [\n")
|
||||||
|
for (i in 0 until stackSize) {
|
||||||
|
append(" #$i: ${this@dump.getStack(i)}\n")
|
||||||
|
}
|
||||||
|
append(" ]\n")
|
||||||
|
}
|
||||||
|
append("}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-58
@@ -46,21 +46,16 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
|||||||
/**
|
/**
|
||||||
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
|
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
|
||||||
*/
|
*/
|
||||||
@Suppress("DuplicatedCode")
|
|
||||||
open class FastMethodAnalyzer<V : Value>
|
open class FastMethodAnalyzer<V : Value>
|
||||||
@JvmOverloads constructor(
|
@JvmOverloads constructor(
|
||||||
private val owner: String,
|
owner: String,
|
||||||
private val method: MethodNode,
|
method: MethodNode,
|
||||||
private val interpreter: Interpreter<V>,
|
interpreter: Interpreter<V>,
|
||||||
private val pruneExceptionEdges: Boolean = false
|
private val pruneExceptionEdges: Boolean = false
|
||||||
) {
|
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
|
||||||
private val nInsns = method.instructions.size()
|
|
||||||
|
|
||||||
private val isMergeNode = findMergeNodes(method)
|
private val isMergeNode = findMergeNodes(method)
|
||||||
|
|
||||||
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||||
|
|
||||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
|
||||||
private val queued = BooleanArray(nInsns)
|
private val queued = BooleanArray(nInsns)
|
||||||
private val queue = IntArray(nInsns)
|
private val queue = IntArray(nInsns)
|
||||||
private var top = 0
|
private var top = 0
|
||||||
@@ -102,18 +97,7 @@ open class FastMethodAnalyzer<V : Value>
|
|||||||
mergeControlFlowEdge(insn + 1, f, canReuse = true)
|
mergeControlFlowEdge(insn + 1, f, canReuse = true)
|
||||||
} else {
|
} else {
|
||||||
current.init(f).execute(insnNode, interpreter)
|
current.init(f).execute(insnNode, interpreter)
|
||||||
when {
|
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
|
||||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
|
||||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
|
||||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
|
||||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
|
||||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
|
||||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
|
||||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
|
||||||
visitOpInsn(current, insn)
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jump by an exception edge clears the stack, putting exception on top.
|
// Jump by an exception edge clears the stack, putting exception on top.
|
||||||
@@ -155,7 +139,7 @@ open class FastMethodAnalyzer<V : Value>
|
|||||||
return frames
|
return frames
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun initLocals(current: Frame<V>) {
|
private fun initLocals(current: Frame<V>) {
|
||||||
current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)))
|
current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)))
|
||||||
val args = Type.getArgumentTypes(method.desc)
|
val args = Type.getArgumentTypes(method.desc)
|
||||||
var local = 0
|
var local = 0
|
||||||
@@ -178,22 +162,14 @@ open class FastMethodAnalyzer<V : Value>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.indexOf() =
|
|
||||||
method.instructions.indexOf(this)
|
|
||||||
|
|
||||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||||
frames[insn.indexOf()]
|
frames[insn.indexOf()]
|
||||||
|
|
||||||
private fun checkAssertions() {
|
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
|
||||||
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
|
||||||
throw AssertionError("Subroutines are deprecated since Java 6")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visitOpInsn(current: Frame<V>, insn: Int) {
|
|
||||||
mergeControlFlowEdge(insn + 1, current)
|
mergeControlFlowEdge(insn + 1, current)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
||||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||||
// In most cases order of visiting switch labels should not matter
|
// In most cases order of visiting switch labels should not matter
|
||||||
// The only one is a tableswitch being added in the beginning of coroutine method, these switch' labels may lead
|
// The only one is a tableswitch being added in the beginning of coroutine method, these switch' labels may lead
|
||||||
@@ -205,14 +181,14 @@ open class FastMethodAnalyzer<V : Value>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
||||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||||
for (label in insnNode.labels) {
|
for (label in insnNode.labels) {
|
||||||
mergeControlFlowEdge(label.indexOf(), current)
|
mergeControlFlowEdge(label.indexOf(), current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||||
mergeControlFlowEdge(insnNode.label.indexOf(), current)
|
mergeControlFlowEdge(insnNode.label.indexOf(), current)
|
||||||
if (insnOpcode != Opcodes.GOTO) {
|
if (insnOpcode != Opcodes.GOTO) {
|
||||||
mergeControlFlowEdge(insn + 1, current)
|
mergeControlFlowEdge(insn + 1, current)
|
||||||
@@ -272,30 +248,6 @@ open class FastMethodAnalyzer<V : Value>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Frame<V>.dump(): String {
|
|
||||||
return buildString {
|
|
||||||
append("{\n")
|
|
||||||
append(" locals: [\n")
|
|
||||||
for (i in 0 until method.maxLocals) {
|
|
||||||
append(" #$i: ${this@dump.getLocal(i)}\n")
|
|
||||||
}
|
|
||||||
append(" ]\n")
|
|
||||||
val stackSize = this@dump.stackSize
|
|
||||||
append(" stack: size=")
|
|
||||||
append(stackSize)
|
|
||||||
if (stackSize == 0) {
|
|
||||||
append(" []\n")
|
|
||||||
} else {
|
|
||||||
append(" [\n")
|
|
||||||
for (i in 0 until stackSize) {
|
|
||||||
append(" #$i: ${this@dump.getStack(i)}\n")
|
|
||||||
}
|
|
||||||
append(" ]\n")
|
|
||||||
}
|
|
||||||
append("}\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun findMergeNodes(method: MethodNode): BooleanArray {
|
fun findMergeNodes(method: MethodNode): BooleanArray {
|
||||||
val isMergeNode = BooleanArray(method.instructions.size())
|
val isMergeNode = BooleanArray(method.instructions.size())
|
||||||
|
|||||||
+10
-32
@@ -34,6 +34,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization.fixStack
|
package org.jetbrains.kotlin.codegen.optimization.fixStack
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
@@ -45,17 +46,13 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
|||||||
/**
|
/**
|
||||||
* @see org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
* @see org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
|
||||||
*/
|
*/
|
||||||
@Suppress("DuplicatedCode")
|
|
||||||
internal open class FastStackAnalyzer<V : Value>(
|
internal open class FastStackAnalyzer<V : Value>(
|
||||||
private val owner: String,
|
owner: String,
|
||||||
val method: MethodNode,
|
method: MethodNode,
|
||||||
protected val interpreter: Interpreter<V>
|
interpreter: Interpreter<V>
|
||||||
) {
|
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
|
||||||
private val nInsns = method.instructions.size()
|
|
||||||
|
|
||||||
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||||
|
|
||||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
|
||||||
private val queued = BooleanArray(nInsns)
|
private val queued = BooleanArray(nInsns)
|
||||||
private val queue = IntArray(nInsns)
|
private val queue = IntArray(nInsns)
|
||||||
private var top = 0
|
private var top = 0
|
||||||
@@ -98,19 +95,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
|||||||
// Don't care about possibly incompatible return type
|
// Don't care about possibly incompatible return type
|
||||||
current.execute(insnNode, interpreter)
|
current.execute(insnNode, interpreter)
|
||||||
}
|
}
|
||||||
|
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
|
||||||
when {
|
|
||||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
|
||||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
|
||||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
|
||||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
|
||||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
|
||||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
|
||||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
|
||||||
visitOpInsn(insnNode, current, insn)
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers[insn]?.forEach { tcb ->
|
handlers[insn]?.forEach { tcb ->
|
||||||
@@ -135,17 +120,10 @@ internal open class FastStackAnalyzer<V : Value>(
|
|||||||
return frames
|
return frames
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AbstractInsnNode.indexOf() = method.instructions.indexOf(this)
|
|
||||||
|
|
||||||
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||||
frames[insn.indexOf()]
|
frames[insn.indexOf()]
|
||||||
|
|
||||||
private fun checkAssertions() {
|
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
|
||||||
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
|
||||||
throw AssertionError("Subroutines are deprecated since Java 6")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
|
|
||||||
processControlFlowEdge(current, insnNode, insn + 1)
|
processControlFlowEdge(current, insnNode, insn + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +131,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
|||||||
processControlFlowEdge(f, insnNode, insn + 1)
|
processControlFlowEdge(f, insnNode, insn + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
|
||||||
var jump = insnNode.dflt.indexOf()
|
var jump = insnNode.dflt.indexOf()
|
||||||
processControlFlowEdge(current, insnNode, jump)
|
processControlFlowEdge(current, insnNode, jump)
|
||||||
// In most cases order of visiting switch labels should not matter
|
// In most cases order of visiting switch labels should not matter
|
||||||
@@ -167,7 +145,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
|
||||||
var jump = insnNode.dflt.indexOf()
|
var jump = insnNode.dflt.indexOf()
|
||||||
processControlFlowEdge(current, insnNode, jump)
|
processControlFlowEdge(current, insnNode, jump)
|
||||||
for (label in insnNode.labels) {
|
for (label in insnNode.labels) {
|
||||||
@@ -176,7 +154,7 @@ internal open class FastStackAnalyzer<V : Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
|
||||||
if (insnOpcode != Opcodes.GOTO) {
|
if (insnOpcode != Opcodes.GOTO) {
|
||||||
processControlFlowEdge(current, insnNode, insn + 1)
|
processControlFlowEdge(current, insnNode, insn + 1)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -103,8 +103,6 @@ internal class FixStackAnalyzer(
|
|||||||
override fun newFrame(nLocals: Int, nStack: Int): Frame<FixStackValue> =
|
override fun newFrame(nLocals: Int, nStack: Int): Frame<FixStackValue> =
|
||||||
FixStackFrame(nLocals, nStack)
|
FixStackFrame(nLocals, nStack)
|
||||||
|
|
||||||
private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node)
|
|
||||||
|
|
||||||
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
|
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
|
||||||
val extraStack = Stack<FixStackValue>()
|
val extraStack = Stack<FixStackValue>()
|
||||||
|
|
||||||
@@ -207,7 +205,7 @@ internal class FixStackAnalyzer(
|
|||||||
private fun FixStackFrame.executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
|
private fun FixStackFrame.executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
|
||||||
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
|
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
|
||||||
val savedValues = spilledStacks.getOrElse(saveNode!!) {
|
val savedValues = spilledStacks.getOrElse(saveNode!!) {
|
||||||
throw AssertionError("${indexOf(insn)}: Restore stack is unavailable for ${indexOf(saveNode)}")
|
throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}")
|
||||||
}
|
}
|
||||||
pushAll(savedValues)
|
pushAll(savedValues)
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-46
@@ -34,28 +34,30 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization.temporaryVals
|
package org.jetbrains.kotlin.codegen.optimization.temporaryVals
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.inline.insnText
|
import org.jetbrains.kotlin.codegen.inline.insnText
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||||
|
|
||||||
interface StoreLoadValue
|
interface StoreLoadValue : Value
|
||||||
|
|
||||||
|
abstract class StoreLoadInterpreter<V : StoreLoadValue> : Interpreter<V>(API_VERSION) {
|
||||||
interface StoreLoadInterpreter<V : StoreLoadValue> {
|
abstract fun uninitialized(): V
|
||||||
fun uninitialized(): V
|
abstract fun valueParameter(type: Type): V
|
||||||
fun valueParameter(type: Type): V
|
abstract fun store(insn: VarInsnNode): V
|
||||||
fun store(insn: VarInsnNode): V
|
abstract fun load(insn: VarInsnNode, value: V)
|
||||||
fun load(insn: VarInsnNode, value: V)
|
abstract fun iinc(insn: IincInsnNode, value: V): V
|
||||||
fun iinc(insn: IincInsnNode, value: V): V
|
|
||||||
fun merge(a: V, b: V): V
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) {
|
class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) : Frame<V>(maxLocals, 0) {
|
||||||
private val locals = arrayOfNulls<StoreLoadValue>(maxLocals)
|
private val locals = arrayOfNulls<StoreLoadValue>(maxLocals)
|
||||||
|
|
||||||
operator fun get(index: Int): V =
|
operator fun get(index: Int): V =
|
||||||
@@ -101,19 +103,14 @@ class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DuplicatedCode")
|
|
||||||
class FastStoreLoadAnalyzer<V : StoreLoadValue>(
|
class FastStoreLoadAnalyzer<V : StoreLoadValue>(
|
||||||
private val owner: String,
|
owner: String,
|
||||||
private val method: MethodNode,
|
method: MethodNode,
|
||||||
private val interpreter: StoreLoadInterpreter<V>
|
interpreter: StoreLoadInterpreter<V>
|
||||||
) {
|
) : FastAnalyzer<V, StoreLoadInterpreter<V>, StoreLoadFrame<V>>(owner, method, interpreter) {
|
||||||
private val nInsns = method.instructions.size()
|
|
||||||
|
|
||||||
private val isMergeNode = BooleanArray(nInsns)
|
private val isMergeNode = BooleanArray(nInsns)
|
||||||
|
|
||||||
private val frames: Array<StoreLoadFrame<V>?> = arrayOfNulls(nInsns)
|
private val frames: Array<StoreLoadFrame<V>?> = arrayOfNulls(nInsns)
|
||||||
|
|
||||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
|
||||||
private val queued = BooleanArray(nInsns)
|
private val queued = BooleanArray(nInsns)
|
||||||
private val queue = IntArray(nInsns)
|
private val queue = IntArray(nInsns)
|
||||||
private var top = 0
|
private var top = 0
|
||||||
@@ -144,18 +141,7 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
|
|||||||
mergeControlFlowEdge(insn + 1, f)
|
mergeControlFlowEdge(insn + 1, f)
|
||||||
} else {
|
} else {
|
||||||
current.init(f).execute(insnNode, interpreter)
|
current.init(f).execute(insnNode, interpreter)
|
||||||
when {
|
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
|
||||||
insnType == AbstractInsnNode.JUMP_INSN ->
|
|
||||||
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
|
|
||||||
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
|
|
||||||
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
|
|
||||||
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
|
|
||||||
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
|
|
||||||
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
|
|
||||||
mergeControlFlowEdge(insn + 1, current)
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers[insn]?.forEach { tcb ->
|
handlers[insn]?.forEach { tcb ->
|
||||||
@@ -177,29 +163,25 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
|
|||||||
private fun newFrame(maxLocals: Int) =
|
private fun newFrame(maxLocals: Int) =
|
||||||
StoreLoadFrame<V>(maxLocals)
|
StoreLoadFrame<V>(maxLocals)
|
||||||
|
|
||||||
private fun AbstractInsnNode.indexOf() =
|
override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame<V>, insn: Int) {
|
||||||
method.instructions.indexOf(this)
|
mergeControlFlowEdge(insn + 1, current)
|
||||||
|
|
||||||
private fun checkAssertions() {
|
|
||||||
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
|
|
||||||
throw AssertionError("Subroutines are deprecated since Java 6")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame<V>) {
|
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame<V>) {
|
||||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||||
for (label in insnNode.labels) {
|
for (label in insnNode.labels) {
|
||||||
mergeControlFlowEdge(label.indexOf(), current)
|
mergeControlFlowEdge(label.indexOf(), current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame<V>) {
|
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame<V>) {
|
||||||
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
|
||||||
for (label in insnNode.labels) {
|
for (label in insnNode.labels) {
|
||||||
mergeControlFlowEdge(label.indexOf(), current)
|
mergeControlFlowEdge(label.indexOf(), current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame<V>, insn: Int, insnOpcode: Int) {
|
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame<V>, insn: Int, insnOpcode: Int) {
|
||||||
if (insnOpcode != Opcodes.GOTO) {
|
if (insnOpcode != Opcodes.GOTO) {
|
||||||
mergeControlFlowEdge(insn + 1, current)
|
mergeControlFlowEdge(insn + 1, current)
|
||||||
}
|
}
|
||||||
@@ -254,21 +236,25 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun initLocals(current: StoreLoadFrame<V>) {
|
private fun initLocals(current: StoreLoadFrame<V>) {
|
||||||
val args = Type.getArgumentTypes(method.desc)
|
val args = Type.getArgumentTypes(method.desc)
|
||||||
var local = 0
|
var local = 0
|
||||||
if ((method.access and Opcodes.ACC_STATIC) == 0) {
|
if ((method.access and Opcodes.ACC_STATIC) == 0) {
|
||||||
val ctype = Type.getObjectType(owner)
|
val ctype = Type.getObjectType(owner)
|
||||||
current[local++] = interpreter.valueParameter(ctype)
|
current.setLocal(local, interpreter.newValue(ctype))
|
||||||
|
local++
|
||||||
}
|
}
|
||||||
for (arg in args) {
|
for (arg in args) {
|
||||||
current[local++] = interpreter.valueParameter(arg)
|
current.setLocal(local, interpreter.newValue(arg))
|
||||||
|
local++
|
||||||
if (arg.size == 2) {
|
if (arg.size == 2) {
|
||||||
current[local++] = interpreter.uninitialized()
|
current.setLocal(local, interpreter.uninitialized())
|
||||||
|
local++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (local < method.maxLocals) {
|
while (local < method.maxLocals) {
|
||||||
current[local++] = interpreter.uninitialized()
|
current.setLocal(local, interpreter.uninitialized())
|
||||||
|
local++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-5
@@ -134,6 +134,9 @@ class TemporaryValsAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private sealed class StoredValue : StoreLoadValue {
|
private sealed class StoredValue : StoreLoadValue {
|
||||||
|
// `StoredValue` represent some abstract value that doesn't really have a definition of size.
|
||||||
|
// `getSize` can return either 1 or 2, so we use 1 here.
|
||||||
|
override fun getSize(): Int = 1
|
||||||
|
|
||||||
object Unknown : StoredValue()
|
object Unknown : StoredValue()
|
||||||
|
|
||||||
@@ -156,13 +159,13 @@ class TemporaryValsAnalyzer {
|
|||||||
|
|
||||||
private class StoreTrackingInterpreter(
|
private class StoreTrackingInterpreter(
|
||||||
private val storeInsnToStoreData: Map<AbstractInsnNode, StoreData>
|
private val storeInsnToStoreData: Map<AbstractInsnNode, StoreData>
|
||||||
) : StoreLoadInterpreter<StoredValue> {
|
) : StoreLoadInterpreter<StoredValue>() {
|
||||||
|
|
||||||
override fun uninitialized(): StoredValue =
|
override fun uninitialized(): StoredValue = StoredValue.Unknown
|
||||||
StoredValue.Unknown
|
|
||||||
|
|
||||||
override fun valueParameter(type: Type): StoredValue =
|
override fun newValue(type: Type?): StoredValue = StoredValue.Unknown
|
||||||
StoredValue.Unknown
|
|
||||||
|
override fun valueParameter(type: Type): StoredValue = StoredValue.Unknown
|
||||||
|
|
||||||
override fun store(insn: VarInsnNode): StoredValue {
|
override fun store(insn: VarInsnNode): StoredValue {
|
||||||
val temporaryValData = storeInsnToStoreData[insn]
|
val temporaryValData = storeInsnToStoreData[insn]
|
||||||
@@ -218,5 +221,38 @@ class TemporaryValsAnalyzer {
|
|||||||
is StoredValue.DirtyStore -> this.temporaryVals
|
is StoredValue.DirtyStore -> this.temporaryVals
|
||||||
else -> emptySet()
|
else -> emptySet()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun copyOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun newOperation(insn: AbstractInsnNode?): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun unaryOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun binaryOperation(insn: AbstractInsnNode?, value1: StoredValue?, value2: StoredValue?): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun ternaryOperation(
|
||||||
|
insn: AbstractInsnNode?,
|
||||||
|
value1: StoredValue?,
|
||||||
|
value2: StoredValue?,
|
||||||
|
value3: StoredValue?,
|
||||||
|
): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun naryOperation(insn: AbstractInsnNode?, values: MutableList<out StoredValue>?): StoredValue {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun returnOperation(insn: AbstractInsnNode?, value: StoredValue?, expected: StoredValue?) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user