1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
/*
* 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.*
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type.*
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.isGetFieldOrGetStatic() =
this is FieldInsnNode && (this.opcode == GETFIELD || this.opcode == GETSTATIC)
fun AbstractInsnNode.isAreturn() =
this.opcode == ARETURN
fun AbstractInsnNode.isReturn() =
this.opcode == RETURN
fun AbstractInsnNode.isTypeReturn(type: Type) =
opcode == when (type) {
INT_TYPE -> IRETURN
LONG_TYPE -> LRETURN
BOOLEAN_TYPE -> IRETURN
else -> ARETURN
}
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('/')
|