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.Opcodes.*
8 import org.objectweb.asm.tree.*
9 import org.objectweb.asm.util.*
10
11 val AbstractInsnNode.line: Int?
12 get() {
13 var cur = this
14 while (true) {
15 if (cur is LineNumberNode) return cur.line
16 cur = cur.previous ?: return null
17 }
18 }
19
atIndexnull20 fun AbstractInsnNode.atIndex(insnList: InsnList?): String {
21 var cur = insnList?.first
22 var index = 1
23 while (cur != null && cur != this) {
24 if (!cur.isUseless()) index++
25 cur = cur.next
26 }
27 if (cur == null) return ""
28 return "inst #$index: "
29 }
30
31 val AbstractInsnNode.nextUseful: AbstractInsnNode?
32 get() {
33 var cur: AbstractInsnNode? = next
34 while (cur.isUseless()) cur = cur!!.next
35 return cur
36 }
37
38 val AbstractInsnNode?.thisOrPrevUseful: AbstractInsnNode?
39 get() {
40 var cur: AbstractInsnNode? = this
41 while (cur.isUseless()) cur = cur!!.previous
42 return cur
43 }
44
AbstractInsnNodenull45 private fun AbstractInsnNode?.isUseless() = this is LabelNode || this is LineNumberNode || this is FrameNode
46
47 fun InsnList.listUseful(limit: Int = Int.MAX_VALUE): List<AbstractInsnNode> {
48 val result = ArrayList<AbstractInsnNode>(limit)
49 var cur = first
50 while (cur != null && result.size < limit) {
51 if (!cur.isUseless()) result.add(cur)
52 cur = cur.next
53 }
54 return result
55 }
56
AbstractInsnNodenull57 fun AbstractInsnNode.isAload(index: Int) =
58 this is VarInsnNode && this.opcode == ALOAD && this.`var` == index
59
60 fun AbstractInsnNode.isGetField(owner: String) =
61 this is FieldInsnNode && this.opcode == GETFIELD && this.owner == owner
62
63 fun AbstractInsnNode.isGetStatic(owner: String) =
64 this is FieldInsnNode && this.opcode == GETSTATIC && this.owner == owner
65
66 fun AbstractInsnNode.isAreturn() =
67 this.opcode == ARETURN
68
69 fun AbstractInsnNode.isReturn() =
70 this.opcode == RETURN
71
72 @Suppress("UNCHECKED_CAST")
73 fun MethodNode.localVar(v: Int, node: AbstractInsnNode): LocalVariableNode? =
74 (localVariables as List<LocalVariableNode>).firstOrNull { it.index == v && verifyLocalVarScopeStart(v, node, it.start)}
75
76 // checks that the store instruction is followed by the label equal to the local variable scope start from the local variables table
verifyLocalVarScopeStartnull77 private fun verifyLocalVarScopeStart(v: Int, node: AbstractInsnNode, scopeStart: LabelNode): Boolean {
78 var i = node.next
79 while (i != null) {
80 // check that no other variable is stored into the same slot v before finding the scope start label
81 if (i is VarInsnNode && i.`var` == v) return false
82 if (i is LabelNode && i === scopeStart) return true
83 i = i.next
84 }
85 return false
86 }
87
forVarLoadsnull88 inline fun forVarLoads(v: Int, start: LabelNode, end: LabelNode, block: (VarInsnNode) -> AbstractInsnNode?) {
89 var cur: AbstractInsnNode? = start
90 while (cur != null && cur !== end) {
91 if (cur is VarInsnNode && cur.opcode == ALOAD && cur.`var` == v) {
92 cur = block(cur)
93 } else
94 cur = cur.next
95 }
96 }
97
nextVarLoadnull98 fun nextVarLoad(v: Int, start: AbstractInsnNode): VarInsnNode {
99 var cur: AbstractInsnNode? = start
100 while (cur != null) {
101 when (cur.opcode) {
102 GOTO, TABLESWITCH, LOOKUPSWITCH, ATHROW, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL,
103 IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE,
104 IRETURN, FRETURN, ARETURN, RETURN, LRETURN, DRETURN -> {
105 abort("Unsupported branching/control while searching for load of spilled variable #$v", cur)
106 }
107 ALOAD -> {
108 if ((cur as VarInsnNode).`var` == v) return cur
109 }
110 }
111 cur = cur.next
112 }
113 abort("Flow control falls after the end of the method while searching for load of spilled variable #$v")
114 }
115
accessToInvokeOpcodenull116 fun accessToInvokeOpcode(access: Int) =
117 if (access and ACC_STATIC != 0) INVOKESTATIC else INVOKEVIRTUAL
118
119 fun AbstractInsnNode.toText(): String {
120 val printer = Textifier()
121 accept(TraceMethodVisitor(printer))
122 return (printer.getText()[0] as String).trim()
123 }
124
125 val String.ownerPackageName get() = substringBeforeLast('/')
126