• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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