/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj package da import scala.xml.Node import scala.xml.NodeSeq import scala.xml.Text import scala.xml.Unparsed import org.opalj.control.repeat /** * @author Wael Alkhatib * @author Isbel Isbel * @author Noorulla Sharief * @author Tobias Becker */ case class Code(instructions: Array[Byte]) { assert(instructions.length > 0) import Code.id def toXHTML( methodIndex: Int, exceptionTable: ExceptionTable, lineNumberTable: Option[Seq[LineNumberTableEntry]] )( implicit cp: Constant_Pool ): Node = { val instructions = InstructionsToXHTML(methodIndex, this.instructions) val exceptions = ExceptionsToXHTMLTableElements(instructions, exceptionTable) { lineNumberTable.map(_ => ).getOrElse(NodeSeq.Empty) } { if (exceptionTable.nonEmpty) Seq( ) ++ exceptionTable.tail.map(_ => ) else scala.xml.NodeSeq.Empty } { // One instruction per row for { pc <- (0 until instructions.length) if instructions(pc) != null } yield { val exceptionInfo = exceptions.foldRight(Seq.empty[Node]) { (a, b) => Seq(a(pc)) ++ b } createTableRowForInstruction( methodIndex, instructions(pc), exceptionInfo, pc, lineNumberTable ) } }
PCLineInstructionExceptions
} private[this] def createTableRowForInstruction( methodIndex: Int, instruction: Node, exceptions: Seq[Node], pc: Int, lineNumberTable: Option[Seq[LineNumberTableEntry]] ): Node = { { pc } { lineNumberTable.map { lineNumberTable => val ln = lineNumberTable.find(e => e.start_pc == pc).map(_.line_number) { Text(ln.map(_.toString).getOrElse("|")) } }.getOrElse { scala.xml.NodeSeq.Empty } } { instruction } { exceptions } } private[this] def InstructionsToXHTML( methodIndex: Int, source: Array[Byte] )( implicit cp: Constant_Pool ): Array[Node] = { import java.io.DataInputStream import java.io.ByteArrayInputStream val bas = new ByteArrayInputStream(source) val in = new DataInputStream(bas) val codeLength = source.size val instructions = new Array[Node](codeLength) var wide: Boolean = false def lvIndex: Int = if (wide) { wide = false in.readUnsignedShort } else { in.readUnsignedByte } def ifToString(mnemonic: String, pc: Int): Node = { val targetPC = in.readShort + pc val targetID = "#"+id(methodIndex, targetPC) { mnemonic } { Text(targetPC.toString) } } while (in.available > 0) { val pc = codeLength - in.available instructions(pc) = (in.readUnsignedByte: @scala.annotation.switch) match { case 50 => aaload case 83 => aastore case 1 => aconst_null case 25 => aload { lvIndex } case 42 => aload_0 case 43 => aload_1 case 44 => aload_2 case 45 => aload_3 case 189 => anewarray { asJavaObjectType(in.readUnsignedShort()).asSpan("") } case 176 => areturn case 190 => arraylength case 58 => astore { lvIndex } case 75 => astore_0 case 76 => astore_1 case 77 => astore_2 case 78 => astore_3 case 191 => athrow case 51 => baload case 84 => bastore case 16 => bipush { in.readByte } case 52 => caload case 85 => castore case 192 => checkcast { asJavaReferenceType(in.readUnsignedShort()).asSpan("") } case 144 => d2f case 142 => d2i case 143 => d2l case 99 => dadd case 49 => daload case 82 => dastore case 152 => dcmpg case 151 => dcmpl case 14 => dconst_0 case 15 => dconst_1 case 111 => ddiv case 24 => dload { lvIndex } case 38 => dload_0 case 39 => dload_1 case 40 => dload_2 case 41 => dload_3 case 107 => dmul case 119 => dneg case 115 => drem case 175 => dreturn case 57 => dstore { lvIndex } case 71 => dstore_0 case 72 => dstore_1 case 73 => dstore_2 case 74 => dstore_3 case 103 => dsub case 89 => dup case 90 => dup_x1 case 91 => dup_x2 case 92 => dup2 case 93 => dup2_x1 case 94 => dup2_x2 case 141 => f2d case 139 => f2i case 140 => f2l case 98 => fadd case 48 => faload case 81 => fastore case 150 => fcmpg case 149 => fcmpl case 11 => fconst_0 case 12 => fconst_1 case 13 => fconst_2 case 110 => fdiv case 23 => fload { lvIndex } case 34 => fload_0 case 35 => fload_1 case 36 => fload_2 case 37 => fload_3 case 106 => fmul case 118 => fneg case 114 => frem case 174 => freturn case 56 => fstore { lvIndex } case 67 => fstore_0 case 68 => fstore_1 case 69 => fstore_2 case 70 => fstore_3 case 102 => fsub case 180 => getfield { val c = in.readUnsignedShort(); cp(c).asInstructionParameter } case 178 => getstatic { val c = in.readUnsignedShort(); cp(c).asInstructionParameter } case 167 => val targetPC = in.readShort + pc val targetID = "#"+id(methodIndex, targetPC) goto { targetPC } case 200 => val targetPC = in.readInt + pc val targetID = "#"+id(methodIndex, targetPC) goto_w { targetPC } case 145 => i2b case 146 => i2c case 135 => i2d case 134 => i2f case 133 => i2l case 147 => i2s case 96 => iadd case 46 => iaload case 126 => iand case 79 => iastore case 2 => iconst_m1 case 3 => iconst_0 case 4 => iconst_1 case 5 => iconst_2 case 6 => iconst_3 case 7 => iconst_4 case 8 => iconst_5 case 108 => idiv case 165 => ifToString("if_acmpeq", pc) case 166 => ifToString("if_acmpne", pc) case 159 => ifToString("if_icmpeq", pc) case 160 => ifToString("if_icmpne", pc) case 161 => ifToString("if_icmplt", pc) case 162 => ifToString("if_icmpge", pc) case 163 => ifToString("if_icmpgt", pc) case 164 => ifToString("if_icmple", pc) case 153 => ifToString("ifeq", pc) case 154 => ifToString("ifne", pc) case 155 => ifToString("iflt", pc) case 156 => ifToString("ifge", pc) case 157 => ifToString("ifgt", pc) case 158 => ifToString("ifle", pc) case 199 => ifToString("ifnonnull", pc) case 198 => ifToString("ifnull", pc) case 132 => val (lvIndex, increment) = if (wide) { wide = false val lvIndex = in.readUnsignedShort val constValue = in.readShort (lvIndex, constValue) } else { val lvIndex = in.readUnsignedByte val constValue = in.readByte (lvIndex, constValue) } iinc { lvIndex } { increment } case 21 => iload { lvIndex } case 26 => iload_0 case 27 => iload_1 case 28 => iload_2 case 29 => iload_3 case 104 => imul case 116 => ineg case 193 => instanceof { asJavaReferenceType(in.readUnsignedShort()).asSpan("") } case 186 => val c = in.readUnsignedShort() in.readByte // ignored; fixed value in.readByte // ignored; fixed value val signature = cp(c).asInstructionParameter invokedynamic { signature } case 185 => val c = in.readUnsignedShort() val count = in.readByte in.readByte // ignored; fixed value val signature = cp(c).asInstructionParameter invokeinterface (nargs={ count }) { signature } case 183 => val c = in.readUnsignedShort() val signature = cp(c).asInstructionParameter invokespecial { signature } case 184 => val c = in.readUnsignedShort() val signature = cp(c).asInstructionParameter invokestatic { signature } case 182 => val c = in.readUnsignedShort() val signature = cp(c).asInstructionParameter invokevirtual { signature } case 128 => ior case 112 => irem case 172 => ireturn case 120 => ishl case 122 => ishr case 54 => istore { lvIndex } case 59 => istore_0 case 60 => istore_1 case 61 => istore_2 case 62 => istore_3 case 100 => isub case 124 => iushr case 130 => ixor case 168 => val targetPC = in.readShort + pc val targetID = "#"+id(methodIndex, targetPC) jsr { targetPC } case 201 => val targetPC = in.readInt + pc val targetID = "#"+id(methodIndex, targetPC) jsr_w { targetPC } case 138 => l2d case 137 => l2f case 136 => l2i case 97 => ladd case 47 => laload case 127 => land case 80 => lastore case 148 => lcmp case 9 => lconst_0 case 10 => lconst_1 case 18 => val constantValue = cp(in.readUnsignedByte()) match { case ci: CONSTANT_Class_info => Seq(ci.asInstructionParameter, Text(".class")) case cv => Seq(cv.asInstructionParameter) } ldc { constantValue } case 19 => val constantValue = cp(in.readUnsignedShort()) match { case ci: CONSTANT_Class_info => Seq(ci.asInstructionParameter, Text(".class")) case cv => Seq(cv.asInstructionParameter) } ldc_w { constantValue } case 20 => val constantValue = cp(in.readUnsignedShort).asInstructionParameter ldc2_w { constantValue } case 109 => ldiv case 22 => lload { lvIndex } case 30 => lload_0 case 31 => lload_1 case 32 => lload_2 case 33 => lload_3 case 105 => lmul case 117 => lneg case 171 => // LOOKUPSWITCH in.skip((3 - (pc % 4)).toLong) // skip padding bytes val defaultTarget = in.readInt + pc val npairsCount = in.readInt val table = new StringBuilder(""); repeat(npairsCount) { table.append("(case:"+in.readInt+","+(in.readInt + pc)+") ") } lookupswitch default:{ defaultTarget } [{ table }] case 129 => lor case 113 => lrem case 173 => lreturn case 121 => lshl case 123 => lshr case 55 => lstore { lvIndex } case 63 => lstore_0 case 64 => lstore_1 case 65 => lstore_2 case 66 => lstore_3 case 101 => lsub case 125 => lushr case 131 => lxor case 194 => monitorenter case 195 => monitorexit case 197 => val referenceType = asJavaReferenceType(in.readUnsignedShort()) val dim = in.readUnsignedByte multianewarray { referenceType.asSpan("") }{ dim } case 187 => val objectType = asJavaObjectType(in.readUnsignedShort()) new { objectType.asSpan("") } case 188 => newarray { in.readByte match { case 4=> "T_BOOLEAN (4)" case 5=> "T_CHAR (5)" case 6=> "T_FLOAT (6)" case 7=> "T_DOUBLE (7)" case 8=> "T_BYTE (8)" case 9=> "T_SHORT (9)" case 10=> "T_INT (10)" case 11=> "T_LONG (11)" } } case 0 => nop case 87 => pop case 88 => pop2 case 181 => val c = in.readUnsignedShort() val signature = cp(c).asInstructionParameter putfield { signature } case 179 => val c = in.readUnsignedShort() val signature = cp(c).asInstructionParameter putstatic { signature } case 169 => ret { lvIndex } case 177 => return case 53 => saload case 86 => sastore case 17 => sipush { in.readShort /* value */ } case 95 => swap case 170 => in.skip((3 - (pc % 4)).toLong) // skip padding bytes val defaultTargetPC = in.readInt + pc val defaultTargetID = "#"+id(methodIndex, defaultTargetPC) val defaultTarget = s"$defaultTargetPC" val low = in.readInt val high = in.readInt var offsetcounter: Int = 0; val switchTargets = new StringBuilder(""); repeat(high - low + 1) { val targetPC = in.readInt + pc val targetID = "#"+id(methodIndex, targetPC) val target = s"$targetPC" switchTargets.append("(case "+(low + offsetcounter)+" → "+target+") ") offsetcounter += 1; } tableswitch default → { Unparsed(defaultTarget) }; { Unparsed(switchTargets.toString) } case 196 => wide = true wide case opcode => throw new UnknownError("unknown opcode: "+opcode) } } instructions } def ExceptionsToXHTMLTableElements( instructions: Array[Node], exceptionTable: ExceptionTable )( implicit cp: Constant_Pool ): Array[Array[Node]] = { val exceptions: Array[Array[Node]] = new Array(exceptionTable.size) for { (exceptionHandler, index) <- exceptionTable.iterator.zipWithIndex } { val exceptionName = (index + 1).toString+": "+( if (exceptionHandler.catch_type != 0) cp(exceptionHandler.catch_type).toString else "Any" ) var exceptionPCLength = 0 var exceptionPCStart = -1 exceptions(index) = new Array[Node](instructions.length) for { i <- exceptionHandler.start_pc until exceptionHandler.end_pc if instructions(i) ne null } { if (exceptionPCLength == 0) exceptionPCStart = i exceptionPCLength += 1 } for { i <- 0 until exceptionHandler.start_pc if i != exceptionHandler.handler_pc } { exceptions(index)(i) = } for { i <- exceptionHandler.end_pc until instructions.length if i != exceptionHandler.handler_pc } { exceptions(index)(i) = } var classes = "exception" if (exceptionPCStart == exceptionHandler.handler_pc) classes += " handler_overlap" else exceptions(index)(exceptionHandler.handler_pc) =
exceptions(index)(exceptionPCStart) = { exceptionName }
} exceptions } } object Code { def id(methodIndex: Int, pc: Int): String = s"m${methodIndex}_pc$pc" }