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