1 /*
2 * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3 */
4
5 package kotlinx.atomicfu.transformer
6
7 import org.objectweb.asm.*
8 import org.objectweb.asm.Opcodes.*
9 import org.objectweb.asm.Type.*
10 import org.objectweb.asm.tree.*
11 import org.objectweb.asm.util.*
12
13 val AbstractInsnNode.line: Int?
14 get() {
15 var cur = this
16 while (true) {
17 if (cur is LineNumberNode) return cur.line
18 cur = cur.previous ?: return null
19 }
20 }
21
atIndexnull22 fun AbstractInsnNode.atIndex(insnList: InsnList?): String {
23 var cur = insnList?.first
24 var index = 1
25 while (cur != null && cur != this) {
26 if (!cur.isUseless()) index++
27 cur = cur.next
28 }
29 if (cur == null) return ""
30 return "inst #$index: "
31 }
32
33 val AbstractInsnNode.nextUseful: AbstractInsnNode?
34 get() {
35 var cur: AbstractInsnNode? = next
36 while (cur.isUseless()) cur = cur!!.next
37 return cur
38 }
39
40 val AbstractInsnNode?.thisOrPrevUseful: AbstractInsnNode?
41 get() {
42 var cur: AbstractInsnNode? = this
43 while (cur.isUseless()) cur = cur!!.previous
44 return cur
45 }
46
getInsnOrNullnull47 fun getInsnOrNull(from: AbstractInsnNode?, to: AbstractInsnNode?, predicate: (AbstractInsnNode) -> Boolean): AbstractInsnNode? {
48 var cur: AbstractInsnNode? = from?.next
49 while (cur != null && cur != to && !predicate(cur)) cur = cur.next
50 return cur
51 }
52
AbstractInsnNodenull53 private fun AbstractInsnNode?.isUseless() = this is LabelNode || this is LineNumberNode || this is FrameNode
54
55 fun InsnList.listUseful(limit: Int = Int.MAX_VALUE): List<AbstractInsnNode> {
56 val result = ArrayList<AbstractInsnNode>(limit)
57 var cur = first
58 while (cur != null && result.size < limit) {
59 if (!cur.isUseless()) result.add(cur)
60 cur = cur.next
61 }
62 return result
63 }
64
AbstractInsnNodenull65 fun AbstractInsnNode.isAload(index: Int) =
66 this is VarInsnNode && this.opcode == ALOAD && this.`var` == index
67
68 fun AbstractInsnNode.isGetField(owner: String) =
69 this is FieldInsnNode && this.opcode == GETFIELD && this.owner == owner
70
71 fun AbstractInsnNode.isGetStatic(owner: String) =
72 this is FieldInsnNode && this.opcode == GETSTATIC && this.owner == owner
73
74 fun AbstractInsnNode.isGetFieldOrGetStatic() =
75 this is FieldInsnNode && (this.opcode == GETFIELD || this.opcode == GETSTATIC)
76
77 fun AbstractInsnNode.isAreturn() =
78 this.opcode == ARETURN
79
80 fun AbstractInsnNode.isReturn() =
81 this.opcode == RETURN
82
83 fun AbstractInsnNode.isTypeReturn(type: Type) =
84 opcode == when (type) {
85 INT_TYPE -> IRETURN
86 LONG_TYPE -> LRETURN
87 BOOLEAN_TYPE -> IRETURN
88 else -> ARETURN
89 }
90
AbstractInsnNodenull91 fun AbstractInsnNode.isInvokeVirtual() =
92 this.opcode == INVOKEVIRTUAL
93
94 @Suppress("UNCHECKED_CAST")
95 fun MethodNode.localVar(v: Int, node: AbstractInsnNode): LocalVariableNode? =
96 (localVariables as List<LocalVariableNode>).firstOrNull { it.index == v && verifyLocalVarScopeStart(v, node, it.start)}
97
98 // checks that the store instruction is followed by the label equal to the local variable scope start from the local variables table
verifyLocalVarScopeStartnull99 private fun verifyLocalVarScopeStart(v: Int, node: AbstractInsnNode, scopeStart: LabelNode): Boolean {
100 var i = node.next
101 while (i != null) {
102 // check that no other variable is stored into the same slot v before finding the scope start label
103 if (i is VarInsnNode && i.`var` == v) return false
104 if (i is LabelNode && i === scopeStart) return true
105 i = i.next
106 }
107 return false
108 }
109
forVarLoadsnull110 inline fun forVarLoads(v: Int, start: LabelNode, end: LabelNode, block: (VarInsnNode) -> AbstractInsnNode?) {
111 var cur: AbstractInsnNode? = start
112 while (cur != null && cur !== end) {
113 if (cur is VarInsnNode && cur.opcode == ALOAD && cur.`var` == v) {
114 cur = block(cur)
115 } else
116 cur = cur.next
117 }
118 }
119
nextVarLoadnull120 fun nextVarLoad(v: Int, start: AbstractInsnNode): VarInsnNode {
121 var cur: AbstractInsnNode? = start
122 while (cur != null) {
123 when (cur.opcode) {
124 GOTO, TABLESWITCH, LOOKUPSWITCH, ATHROW, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL,
125 IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE,
126 IRETURN, FRETURN, ARETURN, RETURN, LRETURN, DRETURN -> {
127 abort("Unsupported branching/control while searching for load of spilled variable #$v", cur)
128 }
129 ALOAD -> {
130 if ((cur as VarInsnNode).`var` == v) return cur
131 }
132 }
133 cur = cur.next
134 }
135 abort("Flow control falls after the end of the method while searching for load of spilled variable #$v")
136 }
137
accessToInvokeOpcodenull138 fun accessToInvokeOpcode(access: Int) =
139 if (access and ACC_STATIC != 0) INVOKESTATIC else INVOKEVIRTUAL
140
141 fun AbstractInsnNode.toText(): String {
142 val printer = Textifier()
143 accept(TraceMethodVisitor(printer))
144 return (printer.getText()[0] as String).trim()
145 }
146
147 val String.ownerPackageName get() = substringBeforeLast('/')
148