blob: ba08fbc3e14aa79f62b4727595a58cb0ac578d96 [file] [log] [blame]
/*
* Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.atomicfu.transformer
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.*
import org.objectweb.asm.util.*
val AbstractInsnNode.line: Int?
get() {
var cur = this
while (true) {
if (cur is LineNumberNode) return cur.line
cur = cur.previous ?: return null
}
}
fun AbstractInsnNode.atIndex(insnList: InsnList?): String {
var cur = insnList?.first
var index = 1
while (cur != null && cur != this) {
if (!cur.isUseless()) index++
cur = cur.next
}
if (cur == null) return ""
return "inst #$index: "
}
val AbstractInsnNode.nextUseful: AbstractInsnNode?
get() {
var cur: AbstractInsnNode? = next
while (cur.isUseless()) cur = cur!!.next
return cur
}
val AbstractInsnNode?.thisOrPrevUseful: AbstractInsnNode?
get() {
var cur: AbstractInsnNode? = this
while (cur.isUseless()) cur = cur!!.previous
return cur
}
fun getInsnOrNull(from: AbstractInsnNode?, to: AbstractInsnNode?, predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
var cur: AbstractInsnNode? = from?.next
while (cur != null && cur != to && !predicate(cur)) cur = cur.next
return cur
}
private fun AbstractInsnNode?.isUseless() = this is LabelNode || this is LineNumberNode || this is FrameNode
fun InsnList.listUseful(limit: Int = Int.MAX_VALUE): List<AbstractInsnNode> {
val result = ArrayList<AbstractInsnNode>(limit)
var cur = first
while (cur != null && result.size < limit) {
if (!cur.isUseless()) result.add(cur)
cur = cur.next
}
return result
}
fun AbstractInsnNode.isAload(index: Int) =
this is VarInsnNode && this.opcode == ALOAD && this.`var` == index
fun AbstractInsnNode.isGetField(owner: String) =
this is FieldInsnNode && this.opcode == GETFIELD && this.owner == owner
fun AbstractInsnNode.isGetStatic(owner: String) =
this is FieldInsnNode && this.opcode == GETSTATIC && this.owner == owner
fun AbstractInsnNode.isAreturn() =
this.opcode == ARETURN
fun AbstractInsnNode.isReturn() =
this.opcode == RETURN
fun AbstractInsnNode.isInvokeVirtual() =
this.opcode == INVOKEVIRTUAL
@Suppress("UNCHECKED_CAST")
fun MethodNode.localVar(v: Int, node: AbstractInsnNode): LocalVariableNode? =
(localVariables as List<LocalVariableNode>).firstOrNull { it.index == v && verifyLocalVarScopeStart(v, node, it.start)}
// checks that the store instruction is followed by the label equal to the local variable scope start from the local variables table
private fun verifyLocalVarScopeStart(v: Int, node: AbstractInsnNode, scopeStart: LabelNode): Boolean {
var i = node.next
while (i != null) {
// check that no other variable is stored into the same slot v before finding the scope start label
if (i is VarInsnNode && i.`var` == v) return false
if (i is LabelNode && i === scopeStart) return true
i = i.next
}
return false
}
inline fun forVarLoads(v: Int, start: LabelNode, end: LabelNode, block: (VarInsnNode) -> AbstractInsnNode?) {
var cur: AbstractInsnNode? = start
while (cur != null && cur !== end) {
if (cur is VarInsnNode && cur.opcode == ALOAD && cur.`var` == v) {
cur = block(cur)
} else
cur = cur.next
}
}
fun nextVarLoad(v: Int, start: AbstractInsnNode): VarInsnNode {
var cur: AbstractInsnNode? = start
while (cur != null) {
when (cur.opcode) {
GOTO, TABLESWITCH, LOOKUPSWITCH, ATHROW, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL,
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE,
IRETURN, FRETURN, ARETURN, RETURN, LRETURN, DRETURN -> {
abort("Unsupported branching/control while searching for load of spilled variable #$v", cur)
}
ALOAD -> {
if ((cur as VarInsnNode).`var` == v) return cur
}
}
cur = cur.next
}
abort("Flow control falls after the end of the method while searching for load of spilled variable #$v")
}
fun accessToInvokeOpcode(access: Int) =
if (access and ACC_STATIC != 0) INVOKESTATIC else INVOKEVIRTUAL
fun AbstractInsnNode.toText(): String {
val printer = Textifier()
accept(TraceMethodVisitor(printer))
return (printer.getText()[0] as String).trim()
}
val String.ownerPackageName get() = substringBeforeLast('/')